diff options
443 files changed, 11068 insertions, 9915 deletions
diff --git a/Android.bp b/Android.bp index 82b844b3d0a4..f3b2ebb4fc17 100644 --- a/Android.bp +++ b/Android.bp @@ -665,6 +665,7 @@ java_library { lint: { baseline_filename: "lint-baseline.xml", }, + apex_available: ["com.android.wifi"], } filegroup { diff --git a/WEAR_OWNERS b/WEAR_OWNERS index 4f3bc27c380f..4127f996da03 100644 --- a/WEAR_OWNERS +++ b/WEAR_OWNERS @@ -4,3 +4,9 @@ yeabkal@google.com adsule@google.com andriyn@google.com yfz@google.com +con@google.com +leetodd@google.com +sadrul@google.com +rwmyers@google.com +nalmalki@google.com +shijianli@google.com diff --git a/apct-tests/perftests/permission/Android.bp b/apct-tests/perftests/permission/Android.bp new file mode 100644 index 000000000000..b80a6af612ec --- /dev/null +++ b/apct-tests/perftests/permission/Android.bp @@ -0,0 +1,87 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES 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: "PermissionServicePerfTests", + + srcs: [ + "src/**/*.java", + "src/**/*.kt", + ], + + static_libs: [ + "platform-compat-test-rules", + "androidx.appcompat_appcompat", + "androidx.test.rules", + "androidx.test.ext.junit", + "androidx.annotation_annotation", + "apct-perftests-utils", + "androidx.benchmark_benchmark-common", + "androidx.benchmark_benchmark-junit4", + "collector-device-lib-platform", + "cts-install-lib-java", + ], + + libs: ["android.test.base"], + + platform_apis: true, + + test_suites: ["device-tests"], + + data: [ + ":UsePermissionApp0", + ":UsePermissionApp1", + ":UsePermissionApp2", + ":UsePermissionApp3", + ":UsePermissionApp4", + ":UsePermissionApp5", + ":UsePermissionApp6", + ":UsePermissionApp7", + ":UsePermissionApp8", + ":UsePermissionApp9", + ":UsePermissionApp10", + ":UsePermissionApp11", + ":UsePermissionApp12", + ":UsePermissionApp13", + ":UsePermissionApp14", + ":UsePermissionApp15", + ":UsePermissionApp16", + ":UsePermissionApp17", + ":UsePermissionApp18", + ":UsePermissionApp19", + ":UsePermissionApp20", + ":UsePermissionApp21", + ":UsePermissionApp22", + ":UsePermissionApp23", + ":UsePermissionApp24", + ":UsePermissionApp25", + ":UsePermissionApp26", + ":UsePermissionApp27", + ":UsePermissionApp28", + ":UsePermissionApp29", + ":perfetto_artifacts", + ], + + certificate: "platform", + +} diff --git a/apct-tests/perftests/permission/AndroidManifest.xml b/apct-tests/perftests/permission/AndroidManifest.xml new file mode 100644 index 000000000000..fa29ad0404da --- /dev/null +++ b/apct-tests/perftests/permission/AndroidManifest.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT 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="android.perftests.permission"> + + <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> + + <application> + <uses-library android:name="android.test.runner" /> + <activity android:name="android.perftests.utils.PerfTestActivity" + android:exported="true"> + <intent-filter> + <action android:name="android.perftests.permission.PERFTEST" /> + </intent-filter> + </activity> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.perftests.permission"/> + +</manifest> diff --git a/apct-tests/perftests/permission/AndroidTest.xml b/apct-tests/perftests/permission/AndroidTest.xml new file mode 100644 index 000000000000..07558deafb6c --- /dev/null +++ b/apct-tests/perftests/permission/AndroidTest.xml @@ -0,0 +1,180 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<configuration description="Runs PermissionServicePerfTests metric instrumentation."> + <option name="test-suite-tag" value="apct"/> + <option name="test-suite-tag" value="apct-metric-instrumentation"/> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true"/> + <option name="test-file-name" value="PermissionServicePerfTests.apk"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true"/> + <option name="force-queryable" value="false"/> + <option name="test-file-name" value="UsePermissionApp0.apk"/> + <option name="test-file-name" value="UsePermissionApp1.apk"/> + <option name="test-file-name" value="UsePermissionApp2.apk"/> + <option name="test-file-name" value="UsePermissionApp3.apk"/> + <option name="test-file-name" value="UsePermissionApp4.apk"/> + <option name="test-file-name" value="UsePermissionApp5.apk"/> + <option name="test-file-name" value="UsePermissionApp6.apk"/> + <option name="test-file-name" value="UsePermissionApp7.apk"/> + <option name="test-file-name" value="UsePermissionApp8.apk"/> + <option name="test-file-name" value="UsePermissionApp9.apk"/> + <option name="test-file-name" value="UsePermissionApp10.apk"/> + <option name="test-file-name" value="UsePermissionApp11.apk"/> + <option name="test-file-name" value="UsePermissionApp12.apk"/> + <option name="test-file-name" value="UsePermissionApp13.apk"/> + <option name="test-file-name" value="UsePermissionApp14.apk"/> + <option name="test-file-name" value="UsePermissionApp15.apk"/> + <option name="test-file-name" value="UsePermissionApp16.apk"/> + <option name="test-file-name" value="UsePermissionApp17.apk"/> + <option name="test-file-name" value="UsePermissionApp18.apk"/> + <option name="test-file-name" value="UsePermissionApp19.apk"/> + <option name="test-file-name" value="UsePermissionApp20.apk"/> + <option name="test-file-name" value="UsePermissionApp21.apk"/> + <option name="test-file-name" value="UsePermissionApp22.apk"/> + <option name="test-file-name" value="UsePermissionApp23.apk"/> + <option name="test-file-name" value="UsePermissionApp24.apk"/> + <option name="test-file-name" value="UsePermissionApp25.apk"/> + <option name="test-file-name" value="UsePermissionApp26.apk"/> + <option name="test-file-name" value="UsePermissionApp27.apk"/> + <option name="test-file-name" value="UsePermissionApp28.apk"/> + <option name="test-file-name" value="UsePermissionApp29.apk"/> + </target_preparer> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="android.perftests.permission"/> + <option name="hidden-api-checks" value="false"/> + </test> + + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="directory-keys" value="/data/local/PermissionServicePerfTests"/> + <option name="collect-on-run-ended-only" value="true"/> + </metrics_collector> + + <!-- Needed for pushing the trace config file --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="push-file" key="trace_config_detailed.textproto" + value="/data/misc/perfetto-traces/trace_config.textproto"/> + <!--Install the content provider automatically when we push some file in sdcard folder.--> + <!--Needed to avoid the installation during the test suite.--> + <option name="push-file" key="trace_config_detailed.textproto" + value="/sdcard/sample.textproto"/> + <option name="push-file" key="UsePermissionApp0.apk" + value="/data/local/tmp/perftests/UsePermissionApp0.apk" /> + <option name="push-file" key="UsePermissionApp1.apk" + value="/data/local/tmp/perftests/UsePermissionApp1.apk" /> + <option name="push-file" key="UsePermissionApp2.apk" + value="/data/local/tmp/perftests/UsePermissionApp2.apk" /> + <option name="push-file" key="UsePermissionApp3.apk" + value="/data/local/tmp/perftests/UsePermissionApp3.apk" /> + <option name="push-file" key="UsePermissionApp4.apk" + value="/data/local/tmp/perftests/UsePermissionApp4.apk" /> + <option name="push-file" key="UsePermissionApp5.apk" + value="/data/local/tmp/perftests/UsePermissionApp5.apk" /> + <option name="push-file" key="UsePermissionApp6.apk" + value="/data/local/tmp/perftests/UsePermissionApp6.apk" /> + <option name="push-file" key="UsePermissionApp7.apk" + value="/data/local/tmp/perftests/UsePermissionApp7.apk" /> + <option name="push-file" key="UsePermissionApp8.apk" + value="/data/local/tmp/perftests/UsePermissionApp8.apk" /> + <option name="push-file" key="UsePermissionApp9.apk" + value="/data/local/tmp/perftests/UsePermissionApp9.apk" /> + <option name="push-file" key="UsePermissionApp10.apk" + value="/data/local/tmp/perftests/UsePermissionApp10.apk" /> + <option name="push-file" key="UsePermissionApp11.apk" + value="/data/local/tmp/perftests/UsePermissionApp11.apk" /> + <option name="push-file" key="UsePermissionApp12.apk" + value="/data/local/tmp/perftests/UsePermissionApp12.apk" /> + <option name="push-file" key="UsePermissionApp13.apk" + value="/data/local/tmp/perftests/UsePermissionApp13.apk" /> + <option name="push-file" key="UsePermissionApp14.apk" + value="/data/local/tmp/perftests/UsePermissionApp14.apk" /> + <option name="push-file" key="UsePermissionApp15.apk" + value="/data/local/tmp/perftests/UsePermissionApp15.apk" /> + <option name="push-file" key="UsePermissionApp16.apk" + value="/data/local/tmp/perftests/UsePermissionApp16.apk" /> + <option name="push-file" key="UsePermissionApp17.apk" + value="/data/local/tmp/perftests/UsePermissionApp17.apk" /> + <option name="push-file" key="UsePermissionApp18.apk" + value="/data/local/tmp/perftests/UsePermissionApp18.apk" /> + <option name="push-file" key="UsePermissionApp19.apk" + value="/data/local/tmp/perftests/UsePermissionApp19.apk" /> + <option name="push-file" key="UsePermissionApp20.apk" + value="/data/local/tmp/perftests/UsePermissionApp20.apk" /> + <option name="push-file" key="UsePermissionApp21.apk" + value="/data/local/tmp/perftests/UsePermissionApp21.apk" /> + <option name="push-file" key="UsePermissionApp22.apk" + value="/data/local/tmp/perftests/UsePermissionApp22.apk" /> + <option name="push-file" key="UsePermissionApp23.apk" + value="/data/local/tmp/perftests/UsePermissionApp23.apk" /> + <option name="push-file" key="UsePermissionApp24.apk" + value="/data/local/tmp/perftests/UsePermissionApp24.apk" /> + <option name="push-file" key="UsePermissionApp25.apk" + value="/data/local/tmp/perftests/UsePermissionApp25.apk" /> + <option name="push-file" key="UsePermissionApp26.apk" + value="/data/local/tmp/perftests/UsePermissionApp26.apk" /> + <option name="push-file" key="UsePermissionApp27.apk" + value="/data/local/tmp/perftests/UsePermissionApp27.apk" /> + <option name="push-file" key="UsePermissionApp28.apk" + value="/data/local/tmp/perftests/UsePermissionApp28.apk" /> + <option name="push-file" key="UsePermissionApp29.apk" + value="/data/local/tmp/perftests/UsePermissionApp29.apk" /> + </target_preparer> + + <!-- Needed for pulling the collected trace config on to the host --> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="perfetto_file_path"/> + </metrics_collector> + + <!-- Needed for storing the perfetto trace files in the sdcard/test_results --> + <option name="isolated-storage" value="false"/> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="android.perftests.permission"/> + <option name="hidden-api-checks" value="false"/> + + <!-- Listener related args for collecting the traces and waiting for the device to + stabilize. --> + <option name="device-listeners" + value="android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener"/> + <!-- Guarantee that user defined RunListeners will be running before any of the default + listeners defined in this runner. --> + <option name="instrumentation-arg" key="newRunListenerMode" value="true"/> + + <!-- ProcLoadListener related arguments --> + <!-- Wait for device last minute threshold to reach 3 with 2 minute timeout before starting + the test run --> + <option name="instrumentation-arg" key="procload-collector:per_run" value="true"/> + <option name="instrumentation-arg" key="proc-loadavg-threshold" value="3"/> + <option name="instrumentation-arg" key="proc-loadavg-timeout" value="120000"/> + <option name="instrumentation-arg" key="proc-loadavg-interval" value="10000"/> + + <!-- PerfettoListener related arguments --> + <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/> + <option name="instrumentation-arg" key="perfetto_config_file" + value="trace_config.textproto"/> + + <!-- + PackageInstallerBenchmark will break for 5 minutes time out so it changes to 10 minutes + --> + <option name="test-timeout" value="600000" /> + </test> + + +</configuration> diff --git a/apct-tests/perftests/permission/apps/usepermissionapp/Android.bp b/apct-tests/perftests/permission/apps/usepermissionapp/Android.bp new file mode 100644 index 000000000000..1ad20b6fff6c --- /dev/null +++ b/apct-tests/perftests/permission/apps/usepermissionapp/Android.bp @@ -0,0 +1,232 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES 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_helper_app { + name: "UsePermissionApp0", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration0", + ], +} + +android_test_helper_app { + name: "UsePermissionApp1", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration1", + ], +} + +android_test_helper_app { + name: "UsePermissionApp2", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration2", + ], +} + +android_test_helper_app { + name: "UsePermissionApp3", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration3", + ], +} + +android_test_helper_app { + name: "UsePermissionApp4", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration4", + ], +} + +android_test_helper_app { + name: "UsePermissionApp5", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration5", + ], +} + +android_test_helper_app { + name: "UsePermissionApp6", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration6", + ], +} + +android_test_helper_app { + name: "UsePermissionApp7", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration7", + ], +} + +android_test_helper_app { + name: "UsePermissionApp8", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration8", + ], +} + +android_test_helper_app { + name: "UsePermissionApp9", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration9", + ], +} + +android_test_helper_app { + name: "UsePermissionApp10", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration10", + ], +} + +android_test_helper_app { + name: "UsePermissionApp11", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration11", + ], +} + +android_test_helper_app { + name: "UsePermissionApp12", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration12", + ], +} + +android_test_helper_app { + name: "UsePermissionApp13", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration13", + ], +} + +android_test_helper_app { + name: "UsePermissionApp14", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration14", + ], +} + +android_test_helper_app { + name: "UsePermissionApp15", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration15", + ], +} + +android_test_helper_app { + name: "UsePermissionApp16", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration16", + ], +} + +android_test_helper_app { + name: "UsePermissionApp17", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration17", + ], +} + +android_test_helper_app { + name: "UsePermissionApp18", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration18", + ], +} + +android_test_helper_app { + name: "UsePermissionApp19", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration19", + ], +} + +android_test_helper_app { + name: "UsePermissionApp20", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration20", + ], +} + +android_test_helper_app { + name: "UsePermissionApp21", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration21", + ], +} + +android_test_helper_app { + name: "UsePermissionApp22", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration22", + ], +} + +android_test_helper_app { + name: "UsePermissionApp23", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration23", + ], +} + +android_test_helper_app { + name: "UsePermissionApp24", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration24", + ], +} + +android_test_helper_app { + name: "UsePermissionApp25", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration25", + ], +} + +android_test_helper_app { + name: "UsePermissionApp26", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration26", + ], +} + +android_test_helper_app { + name: "UsePermissionApp27", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration27", + ], +} + +android_test_helper_app { + name: "UsePermissionApp28", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration28", + ], +} + +android_test_helper_app { + name: "UsePermissionApp29", + aaptflags: [ + "--rename-manifest-package android.perftests.appenumeration29", + ], +} diff --git a/apct-tests/perftests/permission/apps/usepermissionapp/AndroidManifest.xml b/apct-tests/perftests/permission/apps/usepermissionapp/AndroidManifest.xml new file mode 100644 index 000000000000..3bccefd24b49 --- /dev/null +++ b/apct-tests/perftests/permission/apps/usepermissionapp/AndroidManifest.xml @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT 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="android.perftests.appenumeration"> + + <uses-permission android:name="android.permission.RECORD_AUDIO"/> + <uses-permission android:name="android.permission.CAMERA"/> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" /> + <uses-permission android:name="android.permission.RECORD_BACKGROUND_AUDIO" /> + <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" /> + <uses-permission android:name="android.permission.BACKGROUND_CAMERA" /> + <uses-permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS" /> + <uses-permission android:name="android.permission.BODY_SENSORS" /> + <uses-permission android:name="android.permission.USE_BIOMETRIC" /> + <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> + <uses-permission android:name="android.permission.SET_WALLPAPER" /> + <uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" /> + + <queries> + <package android:name="android.perftests.appenumeration0" /> + <package android:name="android.perftests.appenumeration1" /> + <package android:name="android.perftests.appenumeration2" /> + <package android:name="android.perftests.appenumeration3" /> + <package android:name="android.perftests.appenumeration4" /> + <package android:name="android.perftests.appenumeration5" /> + <package android:name="android.perftests.appenumeration6" /> + <package android:name="android.perftests.appenumeration7" /> + <package android:name="android.perftests.appenumeration8" /> + <package android:name="android.perftests.appenumeration9" /> + <package android:name="android.perftests.appenumeration10" /> + <package android:name="android.perftests.appenumeration11" /> + <package android:name="android.perftests.appenumeration12" /> + <package android:name="android.perftests.appenumeration13" /> + <package android:name="android.perftests.appenumeration14" /> + <package android:name="android.perftests.appenumeration15" /> + <package android:name="android.perftests.appenumeration16" /> + <package android:name="android.perftests.appenumeration17" /> + <package android:name="android.perftests.appenumeration18" /> + <package android:name="android.perftests.appenumeration19" /> + <package android:name="android.perftests.appenumeration20" /> + <package android:name="android.perftests.appenumeration21" /> + <package android:name="android.perftests.appenumeration22" /> + <package android:name="android.perftests.appenumeration23" /> + <package android:name="android.perftests.appenumeration24" /> + <package android:name="android.perftests.appenumeration25" /> + <package android:name="android.perftests.appenumeration26" /> + <package android:name="android.perftests.appenumeration27" /> + <package android:name="android.perftests.appenumeration28" /> + <package android:name="android.perftests.appenumeration29" /> + </queries> + + <application android:hasCode="false" > + <activity android:name="android.perftests.utils.PerfTestActivity" + android:exported="true"> + <intent-filter> + <action android:name="android.perftests.permission.PERFTEST" /> + </intent-filter> + </activity> + </application> + +</manifest> diff --git a/apct-tests/perftests/permission/src/android/perftests/permission/PermissionServicePerfTest.kt b/apct-tests/perftests/permission/src/android/perftests/permission/PermissionServicePerfTest.kt new file mode 100644 index 000000000000..13e67e34a8d9 --- /dev/null +++ b/apct-tests/perftests/permission/src/android/perftests/permission/PermissionServicePerfTest.kt @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.perftests.permission + +import android.Manifest +import android.os.ParcelFileDescriptor +import android.os.Trace +import android.perftests.utils.PerfManualStatusReporter +import android.perftests.utils.TraceMarkParser +import android.util.Log +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import com.android.compatibility.common.util.AdoptShellPermissionsRule +import com.android.compatibility.common.util.SystemUtil.eventually +import com.android.compatibility.common.util.SystemUtil.runShellCommand +import com.google.common.truth.Truth.assertThat +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import java.io.BufferedReader +import java.io.IOException +import java.io.InputStreamReader +import java.util.concurrent.TimeUnit +import java.util.function.BiConsumer + +@RunWith(AndroidJUnit4::class) +class PermissionServicePerfTest { + @get:Rule val mPerfManualStatusReporter = PerfManualStatusReporter() + @get:Rule val mAdoptShellPermissionsRule = AdoptShellPermissionsRule( + InstrumentationRegistry.getInstrumentation().getUiAutomation(), + Manifest.permission.INSTALL_PACKAGES, + Manifest.permission.DELETE_PACKAGES + ) + val mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation() + + @Test + fun testInstallPackages() { + mUiAutomation.executeShellCommand(COMMAND_TRACE_START) + eventually { assertThat(Trace.isTagEnabled(TRACE_TAG)).isTrue() } + val benchmarkState = mPerfManualStatusReporter.benchmarkState + val durations = ArrayList<Long>() + + while (benchmarkState.keepRunning(durations)) { + uninstallAllTestApps() + installAllTestApps() + + val parser = TraceMarkParser { line -> line.name.contains(PKG_INSTALL_TRACE_PREFIX) } + dumpResult(parser) { _, slices -> + slices.forEachIndexed { _, slice -> + durations.add(TimeUnit.MICROSECONDS.toNanos(slice.durationInMicroseconds)) + } + } + } + + mUiAutomation.executeShellCommand(COMMAND_TRACE_END) + } + + private fun installAllTestApps() { + for (i in 0..29) { + installTestApp(i) + } + } + + private fun installTestApp(appId: Int) { + val apkPath = "$APK_DIR$APK_NAME$appId.apk" + runShellCommand("pm install -t $apkPath") + } + + private fun uninstallAllTestApps() { + for (i in 0..29) { + uninstallTestApp(i) + } + } + + private fun uninstallTestApp(appId: Int) { + val packageName = "$PKG_NAME$appId" + runShellCommand("pm uninstall $packageName") + } + + private fun dumpResult( + parser: TraceMarkParser, + handler: BiConsumer<String, List<TraceMarkParser.TraceMarkSlice>> + ) { + parser.reset() + try { + val inputStream = ParcelFileDescriptor.AutoCloseInputStream( + mUiAutomation.executeShellCommand(COMMAND_TRACE_DUMP) + ) + val reader = BufferedReader(InputStreamReader(inputStream)) + var line = reader.readLine() + while (line != null) { + parser.visit(line) + line = reader.readLine() + } + } catch (e: IOException) { + Log.e(LOG_TAG, "IO error while reading trace dump file.") + } + parser.forAllSlices(handler) + } + + companion object { + private val LOG_TAG = PermissionServicePerfTest::class.java.simpleName + private const val TRACE_TAG = Trace.TRACE_TAG_PACKAGE_MANAGER + private const val PKG_INSTALL_TRACE_PREFIX = + "TaggedTracingPermissionManagerServiceImpl#onPackageInstalled" + private const val COMMAND_TRACE_START = "atrace --async_start -b 16000 pm" + private const val COMMAND_TRACE_END = "atrace --async_stop" + private const val COMMAND_TRACE_DUMP = "atrace --async_dump" + private const val APK_DIR = "/data/local/tmp/perftests/" + private const val APK_NAME = "UsePermissionApp" + private const val PKG_NAME = "android.perftests.appenumeration" + } +} 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 d940e380fa7e..b0f378d1752d 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -159,6 +159,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Predicate; @@ -513,6 +514,10 @@ public class JobSchedulerService extends com.android.server.SystemService if (name == null) { continue; } + if (DEBUG) { + Slog.d(TAG, "DeviceConfig " + name + + " changed to " + properties.getString(name, null)); + } switch (name) { case Constants.KEY_ENABLE_API_QUOTAS: case Constants.KEY_ENABLE_EXECUTION_SAFEGUARDS_UDC: @@ -3507,7 +3512,10 @@ public class JobSchedulerService extends com.android.server.SystemService } final boolean shouldForceBatchJob; - if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob()) { + if (job.overrideState > JobStatus.OVERRIDE_NONE) { + // The job should run for some test. Don't force batch it. + shouldForceBatchJob = false; + } else if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob()) { // Never batch expedited or user-initiated jobs, even for RESTRICTED apps. shouldForceBatchJob = false; } else if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX) { @@ -4960,6 +4968,8 @@ public class JobSchedulerService extends com.android.server.SystemService Slog.d(TAG, "executeRunCommand(): " + pkgName + "/" + namespace + "/" + userId + " " + jobId + " s=" + satisfied + " f=" + force); + final CountDownLatch delayLatch = new CountDownLatch(1); + final JobStatus js; try { final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0, userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM); @@ -4968,7 +4978,7 @@ public class JobSchedulerService extends com.android.server.SystemService } synchronized (mLock) { - final JobStatus js = mJobs.getJobByUidAndJobId(uid, namespace, jobId); + js = mJobs.getJobByUidAndJobId(uid, namespace, jobId); if (js == null) { return JobSchedulerShellCommand.CMD_ERR_NO_JOB; } @@ -4979,23 +4989,71 @@ public class JobSchedulerService extends com.android.server.SystemService // Re-evaluate constraints after the override is set in case one of the overridden // constraints was preventing another constraint from thinking it needed to update. for (int c = mControllers.size() - 1; c >= 0; --c) { - mControllers.get(c).reevaluateStateLocked(uid); + mControllers.get(c).evaluateStateLocked(js); } if (!js.isConstraintsSatisfied()) { - js.overrideState = JobStatus.OVERRIDE_NONE; - return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS; + if (js.hasConnectivityConstraint() + && !js.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY) + && js.wouldBeReadyWithConstraint(JobStatus.CONSTRAINT_CONNECTIVITY)) { + // Because of how asynchronous the connectivity signals are, JobScheduler + // may not get the connectivity satisfaction signal immediately. In this + // case, wait a few seconds to see if it comes in before saying the + // connectivity constraint isn't satisfied. + mHandler.postDelayed( + checkConstraintRunnableForTesting( + mHandler, js, delayLatch, 5, 1000), + 1000); + } else { + // There's no asynchronous signal to wait for. We can immediately say the + // job's constraints aren't satisfied and return. + js.overrideState = JobStatus.OVERRIDE_NONE; + return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS; + } + } else { + delayLatch.countDown(); } - - queueReadyJobsForExecutionLocked(); - maybeRunPendingJobsLocked(); } } catch (RemoteException e) { // can't happen + return 0; + } + + // Choose to block the return until we're sure about the state of the connectivity job + // so that tests can expect a reliable state after calling the run command. + try { + delayLatch.await(7L, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Slog.e(TAG, "Couldn't wait for asynchronous constraint change", e); + } + + synchronized (mLock) { + if (!js.isConstraintsSatisfied()) { + js.overrideState = JobStatus.OVERRIDE_NONE; + return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS; + } + + queueReadyJobsForExecutionLocked(); + maybeRunPendingJobsLocked(); } return 0; } + private static Runnable checkConstraintRunnableForTesting(@NonNull final Handler handler, + @NonNull final JobStatus js, @NonNull final CountDownLatch latch, + final int remainingAttempts, final long delayMs) { + return () -> { + if (remainingAttempts <= 0 || js.isConstraintsSatisfied()) { + latch.countDown(); + return; + } + handler.postDelayed( + checkConstraintRunnableForTesting( + handler, js, latch, remainingAttempts - 1, delayMs), + delayMs); + }; + } + // Shell command infrastructure: immediately timeout currently executing jobs int executeStopCommand(PrintWriter pw, String pkgName, int userId, @Nullable String namespace, boolean hasJobId, int jobId, 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 bdc2246475d6..d39863c85f33 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 @@ -2192,7 +2192,7 @@ public final class JobStatus { * @return Whether or not this job would be ready to run if it had the specified constraint * granted, based on its requirements. */ - boolean wouldBeReadyWithConstraint(int constraint) { + public boolean wouldBeReadyWithConstraint(int constraint) { return readinessStatusWithConstraint(constraint, true); } diff --git a/api/Android.bp b/api/Android.bp index 363197a54fac..1686943d08ca 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -444,3 +444,12 @@ genrule { targets: ["droid"], }, } + +phony_rule { + name: "checkapi", + phony_deps: [ + "frameworks-base-api-current-compat", + "frameworks-base-api-system-current-compat", + "frameworks-base-api-module-lib-current-compat", + ], +} diff --git a/api/Android.mk b/api/Android.mk deleted file mode 100644 index ce5f995033c5..000000000000 --- a/api/Android.mk +++ /dev/null @@ -1,2 +0,0 @@ -.PHONY: checkapi -checkapi: frameworks-base-api-current-compat frameworks-base-api-system-current-compat frameworks-base-api-module-lib-current-compat diff --git a/api/ApiDocs.bp b/api/ApiDocs.bp index bcfb68ffd04d..6b8c02f901f1 100644 --- a/api/ApiDocs.bp +++ b/api/ApiDocs.bp @@ -183,6 +183,7 @@ doc_defaults { "-federationapi AndroidX $(location :current-androidx-api)", // doclava contains checks for a few issues that are have been migrated to metalava. // disable them in doclava, to avoid mistriggering or double triggering. + "-hide 101", // TODO: turn Lint 101 back into an error again "-hide 111", // HIDDEN_SUPERCLASS "-hide 113", // DEPRECATION_MISMATCH "-hide 125", // REQUIRES_PERMISSION diff --git a/api/api.go b/api/api.go index 2668999c572e..43713aad0e1e 100644 --- a/api/api.go +++ b/api/api.go @@ -64,6 +64,7 @@ type CombinedApisProperties struct { type CombinedApis struct { android.ModuleBase + android.DefaultableModuleBase properties CombinedApisProperties } @@ -74,6 +75,7 @@ func init() { func registerBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("combined_apis", combinedApisModuleFactory) + ctx.RegisterModuleType("combined_apis_defaults", CombinedApisModuleDefaultsFactory) } var PrepareForCombinedApisTest = android.FixtureRegisterWithContext(registerBuildComponents) @@ -409,6 +411,7 @@ func combinedApisModuleFactory() android.Module { module := &CombinedApis{} module.AddProperties(&module.properties) android.InitAndroidModule(module) + android.InitDefaultableModule(module) android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) }) return module } @@ -445,3 +448,16 @@ func remove(s []string, v string) []string { } return s2 } + +// Defaults +type CombinedApisModuleDefaults struct { + android.ModuleBase + android.DefaultsModuleBase +} + +func CombinedApisModuleDefaultsFactory() android.Module { + module := &CombinedApisModuleDefaults{} + module.AddProperties(&CombinedApisProperties{}) + android.InitDefaultsModule(module) + return module +} diff --git a/cmds/uinput/README.md b/cmds/uinput/README.md index e7361fe95e8e..b6e4e0de8cf7 100644 --- a/cmds/uinput/README.md +++ b/cmds/uinput/README.md @@ -7,11 +7,23 @@ There are two ways to use the `uinput` command: or app (such as the CTS tests via [`UinputDevice`][UinputDevice]). * `uinput <filename>` reads commands from a file instead of standard input. +There are also two supported input formats, described in the sections below. The tool will +automatically detect which format is being used. + [UinputDevice]: https://cs.android.com/android/platform/superproject/main/+/main:cts/libs/input/src/com/android/cts/input/UinputDevice.java -## Command format +## evemu recording format (recommended) + +`uinput` supports the evemu format, as used by the [FreeDesktop project's evemu suite][FreeDesktop]. +This is a simple text-based format compatible with recording and replay tools on other platforms. +However, it only supports playback of events from one device from a single recording. Recordings can +be made using the `evemu-record` command on Android or other Linux-based OSes. + +[FreeDesktop]: https://gitlab.freedesktop.org/libevdev/evemu + +## JSON-like format -Input commands should be in JSON format, though the parser is in [lenient mode] to allow comments, +The other supported format is JSON-based, though the parser is in [lenient mode] to allow comments, and integers can be specified in hexadecimal (e.g. `0xABCD`). The input file (or standard input) can contain multiple commands, which will be executed in sequence. Simply add multiple JSON objects to the file, one after the other without separators: @@ -34,9 +46,9 @@ Many examples of command files can be found [in the CTS tests][cts-example-jsons [lenient mode]: https://developer.android.com/reference/android/util/JsonReader#setLenient(boolean) [cts-example-jsons]: https://cs.android.com/android/platform/superproject/main/+/main:cts/tests/tests/hardware/res/raw/ -## Command reference +### Command reference -### `register` +#### `register` Register a new uinput device @@ -122,7 +134,7 @@ Example: [struct input_absinfo]: https://cs.android.com/android/platform/superproject/main/+/main:bionic/libc/kernel/uapi/linux/input.h?q=%22struct%20input_absinfo%22 -#### Waiting for registration +##### Waiting for registration After the command is sent, there will be a delay before the device is set up by the Android input stack, and `uinput` does not wait for that process to finish. Any commands sent to the device during @@ -135,12 +147,12 @@ finished processing. [onInputDeviceAdded]: https://developer.android.com/reference/android/hardware/input/InputManager.InputDeviceListener.html -#### Unregistering the device +##### Unregistering the device As soon as EOF is reached (either in interactive mode, or in file mode), the device that was created will be unregistered. There is no explicit command for unregistering a device. -### `delay` +#### `delay` Add a delay to command processing @@ -160,7 +172,7 @@ Example: } ``` -### `inject` +#### `inject` Send an array of uinput event packets to the uinput device @@ -190,7 +202,7 @@ keys would look like this: } ``` -### `sync` +#### `sync` A command used to get a response once the command is processed. When several `inject` and `delay` commands are used in a row, the `sync` command can be used to track the progress of the command diff --git a/core/api/current.txt b/core/api/current.txt index e0b224e92a04..7c34812464c0 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -49,6 +49,7 @@ package android { field public static final String BIND_SCREENING_SERVICE = "android.permission.BIND_SCREENING_SERVICE"; field public static final String BIND_TELECOM_CONNECTION_SERVICE = "android.permission.BIND_TELECOM_CONNECTION_SERVICE"; field public static final String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE"; + field @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public static final String BIND_TV_AD_SERVICE = "android.permission.BIND_TV_AD_SERVICE"; field public static final String BIND_TV_INPUT = "android.permission.BIND_TV_INPUT"; field public static final String BIND_TV_INTERACTIVE_APP = "android.permission.BIND_TV_INTERACTIVE_APP"; field public static final String BIND_VISUAL_VOICEMAIL_SERVICE = "android.permission.BIND_VISUAL_VOICEMAIL_SERVICE"; @@ -18617,6 +18618,7 @@ package android.hardware { } public final class SyncFence implements java.lang.AutoCloseable android.os.Parcelable { + ctor @FlaggedApi("com.android.window.flags.sdk_desired_present_time") public SyncFence(@NonNull android.hardware.SyncFence); method public boolean await(@NonNull java.time.Duration); method public boolean awaitForever(); method public void close(); @@ -19732,7 +19734,7 @@ package android.hardware.camera2.params { @FlaggedApi("com.android.internal.camera.flags.concert_mode") public final class LensIntrinsicsSample { ctor @FlaggedApi("com.android.internal.camera.flags.concert_mode") public LensIntrinsicsSample(long, @NonNull float[]); method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public float[] getLensIntrinsics(); - method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public long getTimestamp(); + method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public long getTimestampNanos(); } public final class LensShadingMap { @@ -28958,7 +28960,7 @@ package android.nfc.cardemulation { method public boolean supportsAidPrefixRegistration(); method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean unsetOffHostForService(@NonNull android.content.ComponentName); method public boolean unsetPreferredService(android.app.Activity); - field public static final String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT"; + field @Deprecated public static final String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT"; field public static final String CATEGORY_OTHER = "other"; field public static final String CATEGORY_PAYMENT = "payment"; field public static final String EXTRA_CATEGORY = "category"; @@ -33399,7 +33401,7 @@ package android.os { @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public final class PowerMonitorReadings { method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public long getConsumedEnergy(@NonNull android.os.PowerMonitor); - method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public long getTimestamp(@NonNull android.os.PowerMonitor); + method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public long getTimestampMillis(@NonNull android.os.PowerMonitor); field @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public static final int ENERGY_UNAVAILABLE = -1; // 0xffffffff } @@ -33409,6 +33411,7 @@ package android.os { method public static final long getElapsedCpuTime(); method public static final int[] getExclusiveCores(); method public static final int getGidForName(String); + method @FlaggedApi("com.android.sdksandbox.flags.sdk_sandbox_uid_to_app_uid_api") public static final int getSdkSandboxUidForAppUid(int); method public static long getStartElapsedRealtime(); method public static long getStartRequestedElapsedRealtime(); method public static long getStartRequestedUptimeMillis(); @@ -33993,8 +33996,8 @@ package android.os.health { } public class SystemHealthManager { - method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getPowerMonitorReadings(@NonNull java.util.List<android.os.PowerMonitor>, @Nullable android.os.Handler, @NonNull java.util.function.Consumer<android.os.PowerMonitorReadings>, @NonNull java.util.function.Consumer<java.lang.RuntimeException>); - method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getSupportedPowerMonitors(@Nullable android.os.Handler, @NonNull java.util.function.Consumer<java.util.List<android.os.PowerMonitor>>); + method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getPowerMonitorReadings(@NonNull java.util.List<android.os.PowerMonitor>, @Nullable java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.os.PowerMonitorReadings,java.lang.RuntimeException>); + method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getSupportedPowerMonitors(@Nullable java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.os.PowerMonitor>>); method public android.os.health.HealthStats takeMyUidSnapshot(); method public android.os.health.HealthStats takeUidSnapshot(int); method public android.os.health.HealthStats[] takeUidSnapshots(int[]); @@ -47292,6 +47295,7 @@ package android.text { method public int getLineForOffset(int); method public int getLineForVertical(int); method public float getLineLeft(int); + method @FlaggedApi("com.android.text.flags.inter_character_justification") @IntRange(from=0) public int getLineLetterSpacingUnitCount(@IntRange(from=0) int, boolean); method public float getLineMax(int); method public float getLineRight(int); method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public final float getLineSpacingAmount(); @@ -51798,6 +51802,7 @@ package android.view { public static class SurfaceControl.Transaction implements java.io.Closeable android.os.Parcelable { ctor public SurfaceControl.Transaction(); method @NonNull public android.view.SurfaceControl.Transaction addTransactionCommittedListener(@NonNull java.util.concurrent.Executor, @NonNull android.view.SurfaceControl.TransactionCommittedListener); + method @FlaggedApi("com.android.window.flags.sdk_desired_present_time") @NonNull public android.view.SurfaceControl.Transaction addTransactionCompletedListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.SurfaceControl.TransactionStats>); method public void apply(); method @NonNull public android.view.SurfaceControl.Transaction clearFrameRate(@NonNull android.view.SurfaceControl); method @NonNull public android.view.SurfaceControl.Transaction clearTrustedPresentationCallback(@NonNull android.view.SurfaceControl); @@ -51814,9 +51819,11 @@ package android.view { method @NonNull public android.view.SurfaceControl.Transaction setCrop(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect); method @NonNull public android.view.SurfaceControl.Transaction setDamageRegion(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Region); method @NonNull public android.view.SurfaceControl.Transaction setDataSpace(@NonNull android.view.SurfaceControl, int); + method @FlaggedApi("com.android.window.flags.sdk_desired_present_time") @NonNull public android.view.SurfaceControl.Transaction setDesiredPresentTime(long); method @NonNull public android.view.SurfaceControl.Transaction setExtendedRangeBrightness(@NonNull android.view.SurfaceControl, float, float); method @NonNull public android.view.SurfaceControl.Transaction setFrameRate(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0) float, int); method @NonNull public android.view.SurfaceControl.Transaction setFrameRate(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0) float, int, int); + method @FlaggedApi("com.android.window.flags.sdk_desired_present_time") @NonNull public android.view.SurfaceControl.Transaction setFrameTimeline(long); method @Deprecated @NonNull public android.view.SurfaceControl.Transaction setGeometry(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect, @Nullable android.graphics.Rect, int); method @NonNull public android.view.SurfaceControl.Transaction setLayer(@NonNull android.view.SurfaceControl, @IntRange(from=java.lang.Integer.MIN_VALUE, to=java.lang.Integer.MAX_VALUE) int); method @NonNull public android.view.SurfaceControl.Transaction setOpaque(@NonNull android.view.SurfaceControl, boolean); @@ -51832,6 +51839,11 @@ package android.view { method public void onTransactionCommitted(); } + @FlaggedApi("com.android.window.flags.sdk_desired_present_time") public static final class SurfaceControl.TransactionStats { + method @FlaggedApi("com.android.window.flags.sdk_desired_present_time") public long getLatchTime(); + method @FlaggedApi("com.android.window.flags.sdk_desired_present_time") @NonNull public android.hardware.SyncFence getPresentFence(); + } + public static final class SurfaceControl.TrustedPresentationThresholds { ctor public SurfaceControl.TrustedPresentationThresholds(@FloatRange(from=0.0f, fromInclusive=false, to=1.0f) float, @FloatRange(from=0.0f, fromInclusive=false, to=1.0f) float, @IntRange(from=1) int); } @@ -56489,6 +56501,7 @@ package android.view.textclassifier { field public static final String TYPE_EMAIL = "email"; field public static final String TYPE_FLIGHT_NUMBER = "flight"; field public static final String TYPE_OTHER = "other"; + field @FlaggedApi("android.service.notification.redact_sensitive_notifications_from_untrusted_listeners") public static final String TYPE_OTP_CODE = "otp_code"; field public static final String TYPE_PHONE = "phone"; field public static final String TYPE_UNKNOWN = ""; field public static final String TYPE_URL = "url"; diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 9077d02be8be..c9e12c0b5948 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -475,6 +475,7 @@ package android { field public static final int config_defaultNotes = 17039429; // 0x1040045 field @FlaggedApi("android.permission.flags.retail_demo_role_enabled") public static final int config_defaultRetailDemo; field public static final int config_defaultSms = 17039396; // 0x1040024 + field @FlaggedApi("android.permission.flags.wallet_role_enabled") public static final int config_defaultWallet; field public static final int config_devicePolicyManagement = 17039421; // 0x104003d field public static final int config_feedbackIntentExtraKey = 17039391; // 0x104001f field public static final int config_feedbackIntentNameKey = 17039392; // 0x1040020 @@ -861,6 +862,10 @@ package android.app { field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.PackageOps> CREATOR; } + @FlaggedApi("android.app.bic_client") public final class BackgroundInstallControlManager { + method @FlaggedApi("android.app.bic_client") @NonNull @RequiresPermission(android.Manifest.permission.QUERY_ALL_PACKAGES) public java.util.List<android.content.pm.PackageInfo> getBackgroundInstalledPackages(long); + } + public class BroadcastOptions { method public void clearRequireCompatChange(); method public int getPendingIntentBackgroundActivityStartMode(); @@ -1595,12 +1600,14 @@ package android.app.ambientcontext { method public int getDensityLevel(); method @NonNull public java.time.Instant getEndTime(); method public int getEventType(); + method @FlaggedApi("android.app.ambient_heart_rate") @IntRange(from=0xffffffff) public int getRatePerMinute(); method @NonNull public java.time.Instant getStartTime(); method @NonNull public android.os.PersistableBundle getVendorData(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.ambientcontext.AmbientContextEvent> CREATOR; field public static final int EVENT_BACK_DOUBLE_TAP = 3; // 0x3 field public static final int EVENT_COUGH = 1; // 0x1 + field @FlaggedApi("android.app.ambient_heart_rate") public static final int EVENT_HEART_RATE = 4; // 0x4 field public static final int EVENT_SNORE = 2; // 0x2 field public static final int EVENT_UNKNOWN = 0; // 0x0 field public static final int EVENT_VENDOR_WEARABLE_START = 100000; // 0x186a0 @@ -1620,6 +1627,7 @@ package android.app.ambientcontext { method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setDensityLevel(int); method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setEndTime(@NonNull java.time.Instant); method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setEventType(int); + method @FlaggedApi("android.app.ambient_heart_rate") @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setRatePerMinute(@IntRange(from=0xffffffff) int); method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setStartTime(@NonNull java.time.Instant); method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setVendorData(@NonNull android.os.PersistableBundle); } @@ -5747,7 +5755,7 @@ package android.hardware.radio { method public void addOnCompleteListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.radio.ProgramList.OnCompleteListener); method public void addOnCompleteListener(@NonNull android.hardware.radio.ProgramList.OnCompleteListener); method public void close(); - method @Deprecated @Nullable public android.hardware.radio.RadioManager.ProgramInfo get(@NonNull android.hardware.radio.ProgramSelector.Identifier); + method @Nullable public android.hardware.radio.RadioManager.ProgramInfo get(@NonNull android.hardware.radio.ProgramSelector.Identifier); method @FlaggedApi("android.hardware.radio.hd_radio_improved") @NonNull public java.util.List<android.hardware.radio.RadioManager.ProgramInfo> getProgramInfos(@NonNull android.hardware.radio.ProgramSelector.Identifier); method public void registerListCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.radio.ProgramList.ListCallback); method public void registerListCallback(@NonNull android.hardware.radio.ProgramList.ListCallback); @@ -5799,7 +5807,7 @@ package android.hardware.radio { field @Deprecated public static final int IDENTIFIER_TYPE_DAB_SIDECC = 5; // 0x5 field @Deprecated public static final int IDENTIFIER_TYPE_DAB_SID_EXT = 5; // 0x5 field public static final int IDENTIFIER_TYPE_DRMO_FREQUENCY = 10; // 0xa - field @Deprecated public static final int IDENTIFIER_TYPE_DRMO_MODULATION = 11; // 0xb + field public static final int IDENTIFIER_TYPE_DRMO_MODULATION = 11; // 0xb field public static final int IDENTIFIER_TYPE_DRMO_SERVICE_ID = 9; // 0x9 field public static final int IDENTIFIER_TYPE_HD_STATION_ID_EXT = 3; // 0x3 field @FlaggedApi("android.hardware.radio.hd_radio_improved") public static final int IDENTIFIER_TYPE_HD_STATION_LOCATION = 15; // 0xf @@ -5807,8 +5815,8 @@ package android.hardware.radio { field @Deprecated public static final int IDENTIFIER_TYPE_HD_SUBCHANNEL = 4; // 0x4 field public static final int IDENTIFIER_TYPE_INVALID = 0; // 0x0 field public static final int IDENTIFIER_TYPE_RDS_PI = 2; // 0x2 - field @Deprecated public static final int IDENTIFIER_TYPE_SXM_CHANNEL = 13; // 0xd - field @Deprecated public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 12; // 0xc + field public static final int IDENTIFIER_TYPE_SXM_CHANNEL = 13; // 0xd + field public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 12; // 0xc field public static final int IDENTIFIER_TYPE_VENDOR_END = 1999; // 0x7cf field @Deprecated public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = 1999; // 0x7cf field @Deprecated public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = 1000; // 0x3e8 @@ -5861,7 +5869,7 @@ package android.hardware.radio { field public static final int CONFIG_DAB_DAB_SOFT_LINKING = 8; // 0x8 field public static final int CONFIG_DAB_FM_LINKING = 7; // 0x7 field public static final int CONFIG_DAB_FM_SOFT_LINKING = 9; // 0x9 - field @Deprecated public static final int CONFIG_FORCE_ANALOG = 2; // 0x2 + field public static final int CONFIG_FORCE_ANALOG = 2; // 0x2 field @FlaggedApi("android.hardware.radio.hd_radio_improved") public static final int CONFIG_FORCE_ANALOG_AM = 11; // 0xb field @FlaggedApi("android.hardware.radio.hd_radio_improved") public static final int CONFIG_FORCE_ANALOG_FM = 10; // 0xa field public static final int CONFIG_FORCE_DIGITAL = 3; // 0x3 @@ -9967,6 +9975,7 @@ package android.nfc.cardemulation { } public final class CardEmulation { + method @FlaggedApi("android.permission.flags.wallet_role_enabled") @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public android.nfc.cardemulation.ApduServiceInfo getPreferredPaymentService(); method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<android.nfc.cardemulation.ApduServiceInfo> getServices(@NonNull String, int); } @@ -14679,6 +14688,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMobileDataPolicyEnabled(int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isNrDualConnectivityEnabled(); + method @FlaggedApi("com.android.internal.telephony.flags.enable_modem_cipher_transparency") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isNullCipherNotificationsEnabled(); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled(); method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String); @@ -14718,6 +14728,7 @@ package android.telephony { method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean); method @FlaggedApi("com.android.internal.telephony.flags.enable_identifier_disclosure_transparency") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setEnableCellularIdentifierDisclosureNotifications(boolean); + method @FlaggedApi("com.android.internal.telephony.flags.enable_modem_cipher_transparency") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setEnableNullCipherNotifications(boolean); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.PinResult setIccLockEnabled(boolean, @NonNull String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabled(int, boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean); @@ -15125,7 +15136,7 @@ package android.telephony.data { method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>); method public final void notifyDataProfileUnthrottled(@NonNull android.telephony.data.DataProfile); method public void requestDataCallList(@NonNull android.telephony.data.DataServiceCallback); - method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public void requestValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public void requestNetworkValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); 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); @@ -16332,7 +16343,7 @@ package android.telephony.ims { public interface RegistrationManager { field public static final int SUGGESTED_ACTION_NONE = 0; // 0x0 - field @FlaggedApi("com.android.internal.telephony.flags.add_rat_related_suggested_action_to_ims_registration") public static final int SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCK = 4; // 0x4 + field @FlaggedApi("com.android.internal.telephony.flags.add_rat_related_suggested_action_to_ims_registration") public static final int SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCKS = 4; // 0x4 field public static final int SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK = 1; // 0x1 field public static final int SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT = 2; // 0x2 field @FlaggedApi("com.android.internal.telephony.flags.add_rat_related_suggested_action_to_ims_registration") public static final int SUGGESTED_ACTION_TRIGGER_RAT_BLOCK = 3; // 0x3 @@ -17030,8 +17041,8 @@ package android.telephony.satellite { @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public final class PointingInfo implements android.os.Parcelable { method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int describeContents(); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public float getSatelliteAzimuthDegrees(); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public float getSatelliteElevationDegrees(); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @FloatRange(from=0xffffff4c, to=180) public float getSatelliteAzimuthDegrees(); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @FloatRange(from=0xffffffa6, to=90) public float getSatelliteElevationDegrees(); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void writeToParcel(@NonNull android.os.Parcel, int); field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.PointingInfo> CREATOR; } @@ -17070,7 +17081,7 @@ package android.telephony.satellite { method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void registerForNtnSignalStrengthChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.NtnSignalStrengthCallback) throws android.telephony.satellite.SatelliteManager.SatelliteException; method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteCapabilitiesChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteCapabilitiesCallback); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteDatagram(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteDatagramCallback); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteStateCallback); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteModemStateCallback); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteProvisionStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteProvisionStateCallback); method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void removeSatelliteAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsDemoModeEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>); @@ -17091,7 +17102,7 @@ package android.telephony.satellite { method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForNtnSignalStrengthChanged(@NonNull android.telephony.satellite.NtnSignalStrengthCallback); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteCapabilitiesChanged(@NonNull android.telephony.satellite.SatelliteCapabilitiesCallback); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteDatagram(@NonNull android.telephony.satellite.SatelliteDatagramCallback); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteModemStateChanged(@NonNull android.telephony.satellite.SatelliteStateCallback); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteModemStateChanged(@NonNull android.telephony.satellite.SatelliteModemStateCallback); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteProvisionStateChanged(@NonNull android.telephony.satellite.SatelliteProvisionStateCallback); field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DATAGRAM_TYPE_LOCATION_SHARING = 2; // 0x2 field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DATAGRAM_TYPE_SOS_MESSAGE = 1; // 0x1 @@ -17161,12 +17172,12 @@ package android.telephony.satellite { method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int getErrorCode(); } - @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteProvisionStateCallback { - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteProvisionStateChanged(boolean); + @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteModemStateCallback { + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteModemStateChanged(int); } - @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteStateCallback { - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteModemStateChanged(int); + @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteProvisionStateCallback { + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteProvisionStateChanged(boolean); } @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteTransmissionUpdateCallback { diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt index b2a28b2127bc..0505af4488ff 100644 --- a/core/api/system-lint-baseline.txt +++ b/core/api/system-lint-baseline.txt @@ -2359,8 +2359,8 @@ UnflaggedApi: android.telephony.satellite.SatelliteManager#provisionSatelliteSer New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.provisionSatelliteService(String,byte[],android.os.CancellationSignal,java.util.concurrent.Executor,java.util.function.Consumer<java.lang.Integer>) UnflaggedApi: android.telephony.satellite.SatelliteManager#registerForSatelliteDatagram(java.util.concurrent.Executor, android.telephony.satellite.SatelliteDatagramCallback): New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.registerForSatelliteDatagram(java.util.concurrent.Executor,android.telephony.satellite.SatelliteDatagramCallback) -UnflaggedApi: android.telephony.satellite.SatelliteManager#registerForSatelliteModemStateChanged(java.util.concurrent.Executor, android.telephony.satellite.SatelliteStateCallback): - New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.registerForSatelliteModemStateChanged(java.util.concurrent.Executor,android.telephony.satellite.SatelliteStateCallback) +UnflaggedApi: android.telephony.satellite.SatelliteManager#registerForSatelliteModemStateChanged(java.util.concurrent.Executor, android.telephony.satellite.SatelliteModemStateCallback): + New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.registerForSatelliteModemStateChanged(java.util.concurrent.Executor,android.telephony.satellite.SatelliteModemStateCallback) UnflaggedApi: android.telephony.satellite.SatelliteManager#registerForSatelliteProvisionStateChanged(java.util.concurrent.Executor, android.telephony.satellite.SatelliteProvisionStateCallback): New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.registerForSatelliteProvisionStateChanged(java.util.concurrent.Executor,android.telephony.satellite.SatelliteProvisionStateCallback) UnflaggedApi: android.telephony.satellite.SatelliteManager#requestIsDemoModeEnabled(java.util.concurrent.Executor, android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>): @@ -2389,8 +2389,8 @@ UnflaggedApi: android.telephony.satellite.SatelliteManager#stopSatelliteTransmis New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.stopSatelliteTransmissionUpdates(android.telephony.satellite.SatelliteTransmissionUpdateCallback,java.util.concurrent.Executor,java.util.function.Consumer<java.lang.Integer>) UnflaggedApi: android.telephony.satellite.SatelliteManager#unregisterForSatelliteDatagram(android.telephony.satellite.SatelliteDatagramCallback): New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.unregisterForSatelliteDatagram(android.telephony.satellite.SatelliteDatagramCallback) -UnflaggedApi: android.telephony.satellite.SatelliteManager#unregisterForSatelliteModemStateChanged(android.telephony.satellite.SatelliteStateCallback): - New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.unregisterForSatelliteModemStateChanged(android.telephony.satellite.SatelliteStateCallback) +UnflaggedApi: android.telephony.satellite.SatelliteManager#unregisterForSatelliteModemStateChanged(android.telephony.satellite.SatelliteModemStateCallback): + New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.unregisterForSatelliteModemStateChanged(android.telephony.satellite.SatelliteModemStateCallback) UnflaggedApi: android.telephony.satellite.SatelliteManager#unregisterForSatelliteProvisionStateChanged(android.telephony.satellite.SatelliteProvisionStateCallback): New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.unregisterForSatelliteProvisionStateChanged(android.telephony.satellite.SatelliteProvisionStateCallback) UnflaggedApi: android.telephony.satellite.SatelliteManager.SatelliteException: @@ -2403,10 +2403,10 @@ UnflaggedApi: android.telephony.satellite.SatelliteProvisionStateCallback: New API must be flagged with @FlaggedApi: class android.telephony.satellite.SatelliteProvisionStateCallback UnflaggedApi: android.telephony.satellite.SatelliteProvisionStateCallback#onSatelliteProvisionStateChanged(boolean): New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteProvisionStateCallback.onSatelliteProvisionStateChanged(boolean) -UnflaggedApi: android.telephony.satellite.SatelliteStateCallback: - New API must be flagged with @FlaggedApi: class android.telephony.satellite.SatelliteStateCallback -UnflaggedApi: android.telephony.satellite.SatelliteStateCallback#onSatelliteModemStateChanged(int): - New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteStateCallback.onSatelliteModemStateChanged(int) +UnflaggedApi: android.telephony.satellite.SatelliteModemStateCallback: + New API must be flagged with @FlaggedApi: class android.telephony.satellite.SatelliteModemStateCallback +UnflaggedApi: android.telephony.satellite.SatelliteModemStateCallback#onSatelliteModemStateChanged(int): + New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteModemStateCallback.onSatelliteModemStateChanged(int) UnflaggedApi: android.telephony.satellite.SatelliteTransmissionUpdateCallback: New API must be flagged with @FlaggedApi: class android.telephony.satellite.SatelliteTransmissionUpdateCallback UnflaggedApi: android.telephony.satellite.SatelliteTransmissionUpdateCallback#onReceiveDatagramStateChanged(int, int, int): diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 2e22071d72ad..bbe03a3d11a2 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -3735,6 +3735,7 @@ package android.view.animation { public class AnimationUtils { method @FlaggedApi("android.view.flags.expected_presentation_time_read_only") public static void lockAnimationClock(long, long); + method public static void lockAnimationClock(long); method public static void unlockAnimationClock(); } diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java index b4a6955325a3..845a346be593 100644 --- a/core/java/android/animation/AnimatorSet.java +++ b/core/java/android/animation/AnimatorSet.java @@ -1311,8 +1311,9 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim if (!node.mEnded) { float durationScale = ValueAnimator.getDurationScale(); durationScale = durationScale == 0 ? 1 : durationScale; - node.mEnded = node.mAnimation.pulseAnimationFrame( - (long) (animPlayTime * durationScale)); + if (node.mAnimation.pulseAnimationFrame((long) (animPlayTime * durationScale))) { + node.mEnded = true; + } } } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index f9583d22cf58..be2582f6175d 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -3075,7 +3075,7 @@ public class Activity extends ContextThemeWrapper } /** - * Request to put the freeform activity into fullscreen. The requester has to be the top-most + * Request to put the activity into fullscreen. The requester must be pinned or the top-most * activity of the focused display which can be verified using * {@link #onTopResumedActivityChanged(boolean)}. The request should also be a response to a * user input. When getting fullscreen and receiving corresponding diff --git a/core/java/android/app/BackgroundInstallControlManager.java b/core/java/android/app/BackgroundInstallControlManager.java new file mode 100644 index 000000000000..f5b68788f0ea --- /dev/null +++ b/core/java/android/app/BackgroundInstallControlManager.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 static android.Manifest.permission.QUERY_ALL_PACKAGES; +import static android.annotation.SystemApi.Client.PRIVILEGED_APPS; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.annotation.SystemService; +import android.content.Context; +import android.content.pm.IBackgroundInstallControlService; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.os.ServiceManager; + +import java.util.List; + +/** + * BackgroundInstallControlManager client allows apps to query apps installed in background. + * + * <p>Any applications that was installed without an accompanying installer UI activity paired + * with recorded user interaction event is considered background installed. This is determined by + * analysis of user-activity logs. + * + * <p>Warning: BackgroundInstallControl should not be considered a reliable or accurate + * determination of background install application. Consumers can use this as a supplementary + * signal, but must perform additional due diligence to confirm the install nature of the package. + * + * @hide + */ +@FlaggedApi(Flags.FLAG_BIC_CLIENT) +@SystemApi(client = PRIVILEGED_APPS) +@SystemService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE) +public final class BackgroundInstallControlManager { + + private static final String TAG = "BackgroundInstallControlManager"; + private static IBackgroundInstallControlService sService; + private final Context mContext; + + BackgroundInstallControlManager(Context context) { + mContext = context; + } + + private static IBackgroundInstallControlService getService() { + if (sService == null) { + sService = + IBackgroundInstallControlService.Stub.asInterface( + ServiceManager.getService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE)); + } + return sService; + } + + /** + * Returns a full list of {@link PackageInfo} of apps currently installed that are considered + * installed in the background. + * + * <p>Refer to top level doc {@link BackgroundInstallControlManager} for more details on + * background-installed applications. + * <p> + * + * @param flags - Flags will be used to call + * {@link PackageManager#getInstalledPackages(PackageInfoFlags)} to retrieve installed packages. + * @return A list of packages retrieved from {@link PackageManager} with non-background + * installed app filter applied. + * + * @hide + */ + @FlaggedApi(Flags.FLAG_BIC_CLIENT) + @SystemApi + @RequiresPermission(QUERY_ALL_PACKAGES) + public @NonNull List<PackageInfo> getBackgroundInstalledPackages( + @PackageManager.PackageInfoFlagsBits long flags) { + try { + return getService() + .getBackgroundInstalledPackages(flags, mContext.getUserId()) + .getList(); + } catch (SecurityException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + +} diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 9cf732abb86a..390fa2212298 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -1603,6 +1603,20 @@ public final class SystemServiceRegistry { } }); + // DO NOT do a flag check like this unless the flag is read-only. + // (because this code is executed during preload in zygote.) + // If the flag is mutable, the check should be inside CachedServiceFetcher. + if (Flags.bicClient()) { + registerService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE, + BackgroundInstallControlManager.class, + new CachedServiceFetcher<BackgroundInstallControlManager>() { + @Override + public BackgroundInstallControlManager createService(ContextImpl ctx) { + return new BackgroundInstallControlManager(ctx); + } + }); + } + sInitializing = true; try { // Note: the following functions need to be @SystemApis, once they become mainline diff --git a/core/java/android/app/ambient_context.aconfig b/core/java/android/app/ambient_context.aconfig new file mode 100644 index 000000000000..3f73da216b9f --- /dev/null +++ b/core/java/android/app/ambient_context.aconfig @@ -0,0 +1,8 @@ +package: "android.app" + +flag { + namespace: "biometrics_integration" + name: "ambient_heart_rate" + description: "Feature flag for adding heart rate api to ambient context." + bug: "318309481" +} diff --git a/core/java/android/app/ambientcontext/AmbientContextEvent.java b/core/java/android/app/ambientcontext/AmbientContextEvent.java index b5c66ffa72a1..f94987e8495a 100644 --- a/core/java/android/app/ambientcontext/AmbientContextEvent.java +++ b/core/java/android/app/ambientcontext/AmbientContextEvent.java @@ -16,7 +16,9 @@ package android.app.ambientcontext; +import android.annotation.FlaggedApi; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.SystemApi; import android.os.Parcelable; @@ -68,6 +70,14 @@ public final class AmbientContextEvent implements Parcelable { public static final int EVENT_BACK_DOUBLE_TAP = 3; /** + * The integer indicating a heart rate measurement was done. + * + * @see #getRatePerMinute + */ + @Event @FlaggedApi(android.app.Flags.FLAG_AMBIENT_HEART_RATE) + public static final int EVENT_HEART_RATE = 4; + + /** * Integer indicating the start of wearable vendor defined events that can be detected. * These depend on the vendor implementation. */ @@ -79,12 +89,16 @@ public final class AmbientContextEvent implements Parcelable { */ public static final String KEY_VENDOR_WEARABLE_EVENT_NAME = "wearable_event_name"; + /** Default value for the rate per minute data field. */ + private static final int RATE_PER_MINUTE_UNKNOWN = -1; + /** @hide */ @IntDef(prefix = { "EVENT_" }, value = { EVENT_UNKNOWN, EVENT_COUGH, EVENT_SNORE, EVENT_BACK_DOUBLE_TAP, + EVENT_HEART_RATE, EVENT_VENDOR_WEARABLE_START, }) @Retention(RetentionPolicy.SOURCE) @@ -170,6 +184,16 @@ public final class AmbientContextEvent implements Parcelable { return new PersistableBundle(); } + /** + * Rate per minute of the event during the start to end time. + * + * @return the rate per minute, or {@link #RATE_PER_MINUTE_UNKNOWN} if the rate is unknown. + */ + private final @IntRange(from = -1) int mRatePerMinute; + private static int defaultRatePerMinute() { + return RATE_PER_MINUTE_UNKNOWN; + } + // Code below generated by codegen v1.0.23. @@ -179,6 +203,8 @@ public final class AmbientContextEvent implements Parcelable { // // To regenerate run: // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/ambientcontext/AmbientContextEvent.java + // then manually add @FlaggedApi(android.app.Flags.FLAG_AMBIENT_HEART_RATE) back to flagged + // APIs. // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -191,6 +217,7 @@ public final class AmbientContextEvent implements Parcelable { EVENT_COUGH, EVENT_SNORE, EVENT_BACK_DOUBLE_TAP, + EVENT_HEART_RATE, EVENT_VENDOR_WEARABLE_START }) @Retention(RetentionPolicy.SOURCE) @@ -209,6 +236,8 @@ public final class AmbientContextEvent implements Parcelable { return "EVENT_SNORE"; case EVENT_BACK_DOUBLE_TAP: return "EVENT_BACK_DOUBLE_TAP"; + case EVENT_HEART_RATE: + return "EVENT_HEART_RATE"; case EVENT_VENDOR_WEARABLE_START: return "EVENT_VENDOR_WEARABLE_START"; default: return Integer.toHexString(value); @@ -255,7 +284,8 @@ public final class AmbientContextEvent implements Parcelable { @NonNull Instant endTime, @LevelValue int confidenceLevel, @LevelValue int densityLevel, - @NonNull PersistableBundle vendorData) { + @NonNull PersistableBundle vendorData, + @IntRange(from = -1) int ratePerMinute) { this.mEventType = eventType; com.android.internal.util.AnnotationValidations.validate( EventCode.class, null, mEventType); @@ -274,6 +304,10 @@ public final class AmbientContextEvent implements Parcelable { this.mVendorData = vendorData; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mVendorData); + this.mRatePerMinute = ratePerMinute; + com.android.internal.util.AnnotationValidations.validate( + IntRange.class, null, mRatePerMinute, + "from", -1); // onConstructed(); // You can define this method to get a callback } @@ -330,6 +364,17 @@ public final class AmbientContextEvent implements Parcelable { return mVendorData; } + /** + * Rate per minute of the event during the start to end time. + * + * @return the rate per minute, or {@link #RATE_PER_MINUTE_UNKNOWN} if the rate is unknown. + */ + @DataClass.Generated.Member + @FlaggedApi(android.app.Flags.FLAG_AMBIENT_HEART_RATE) + public @IntRange(from = -1) int getRatePerMinute() { + return mRatePerMinute; + } + @Override @DataClass.Generated.Member public String toString() { @@ -342,7 +387,8 @@ public final class AmbientContextEvent implements Parcelable { "endTime = " + mEndTime + ", " + "confidenceLevel = " + mConfidenceLevel + ", " + "densityLevel = " + mDensityLevel + ", " + - "vendorData = " + mVendorData + + "vendorData = " + mVendorData + ", " + + "ratePerMinute = " + mRatePerMinute + " }"; } @@ -380,6 +426,7 @@ public final class AmbientContextEvent implements Parcelable { dest.writeInt(mConfidenceLevel); dest.writeInt(mDensityLevel); dest.writeTypedObject(mVendorData, flags); + dest.writeInt(mRatePerMinute); } @Override @@ -399,6 +446,7 @@ public final class AmbientContextEvent implements Parcelable { int confidenceLevel = in.readInt(); int densityLevel = in.readInt(); PersistableBundle vendorData = (PersistableBundle) in.readTypedObject(PersistableBundle.CREATOR); + int ratePerMinute = in.readInt(); this.mEventType = eventType; com.android.internal.util.AnnotationValidations.validate( @@ -418,6 +466,10 @@ public final class AmbientContextEvent implements Parcelable { this.mVendorData = vendorData; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mVendorData); + this.mRatePerMinute = ratePerMinute; + com.android.internal.util.AnnotationValidations.validate( + IntRange.class, null, mRatePerMinute, + "from", -1); // onConstructed(); // You can define this method to get a callback } @@ -449,6 +501,7 @@ public final class AmbientContextEvent implements Parcelable { private @LevelValue int mConfidenceLevel; private @LevelValue int mDensityLevel; private @NonNull PersistableBundle mVendorData; + private @IntRange(from = -1) int mRatePerMinute; private long mBuilderFieldsSet = 0L; @@ -525,10 +578,22 @@ public final class AmbientContextEvent implements Parcelable { return this; } + /** + * Rate per minute of the event during the start to end time. + */ + @DataClass.Generated.Member + @FlaggedApi(android.app.Flags.FLAG_AMBIENT_HEART_RATE) + public @NonNull Builder setRatePerMinute(@IntRange(from = -1) int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x40; + mRatePerMinute = value; + return this; + } + /** Builds the instance. This builder should not be touched after calling this! */ public @NonNull AmbientContextEvent build() { checkNotUsed(); - mBuilderFieldsSet |= 0x40; // Mark builder used + mBuilderFieldsSet |= 0x80; // Mark builder used if ((mBuilderFieldsSet & 0x1) == 0) { mEventType = defaultEventType(); @@ -548,18 +613,22 @@ public final class AmbientContextEvent implements Parcelable { if ((mBuilderFieldsSet & 0x20) == 0) { mVendorData = defaultVendorData(); } + if ((mBuilderFieldsSet & 0x40) == 0) { + mRatePerMinute = defaultRatePerMinute(); + } AmbientContextEvent o = new AmbientContextEvent( mEventType, mStartTime, mEndTime, mConfidenceLevel, mDensityLevel, - mVendorData); + mVendorData, + mRatePerMinute); return o; } private void checkNotUsed() { - if ((mBuilderFieldsSet & 0x40) != 0) { + if ((mBuilderFieldsSet & 0x80) != 0) { throw new IllegalStateException( "This Builder should not be reused. Use a new Builder instance instead"); } @@ -567,10 +636,10 @@ public final class AmbientContextEvent implements Parcelable { } @DataClass.Generated( - time = 1671217108067L, + time = 1704895515931L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/app/ambientcontext/AmbientContextEvent.java", - inputSignatures = "public static final int EVENT_UNKNOWN\npublic static final int EVENT_COUGH\npublic static final int EVENT_SNORE\npublic static final int EVENT_BACK_DOUBLE_TAP\npublic static final int EVENT_VENDOR_WEARABLE_START\npublic static final java.lang.String KEY_VENDOR_WEARABLE_EVENT_NAME\npublic static final int LEVEL_UNKNOWN\npublic static final int LEVEL_LOW\npublic static final int LEVEL_MEDIUM_LOW\npublic static final int LEVEL_MEDIUM\npublic static final int LEVEL_MEDIUM_HIGH\npublic static final int LEVEL_HIGH\nprivate final @android.app.ambientcontext.AmbientContextEvent.EventCode int mEventType\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mStartTime\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mEndTime\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mConfidenceLevel\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mDensityLevel\nprivate final @android.annotation.NonNull android.os.PersistableBundle mVendorData\nprivate static int defaultEventType()\nprivate static @android.annotation.NonNull java.time.Instant defaultStartTime()\nprivate static @android.annotation.NonNull java.time.Instant defaultEndTime()\nprivate static int defaultConfidenceLevel()\nprivate static int defaultDensityLevel()\nprivate static android.os.PersistableBundle defaultVendorData()\nclass AmbientContextEvent extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=false, genHiddenConstDefs=true, genParcelable=true, genToString=true)") + inputSignatures = "public static final int EVENT_UNKNOWN\npublic static final int EVENT_COUGH\npublic static final int EVENT_SNORE\npublic static final int EVENT_BACK_DOUBLE_TAP\npublic static final @android.app.ambientcontext.AmbientContextEvent.Event @android.annotation.FlaggedApi int EVENT_HEART_RATE\npublic static final int EVENT_VENDOR_WEARABLE_START\npublic static final java.lang.String KEY_VENDOR_WEARABLE_EVENT_NAME\nprivate static final int RATE_PER_MINUTE_UNKNOWN\npublic static final int LEVEL_UNKNOWN\npublic static final int LEVEL_LOW\npublic static final int LEVEL_MEDIUM_LOW\npublic static final int LEVEL_MEDIUM\npublic static final int LEVEL_MEDIUM_HIGH\npublic static final int LEVEL_HIGH\nprivate final @android.app.ambientcontext.AmbientContextEvent.EventCode int mEventType\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mStartTime\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mEndTime\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mConfidenceLevel\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mDensityLevel\nprivate final @android.annotation.NonNull android.os.PersistableBundle mVendorData\nprivate final @android.annotation.IntRange int mRatePerMinute\nprivate static int defaultEventType()\nprivate static @android.annotation.NonNull java.time.Instant defaultStartTime()\nprivate static @android.annotation.NonNull java.time.Instant defaultEndTime()\nprivate static int defaultConfidenceLevel()\nprivate static int defaultDensityLevel()\nprivate static android.os.PersistableBundle defaultVendorData()\nprivate static int defaultRatePerMinute()\nclass AmbientContextEvent extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=false, genHiddenConstDefs=true, genParcelable=true, genToString=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/app/background_install_control_manager.aconfig b/core/java/android/app/background_install_control_manager.aconfig new file mode 100644 index 000000000000..029b93ab4534 --- /dev/null +++ b/core/java/android/app/background_install_control_manager.aconfig @@ -0,0 +1,9 @@ +package: "android.app" + +flag { + namespace: "background_install_control" + name: "bic_client" + description: "System API for background install control." + is_fixed_read_only: true + bug: "287507984" +} diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java index 637187120922..a41cb1fa6ea8 100644 --- a/core/java/android/app/backup/FullBackup.java +++ b/core/java/android/app/backup/FullBackup.java @@ -239,7 +239,7 @@ public class FullBackup { Log.e(TAG, "Unable to create/open file " + outFile.getPath(), e); } - byte[] buffer = new byte[32 * 1024]; + byte[] buffer = new byte[64 * 1024]; final long origSize = size; FileInputStream in = new FileInputStream(data.getFileDescriptor()); while (size > 0) { diff --git a/core/java/android/content/OWNERS b/core/java/android/content/OWNERS index 90c3d04d62d0..a37408b7d847 100644 --- a/core/java/android/content/OWNERS +++ b/core/java/android/content/OWNERS @@ -4,6 +4,7 @@ per-file ContextWrapper.java = * per-file *Content* = file:/services/core/java/com/android/server/am/OWNERS per-file *Sync* = file:/services/core/java/com/android/server/am/OWNERS per-file IntentFilter.java = file:/PACKAGE_MANAGER_OWNERS +per-file UriRelativeFilter* = file:/PACKAGE_MANAGER_OWNERS per-file IntentFilter.java = file:/services/core/java/com/android/server/am/OWNERS per-file Intent.java = file:/INTENT_OWNERS per-file AutofillOptions* = file:/core/java/android/service/autofill/OWNERS diff --git a/core/java/android/content/pm/IBackgroundInstallControlService.aidl b/core/java/android/content/pm/IBackgroundInstallControlService.aidl index c8e7caebc821..4bc8fe16b249 100644 --- a/core/java/android/content/pm/IBackgroundInstallControlService.aidl +++ b/core/java/android/content/pm/IBackgroundInstallControlService.aidl @@ -16,11 +16,20 @@ package android.content.pm; + import android.content.pm.ParceledListSlice; +import android.os.IRemoteCallback; /** * {@hide} */ interface IBackgroundInstallControlService { + @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest.permission.QUERY_ALL_PACKAGES)") ParceledListSlice getBackgroundInstalledPackages(long flags, int userId); -} + + @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(allOf = {android.Manifest.permission.QUERY_ALL_PACKAGES, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})") + void registerBackgroundInstallCallback(IRemoteCallback callback); + + @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(allOf = {android.Manifest.permission.QUERY_ALL_PACKAGES, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})") + void unregisterBackgroundInstallCallback(IRemoteCallback callback); +}
\ No newline at end of file diff --git a/core/java/android/hardware/SyncFence.java b/core/java/android/hardware/SyncFence.java index d6052cd4c67f..c2440fbaab1b 100644 --- a/core/java/android/hardware/SyncFence.java +++ b/core/java/android/hardware/SyncFence.java @@ -16,6 +16,7 @@ package android.hardware; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.media.Image; import android.media.ImageWriter; @@ -26,6 +27,8 @@ import android.os.ParcelFileDescriptor; import android.os.Parcelable; import android.os.SystemClock; +import com.android.window.flags.Flags; + import libcore.util.NativeAllocationRegistry; import java.io.FileDescriptor; @@ -121,6 +124,19 @@ public final class SyncFence implements AutoCloseable, Parcelable { } } + /** + * Creates a copy of the SyncFence from an existing one. + * Both fences must be closed() independently. + */ + @FlaggedApi(Flags.FLAG_SDK_DESIRED_PRESENT_TIME) + public SyncFence(@NonNull SyncFence other) { + this(other.mNativePtr); + + if (mNativePtr != 0) { + nIncRef(mNativePtr); + } + } + private SyncFence() { mCloser = () -> {}; } @@ -312,4 +328,5 @@ public final class SyncFence implements AutoCloseable, Parcelable { private static native int nGetFd(long nPtr); private static native boolean nWait(long nPtr, long timeout); private static native long nGetSignalTime(long nPtr); + private static native void nIncRef(long nPtr); } diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index 3affb73d1075..0cd1c8ca89fe 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -79,6 +79,8 @@ import android.util.Log; import android.util.Range; import android.util.Size; +import com.android.internal.camera.flags.Flags; + import dalvik.annotation.optimization.FastNative; import dalvik.system.VMRuntime; @@ -1795,49 +1797,57 @@ public class CameraMetadataNative implements Parcelable { return false; } - long[] tsArray = new long[samples.length]; - float[] intrinsicsArray = new float[samples.length * 5]; - for (int i = 0; i < samples.length; i++) { - tsArray[i] = samples[i].getTimestamp(); - System.arraycopy(samples[i].getLensIntrinsics(), 0, intrinsicsArray, 5*i, 5); + if (Flags.concertMode()) { + long[] tsArray = new long[samples.length]; + float[] intrinsicsArray = new float[samples.length * 5]; + for (int i = 0; i < samples.length; i++) { + tsArray[i] = samples[i].getTimestampNanos(); + System.arraycopy(samples[i].getLensIntrinsics(), 0, intrinsicsArray, 5 * i, 5); - } - setBase(CaptureResult.STATISTICS_LENS_INTRINSIC_SAMPLES, intrinsicsArray); - setBase(CaptureResult.STATISTICS_LENS_INTRINSIC_TIMESTAMPS, tsArray); + } + setBase(CaptureResult.STATISTICS_LENS_INTRINSIC_SAMPLES, intrinsicsArray); + setBase(CaptureResult.STATISTICS_LENS_INTRINSIC_TIMESTAMPS, tsArray); - return true; + return true; + } else { + return false; + } } private LensIntrinsicsSample[] getLensIntrinsicSamples() { - long[] timestamps = getBase(CaptureResult.STATISTICS_LENS_INTRINSIC_TIMESTAMPS); - float[] intrinsics = getBase(CaptureResult.STATISTICS_LENS_INTRINSIC_SAMPLES); + if (Flags.concertMode()) { + long[] timestamps = getBase(CaptureResult.STATISTICS_LENS_INTRINSIC_TIMESTAMPS); + float[] intrinsics = getBase(CaptureResult.STATISTICS_LENS_INTRINSIC_SAMPLES); - if (timestamps == null) { - if (intrinsics != null) { - throw new AssertionError("timestamps is null but intrinsics is not"); - } + if (timestamps == null) { + if (intrinsics != null) { + throw new AssertionError("timestamps is null but intrinsics is not"); + } - return null; - } + return null; + } - if (intrinsics == null) { - throw new AssertionError("timestamps is not null but intrinsics is"); - } else if((intrinsics.length % 5) != 0) { - throw new AssertionError("intrinsics are not multiple of 5"); - } + if (intrinsics == null) { + throw new AssertionError("timestamps is not null but intrinsics is"); + } else if ((intrinsics.length % 5) != 0) { + throw new AssertionError("intrinsics are not multiple of 5"); + } - if ((intrinsics.length / 5) != timestamps.length) { - throw new AssertionError(String.format( - "timestamps has %d entries but intrinsics has %d", timestamps.length, - intrinsics.length / 5)); - } + if ((intrinsics.length / 5) != timestamps.length) { + throw new AssertionError(String.format( + "timestamps has %d entries but intrinsics has %d", timestamps.length, + intrinsics.length / 5)); + } - LensIntrinsicsSample[] samples = new LensIntrinsicsSample[timestamps.length]; - for (int i = 0; i < timestamps.length; i++) { - float[] currentIntrinsic = Arrays.copyOfRange(intrinsics, 5*i, 5*i + 5); - samples[i] = new LensIntrinsicsSample(timestamps[i], currentIntrinsic); + LensIntrinsicsSample[] samples = new LensIntrinsicsSample[timestamps.length]; + for (int i = 0; i < timestamps.length; i++) { + float[] currentIntrinsic = Arrays.copyOfRange(intrinsics, 5 * i, 5 * i + 5); + samples[i] = new LensIntrinsicsSample(timestamps[i], currentIntrinsic); + } + return samples; + } else { + return null; } - return samples; } private Capability[] getExtendedSceneModeCapabilities() { diff --git a/core/java/android/hardware/camera2/params/LensIntrinsicsSample.java b/core/java/android/hardware/camera2/params/LensIntrinsicsSample.java index 575cbfae34e3..9a4ec5c94b5b 100644 --- a/core/java/android/hardware/camera2/params/LensIntrinsicsSample.java +++ b/core/java/android/hardware/camera2/params/LensIntrinsicsSample.java @@ -37,16 +37,18 @@ public final class LensIntrinsicsSample { * Create a new {@link LensIntrinsicsSample}. * * <p>{@link LensIntrinsicsSample} contains the timestamp and the - * {@link CaptureResult#LENS_INTRINSIC_CALIBRATION} sample. + * {@link CaptureResult#LENS_INTRINSIC_CALIBRATION} sample.</p> * - * @param timestamp timestamp of the lens intrinsics sample. - * @param lensIntrinsics the lens intrinsic calibration for the sample. + * @param timestampNs timestamp in nanoseconds of the lens intrinsics sample. This uses the + * same time basis as {@link CaptureResult#SENSOR_TIMESTAMP}. + * @param lensIntrinsics the lens {@link CaptureResult#LENS_INTRINSIC_CALIBRATION intrinsic} + * calibration for the sample. * * @throws IllegalArgumentException if lensIntrinsics length is different from 5 */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) - public LensIntrinsicsSample(final long timestamp, @NonNull final float[] lensIntrinsics) { - mTimestampNs = timestamp; + public LensIntrinsicsSample(final long timestampNs, @NonNull final float[] lensIntrinsics) { + mTimestampNs = timestampNs; Preconditions.checkArgument(lensIntrinsics.length == 5); mLensIntrinsics = lensIntrinsics; } @@ -54,18 +56,18 @@ public final class LensIntrinsicsSample { /** * Get the timestamp in nanoseconds. * - *<p>The timestamps are in the same timebase as and comparable to + *<p>The timestamps are in the same time basis as and comparable to *{@link CaptureResult#SENSOR_TIMESTAMP android.sensor.timestamp}.</p> * * @return a long value (guaranteed to be finite) */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) - public long getTimestamp() { + public long getTimestampNanos() { return mTimestampNs; } /** - * Get the lens intrinsics calibration + * Get the lens {@link CaptureResult#LENS_INTRINSIC_CALIBRATION intrinsics} calibration * * @return a floating point value (guaranteed to be finite) * @see CaptureResult#LENS_INTRINSIC_CALIBRATION diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 134a510df5f3..8f0e0c911f56 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -1831,15 +1831,6 @@ public final class DisplayManager { String KEY_POWER_THROTTLING_DATA = "power_throttling_data"; /** - * Key for new power controller feature flag. If enabled new DisplayPowerController will - * be used. - * Read value via {@link android.provider.DeviceConfig#getBoolean(String, String, boolean)} - * with {@link android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER} as the namespace. - * @hide - */ - String KEY_NEW_POWER_CONTROLLER = "use_newly_structured_display_power_controller"; - - /** * Key for normal brightness mode controller feature flag. * It enables NormalBrightnessModeController. * Read value via {@link android.provider.DeviceConfig#getBoolean(String, String, boolean)} diff --git a/core/java/android/hardware/radio/ProgramList.java b/core/java/android/hardware/radio/ProgramList.java index c5167dbc7d4c..a3a2a2e6fd16 100644 --- a/core/java/android/hardware/radio/ProgramList.java +++ b/core/java/android/hardware/radio/ProgramList.java @@ -304,11 +304,7 @@ public final class ProgramList implements AutoCloseable { * * @param id primary identifier of a program to fetch * @return the program info, or null if there is no such program on the list - * - * @deprecated Use {@link #getProgramInfos(ProgramSelector.Identifier)} to get all programs - * with the given primary identifier */ - @Deprecated public @Nullable RadioManager.ProgramInfo get(@NonNull ProgramSelector.Identifier id) { Map<UniqueProgramIdentifier, RadioManager.ProgramInfo> entries; synchronized (mLock) { diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java index 7e5c141a399a..4c95e026a180 100644 --- a/core/java/android/hardware/radio/ProgramSelector.java +++ b/core/java/android/hardware/radio/ProgramSelector.java @@ -312,20 +312,14 @@ public final class ProgramSelector implements Parcelable { public static final int IDENTIFIER_TYPE_DRMO_FREQUENCY = 10; /** * 1: AM, 2:FM - * @deprecated use {@link #IDENTIFIER_TYPE_DRMO_FREQUENCY} instead */ - @Deprecated public static final int IDENTIFIER_TYPE_DRMO_MODULATION = 11; /** * 32bit primary identifier for SiriusXM Satellite Radio. - * - * @deprecated SiriusXM Satellite Radio is not supported */ public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 12; /** * 0-999 range - * - * @deprecated SiriusXM Satellite Radio is not supported */ public static final int IDENTIFIER_TYPE_SXM_CHANNEL = 13; /** diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java index f0f7e8a22e2a..41f21efd60dd 100644 --- a/core/java/android/hardware/radio/RadioManager.java +++ b/core/java/android/hardware/radio/RadioManager.java @@ -166,12 +166,7 @@ public class RadioManager { * analog handover state managed from the HAL implementation side. * * <p>Some radio technologies may not support this, i.e. DAB. - * - * @deprecated Use {@link #CONFIG_FORCE_ANALOG_FM} instead. If {@link #CONFIG_FORCE_ANALOG_FM} - * is supported in HAL, {@link RadioTuner#setConfigFlag} and {@link RadioTuner#isConfigFlagSet} - * with CONFIG_FORCE_ANALOG will set/get the value of {@link #CONFIG_FORCE_ANALOG_FM}. */ - @Deprecated public static final int CONFIG_FORCE_ANALOG = 2; /** * Forces the digital playback for the supporting radio technology. diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java index 70cf97308ffb..c727a6034006 100644 --- a/core/java/android/net/vcn/VcnManager.java +++ b/core/java/android/net/vcn/VcnManager.java @@ -115,6 +115,20 @@ public class VcnManager { "vcn_restricted_transports"; /** + * Key for number of seconds to wait before entering safe mode + * + * <p>A VcnGatewayConnection will enter safe mode when it takes over the configured timeout to + * enter {@link ConnectedState}. + * + * <p>Defaults to 30, unless overridden by carrier config + * + * @hide + */ + @NonNull + public static final String VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY = + "vcn_safe_mode_timeout_seconds_key"; + + /** * Key for maximum number of parallel SAs for tunnel aggregation * * <p>If set to a value > 1, multiple tunnels will be set up, and inbound traffic will be @@ -135,6 +149,7 @@ public class VcnManager { VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY, VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY, VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY, + VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY, VCN_TUNNEL_AGGREGATION_SA_COUNT_MAX_KEY, }; diff --git a/core/java/android/net/vcn/flags.aconfig b/core/java/android/net/vcn/flags.aconfig index 6956916af0f1..67a1906d48ed 100644 --- a/core/java/android/net/vcn/flags.aconfig +++ b/core/java/android/net/vcn/flags.aconfig @@ -5,4 +5,11 @@ flag { namespace: "vcn" description: "Feature flag for safe mode configurability" bug: "276358140" +} + +flag { + name: "safe_mode_timeout_config" + namespace: "vcn" + description: "Feature flag for adjustable safe mode timeout" + bug: "317406085" }
\ No newline at end of file diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index 8219d2f873de..75f5491a416f 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -812,8 +812,8 @@ public final class NfcAdapter { if (context == null) { throw new IllegalArgumentException("context cannot be null"); } - context = context.getApplicationContext(); - if (context == null) { + Context applicationContext = context.getApplicationContext(); + if (applicationContext == null) { throw new IllegalArgumentException( "context not associated with any application (using a mock context?)"); } diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java index ad86d70db967..81eab71fe080 100644 --- a/core/java/android/nfc/cardemulation/CardEmulation.java +++ b/core/java/android/nfc/cardemulation/CardEmulation.java @@ -69,7 +69,12 @@ public final class CardEmulation { * specified in {@link #EXTRA_CATEGORY}. There is an optional * extra field using {@link Intent#EXTRA_USER} to specify * the {@link UserHandle} of the user that owns the app. + * + * @deprecated Please use {@link android.app.role.RoleManager#createRequestRoleIntent(String)} + * with {@link android.app.role.RoleManager#ROLE_WALLET} parameter + * and {@link Activity#startActivityForResult(Intent, int)} instead. */ + @Deprecated @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT"; @@ -1084,4 +1089,32 @@ public final class CardEmulation { sService = adapter.getCardEmulationService(); } + /** + * Returns the {@link Settings.Secure#NFC_PAYMENT_DEFAULT_COMPONENT} for the given user. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) + @FlaggedApi(android.permission.flags.Flags.FLAG_WALLET_ROLE_ENABLED) + @Nullable + public ApduServiceInfo getPreferredPaymentService() { + try { + return sService.getPreferredPaymentService(mContext.getUser().getIdentifier()); + } catch (RemoteException e) { + // Try one more time + recoverService(); + if (sService == null) { + Log.e(TAG, "Failed to recover CardEmulationService."); + return null; + } + try { + return sService.getPreferredPaymentService(mContext.getUser().getIdentifier()); + } catch (RemoteException ee) { + Log.e(TAG, "Failed to reach CardEmulationService."); + return null; + } + } + } + } diff --git a/core/java/android/os/AggregateBatteryConsumer.java b/core/java/android/os/AggregateBatteryConsumer.java index c5f56144c29c..67e21957839a 100644 --- a/core/java/android/os/AggregateBatteryConsumer.java +++ b/core/java/android/os/AggregateBatteryConsumer.java @@ -33,6 +33,7 @@ import java.io.PrintWriter; * * {@hide} */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public final class AggregateBatteryConsumer extends BatteryConsumer { static final int CONSUMER_TYPE_AGGREGATE = 0; diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 1a0ce702e38d..b68b94d5bf2d 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -37,6 +37,7 @@ import android.server.ServerProtoEnums; import android.service.batterystats.BatteryStatsServiceDumpHistoryProto; import android.service.batterystats.BatteryStatsServiceDumpProto; import android.telephony.CellSignalStrength; +import android.telephony.ModemActivityInfo; import android.telephony.ServiceState; import android.telephony.TelephonyManager; import android.text.format.DateFormat; @@ -83,6 +84,7 @@ import java.util.Map; * except where indicated otherwise. * @hide */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public abstract class BatteryStats { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) @@ -463,6 +465,7 @@ public abstract class BatteryStats { /** * State for keeping track of long counting information. */ + @android.ravenwood.annotation.RavenwoodKeepWholeClass public static abstract class LongCounter { /** @@ -2724,12 +2727,6 @@ public abstract class BatteryStats { */ public abstract int getMobileRadioActiveUnknownCount(int which); - public static final int DATA_CONNECTION_OUT_OF_SERVICE = 0; - public static final int DATA_CONNECTION_EMERGENCY_SERVICE = - TelephonyManager.getAllNetworkTypes().length + 1; - public static final int DATA_CONNECTION_OTHER = DATA_CONNECTION_EMERGENCY_SERVICE + 1; - - static final String[] DATA_CONNECTION_NAMES = { "oos", "gprs", "edge", "umts", "cdma", "evdo_0", "evdo_A", "1xrtt", "hsdpa", "hsupa", "hspa", "iden", "evdo_b", "lte", @@ -2737,9 +2734,28 @@ public abstract class BatteryStats { "emngcy", "other" }; + public static final int DATA_CONNECTION_OUT_OF_SERVICE = 0; + public static final int DATA_CONNECTION_EMERGENCY_SERVICE = getEmergencyNetworkConnectionType(); + public static final int DATA_CONNECTION_OTHER = DATA_CONNECTION_EMERGENCY_SERVICE + 1; + @UnsupportedAppUsage public static final int NUM_DATA_CONNECTION_TYPES = DATA_CONNECTION_OTHER + 1; + @android.ravenwood.annotation.RavenwoodReplace + private static int getEmergencyNetworkConnectionType() { + int count = TelephonyManager.getAllNetworkTypes().length; + if (DATA_CONNECTION_NAMES.length != count + 3) { // oos, emngcy, other + throw new IllegalStateException( + "DATA_CONNECTION_NAMES length does not match network type count. " + + "Expected: " + (count + 3) + ", actual:" + DATA_CONNECTION_NAMES.length); + } + return count + 1; + } + + private static int getEmergencyNetworkConnectionType$ravenwood() { + return DATA_CONNECTION_NAMES.length - 2; + } + /** * Returns the time in microseconds that the phone has been running with * the given data connection. @@ -9015,4 +9031,44 @@ public abstract class BatteryStats { (lhs, rhs) -> Double.compare(rhs.millisecondsPerPacket, lhs.millisecondsPerPacket)); return uidMobileRadioStats; } + + @android.ravenwood.annotation.RavenwoodReplace + @VisibleForTesting + protected static boolean isLowRamDevice() { + return ActivityManager.isLowRamDeviceStatic(); + } + + protected static boolean isLowRamDevice$ravenwood() { + return false; + } + + @android.ravenwood.annotation.RavenwoodReplace + @VisibleForTesting + protected static int getCellSignalStrengthLevelCount() { + return CellSignalStrength.getNumSignalStrengthLevels(); + } + + protected static int getCellSignalStrengthLevelCount$ravenwood() { + return 5; + } + + @android.ravenwood.annotation.RavenwoodReplace + @VisibleForTesting + protected static int getModemTxPowerLevelCount() { + return ModemActivityInfo.getNumTxPowerLevels(); + } + + protected static int getModemTxPowerLevelCount$ravenwood() { + return 5; + } + + @android.ravenwood.annotation.RavenwoodReplace + @VisibleForTesting + protected static boolean isKernelStatsAvailable() { + return true; + } + + protected static boolean isKernelStatsAvailable$ravenwood() { + return false; + } } diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java index 511f4649e4d4..90d82e7ad2a4 100644 --- a/core/java/android/os/BatteryUsageStats.java +++ b/core/java/android/os/BatteryUsageStats.java @@ -56,6 +56,7 @@ import java.util.List; * * @hide */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public final class BatteryUsageStats implements Parcelable, Closeable { /** diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java index 32840d4b5837..203ef47d857e 100644 --- a/core/java/android/os/BatteryUsageStatsQuery.java +++ b/core/java/android/os/BatteryUsageStatsQuery.java @@ -28,6 +28,7 @@ import java.lang.annotation.RetentionPolicy; * * @hide */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public final class BatteryUsageStatsQuery implements Parcelable { @NonNull diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java index c60f949da408..fbec518e4a29 100644 --- a/core/java/android/os/MessageQueue.java +++ b/core/java/android/os/MessageQueue.java @@ -57,6 +57,7 @@ public final class MessageQueue { @UnsupportedAppUsage Message mMessages; + private Message mLast; @UnsupportedAppUsage private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>(); private SparseArray<FileDescriptorRecord> mFileDescriptorRecords; @@ -66,6 +67,10 @@ public final class MessageQueue { // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout. private boolean mBlocked; + // Tracks the number of async message. We use this in enqueueMessage() to avoid searching the + // queue for async messages when inserting a message at the tail. + private int mAsyncMessageCount; + // The next barrier token. // Barriers are indicated by messages with a null target whose arg1 field carries the token. @UnsupportedAppUsage @@ -364,12 +369,21 @@ public final class MessageQueue { mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; + if (prevMsg.next == null) { + mLast = prevMsg; + } } else { mMessages = msg.next; + if (msg.next == null) { + mLast = null; + } } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); + if (msg.isAsynchronous()) { + mAsyncMessageCount--; + } return msg; } } else { @@ -492,6 +506,14 @@ public final class MessageQueue { msg.when = when; msg.arg1 = token; + if (Flags.messageQueueTailTracking() && mLast != null && mLast.when <= when) { + /* Message goes to tail of list */ + mLast.next = msg; + mLast = msg; + msg.next = null; + return token; + } + Message prev = null; Message p = mMessages; if (when != 0) { @@ -500,6 +522,12 @@ public final class MessageQueue { p = p.next; } } + + if (p == null) { + /* We reached the tail of the list, or list is empty. */ + mLast = msg; + } + if (prev != null) { // invariant: p == prev.next msg.next = p; prev.next = msg; @@ -540,9 +568,15 @@ public final class MessageQueue { final boolean needWake; if (prev != null) { prev.next = p.next; + if (prev.next == null) { + mLast = prev; + } needWake = false; } else { mMessages = p.next; + if (mMessages == null) { + mLast = null; + } needWake = mMessages == null || mMessages.target != null; } p.recycleUnchecked(); @@ -582,24 +616,77 @@ public final class MessageQueue { msg.next = p; mMessages = msg; needWake = mBlocked; + if (p == null) { + mLast = mMessages; + } } else { - // Inserted within the middle of the queue. Usually we don't have to wake - // up the event queue unless there is a barrier at the head of the queue - // and the message is the earliest asynchronous message in the queue. - needWake = mBlocked && p.target == null && msg.isAsynchronous(); - Message prev; - for (;;) { - prev = p; - p = p.next; - if (p == null || when < p.when) { - break; + // Message is to be inserted at tail or middle of queue. Usually we don't have to + // wake up the event queue unless there is a barrier at the head of the queue and + // the message is the earliest asynchronous message in the queue. + // + // For readability, we split this portion of the function into two blocks based on + // whether tail tracking is enabled. This has a minor implication for the case + // where tail tracking is disabled. See the comment below. + if (Flags.messageQueueTailTracking()) { + needWake = mBlocked && p.target == null && msg.isAsynchronous() + && mAsyncMessageCount == 0; + if (when >= mLast.when) { + msg.next = null; + mLast.next = msg; + mLast = msg; + } else { + // Inserted within the middle of the queue. + Message prev; + for (;;) { + prev = p; + p = p.next; + if (p == null || when < p.when) { + break; + } + } + if (p == null) { + /* Inserting at tail of queue */ + mLast = msg; + } + msg.next = p; // invariant: p == prev.next + prev.next = msg; } - if (needWake && p.isAsynchronous()) { - needWake = false; + } else { + needWake = mBlocked && p.target == null && msg.isAsynchronous(); + Message prev; + for (;;) { + prev = p; + p = p.next; + if (p == null || when < p.when) { + break; + } + if (needWake && p.isAsynchronous()) { + needWake = false; + } } + msg.next = p; // invariant: p == prev.next + prev.next = msg; + + /* + * If this block is executing then we have a build without tail tracking - + * specifically: Flags.messageQueueTailTracking() == false. This is determined + * at build time so the flag won't change on us during runtime. + * + * Since we don't want to pepper the code with extra checks, we only check + * for tail tracking when we might use mLast. Otherwise, we continue to update + * mLast as the tail of the list. + * + * In this case however we are not maintaining mLast correctly. Since we never + * use it, this is fine. However, we run the risk of leaking a reference. + * So set mLast to null in this case to avoid any Message leaks. The other + * sites will never use the value so we are safe against null pointer derefs. + */ + mLast = null; } - msg.next = p; // invariant: p == prev.next - prev.next = msg; + } + + if (msg.isAsynchronous()) { + mAsyncMessageCount++; } // We can assume mPtr != 0 because mQuitting is false. @@ -692,10 +779,17 @@ public final class MessageQueue { && (object == null || p.obj == object)) { Message n = p.next; mMessages = n; + if (p.isAsynchronous()) { + mAsyncMessageCount--; + } p.recycleUnchecked(); p = n; } + if (p == null) { + mLast = mMessages; + } + // Remove all messages after front. while (p != null) { Message n = p.next; @@ -703,8 +797,14 @@ public final class MessageQueue { if (n.target == h && n.what == what && (object == null || n.obj == object)) { Message nn = n.next; + if (n.isAsynchronous()) { + mAsyncMessageCount--; + } n.recycleUnchecked(); p.next = nn; + if (p.next == null) { + mLast = p; + } continue; } } @@ -726,10 +826,17 @@ public final class MessageQueue { && (object == null || object.equals(p.obj))) { Message n = p.next; mMessages = n; + if (p.isAsynchronous()) { + mAsyncMessageCount--; + } p.recycleUnchecked(); p = n; } + if (p == null) { + mLast = mMessages; + } + // Remove all messages after front. while (p != null) { Message n = p.next; @@ -737,8 +844,14 @@ public final class MessageQueue { if (n.target == h && n.what == what && (object == null || object.equals(n.obj))) { Message nn = n.next; + if (n.isAsynchronous()) { + mAsyncMessageCount--; + } n.recycleUnchecked(); p.next = nn; + if (p.next == null) { + mLast = p; + } continue; } } @@ -760,10 +873,17 @@ public final class MessageQueue { && (object == null || p.obj == object)) { Message n = p.next; mMessages = n; + if (p.isAsynchronous()) { + mAsyncMessageCount--; + } p.recycleUnchecked(); p = n; } + if (p == null) { + mLast = mMessages; + } + // Remove all messages after front. while (p != null) { Message n = p.next; @@ -771,8 +891,14 @@ public final class MessageQueue { if (n.target == h && n.callback == r && (object == null || n.obj == object)) { Message nn = n.next; + if (n.isAsynchronous()) { + mAsyncMessageCount--; + } n.recycleUnchecked(); p.next = nn; + if (p.next == null) { + mLast = p; + } continue; } } @@ -794,10 +920,17 @@ public final class MessageQueue { && (object == null || object.equals(p.obj))) { Message n = p.next; mMessages = n; + if (p.isAsynchronous()) { + mAsyncMessageCount--; + } p.recycleUnchecked(); p = n; } + if (p == null) { + mLast = mMessages; + } + // Remove all messages after front. while (p != null) { Message n = p.next; @@ -805,8 +938,14 @@ public final class MessageQueue { if (n.target == h && n.callback == r && (object == null || object.equals(n.obj))) { Message nn = n.next; + if (n.isAsynchronous()) { + mAsyncMessageCount--; + } n.recycleUnchecked(); p.next = nn; + if (p.next == null) { + mLast = p; + } continue; } } @@ -829,18 +968,31 @@ public final class MessageQueue { && (object == null || p.obj == object)) { Message n = p.next; mMessages = n; + if (p.isAsynchronous()) { + mAsyncMessageCount--; + } p.recycleUnchecked(); p = n; } + if (p == null) { + mLast = mMessages; + } + // Remove all messages after front. while (p != null) { Message n = p.next; if (n != null) { if (n.target == h && (object == null || n.obj == object)) { Message nn = n.next; + if (n.isAsynchronous()) { + mAsyncMessageCount--; + } n.recycleUnchecked(); p.next = nn; + if (p.next == null) { + mLast = p; + } continue; } } @@ -862,18 +1014,31 @@ public final class MessageQueue { && (object == null || object.equals(p.obj))) { Message n = p.next; mMessages = n; + if (p.isAsynchronous()) { + mAsyncMessageCount--; + } p.recycleUnchecked(); p = n; } + if (p == null) { + mLast = mMessages; + } + // Remove all messages after front. while (p != null) { Message n = p.next; if (n != null) { if (n.target == h && (object == null || object.equals(n.obj))) { Message nn = n.next; + if (n.isAsynchronous()) { + mAsyncMessageCount--; + } n.recycleUnchecked(); p.next = nn; + if (p.next == null) { + mLast = p; + } continue; } } @@ -890,6 +1055,8 @@ public final class MessageQueue { p = n; } mMessages = null; + mLast = null; + mAsyncMessageCount = 0; } private void removeAllFutureMessagesLocked() { @@ -911,9 +1078,14 @@ public final class MessageQueue { p = n; } p.next = null; + mLast = p; + do { p = n; n = p.next; + if (p.isAsynchronous()) { + mAsyncMessageCount--; + } p.recycleUnchecked(); } while (n != null); } diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java index 9c11ad433b8f..164e2659d654 100644 --- a/core/java/android/os/PowerComponents.java +++ b/core/java/android/os/PowerComponents.java @@ -40,6 +40,7 @@ import java.io.PrintWriter; * * @hide */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass class PowerComponents { private final BatteryConsumer.BatteryConsumerData mData; diff --git a/core/java/android/os/PowerMonitorReadings.java b/core/java/android/os/PowerMonitorReadings.java index bb677d529507..a0ab066ffb75 100644 --- a/core/java/android/os/PowerMonitorReadings.java +++ b/core/java/android/os/PowerMonitorReadings.java @@ -71,7 +71,7 @@ public final class PowerMonitorReadings { */ @FlaggedApi("com.android.server.power.optimization.power_monitor_api") @ElapsedRealtimeLong - public long getTimestamp(@NonNull PowerMonitor powerMonitor) { + public long getTimestampMillis(@NonNull PowerMonitor powerMonitor) { int offset = Arrays.binarySearch(mPowerMonitors, powerMonitor, POWER_MONITOR_COMPARATOR); if (offset >= 0) { return mTimestampsMs[offset]; diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index f952fcf36e60..dd0436cbb2f2 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -19,6 +19,7 @@ package android.os; import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; import android.annotation.ElapsedRealtimeLong; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; @@ -37,6 +38,7 @@ import android.webkit.WebViewZygote; import com.android.internal.os.SomeArgs; import com.android.internal.util.Preconditions; +import com.android.sdksandbox.flags.Flags; import dalvik.system.VMRuntime; @@ -1016,11 +1018,30 @@ public class Process { @SystemApi(client = MODULE_LIBRARIES) @TestApi @android.ravenwood.annotation.RavenwoodKeep + // TODO(b/318651609): Deprecate once Process#getSdkSandboxUidForAppUid is rolled out to 100% public static final int toSdkSandboxUid(int uid) { return uid + (FIRST_SDK_SANDBOX_UID - FIRST_APPLICATION_UID); } /** + * Returns the sdk sandbox uid corresponding to an app uid. + * @see android.app.sdksandbox.SdkSandboxManager + * + * @param uid the app uid + * @return the sdk sandbox uid for the given app uid + * + * @throws IllegalArgumentException if input is not an app uid + */ + @FlaggedApi(Flags.FLAG_SDK_SANDBOX_UID_TO_APP_UID_API) + @android.ravenwood.annotation.RavenwoodKeep + public static final int getSdkSandboxUidForAppUid(int uid) { + if (!isApplicationUid(uid)) { + throw new IllegalArgumentException("Input UID is not an app UID"); + } + return uid + (FIRST_SDK_SANDBOX_UID - FIRST_APPLICATION_UID); + } + + /** * Returns whether the current process is a sdk sandbox process. */ @android.ravenwood.annotation.RavenwoodKeep diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java index 3eea94eaf2e6..53af838cb535 100644 --- a/core/java/android/os/UidBatteryConsumer.java +++ b/core/java/android/os/UidBatteryConsumer.java @@ -37,6 +37,7 @@ import java.lang.annotation.RetentionPolicy; * * @hide */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public final class UidBatteryConsumer extends BatteryConsumer { static final int CONSUMER_TYPE_UID = 1; @@ -223,6 +224,7 @@ public final class UidBatteryConsumer extends BatteryConsumer { /** * Builder for UidBatteryConsumer. */ + @android.ravenwood.annotation.RavenwoodKeepWholeClass public static final class Builder extends BaseBuilder<Builder> { private static final String PACKAGE_NAME_UNINITIALIZED = ""; private final BatteryStats.Uid mBatteryStatsUid; diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig index d64614628b61..0db90bff48fd 100644 --- a/core/java/android/os/flags.aconfig +++ b/core/java/android/os/flags.aconfig @@ -98,3 +98,11 @@ flag { description: "Guards StrictMode APIs for detecting restricted network access." bug: "317250784" } + +flag { + name: "message_queue_tail_tracking" + namespace: "system_performance" + description: "track tail of message queue." + bug: "305311707" + is_fixed_read_only: true +} diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java index 2d53341be654..322a8e62dbb3 100644 --- a/core/java/android/os/health/SystemHealthManager.java +++ b/core/java/android/os/health/SystemHealthManager.java @@ -25,8 +25,8 @@ import android.content.Context; import android.os.BatteryStats; import android.os.Build; import android.os.Bundle; -import android.os.Handler; import android.os.IPowerStatsService; +import android.os.OutcomeReceiver; import android.os.PowerMonitor; import android.os.PowerMonitorReadings; import android.os.Process; @@ -39,6 +39,7 @@ import com.android.internal.app.IBatteryStats; import java.util.Arrays; import java.util.Comparator; import java.util.List; +import java.util.concurrent.Executor; import java.util.function.Consumer; /** @@ -161,12 +162,12 @@ public class SystemHealthManager { * (on-device power rail monitor) rails and modeled energy consumers. If ODPM is unsupported * on this device this method delivers an empty list. * - * @param handler optional Handler to deliver the callback. If not supplied, the callback + * @param executor optional Handler to deliver the callback. If not supplied, the callback * may be invoked on an arbitrary thread. * @param onResult callback for the result */ @FlaggedApi("com.android.server.power.optimization.power_monitor_api") - public void getSupportedPowerMonitors(@Nullable Handler handler, + public void getSupportedPowerMonitors(@Nullable Executor executor, @NonNull Consumer<List<PowerMonitor>> onResult) { final List<PowerMonitor> result; synchronized (mPowerMonitorsLock) { @@ -180,15 +181,15 @@ public class SystemHealthManager { } } if (result != null) { - if (handler != null) { - handler.post(() -> onResult.accept(result)); + if (executor != null) { + executor.execute(() -> onResult.accept(result)); } else { onResult.accept(result); } return; } try { - mPowerStats.getSupportedPowerMonitors(new ResultReceiver(handler) { + mPowerStats.getSupportedPowerMonitors(new ResultReceiver(null) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { PowerMonitor[] array = resultData.getParcelableArray( @@ -197,7 +198,11 @@ public class SystemHealthManager { synchronized (mPowerMonitorsLock) { mPowerMonitorsInfo = result; } - onResult.accept(result); + if (executor != null) { + executor.execute(()-> onResult.accept(result)); + } else { + onResult.accept(result); + } } }); } catch (RemoteException e) { @@ -213,17 +218,22 @@ public class SystemHealthManager { * monitors. * * @param powerMonitors power monitors to be retrieved. - * @param handler optional Handler to deliver the callbacks. If not supplied, the callback - * may be invoked on an arbitrary thread. - * @param onSuccess callback for the result - * @param onError callback invoked in case of an error + * @param executor optional Executor to deliver the callbacks. If not supplied, the + * callback may be invoked on an arbitrary thread. + * @param onResult callback for the result */ @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getPowerMonitorReadings(@NonNull List<PowerMonitor> powerMonitors, - @Nullable Handler handler, @NonNull Consumer<PowerMonitorReadings> onSuccess, - @NonNull Consumer<RuntimeException> onError) { + @Nullable Executor executor, + @NonNull OutcomeReceiver<PowerMonitorReadings, RuntimeException> onResult) { if (mPowerStats == null) { - onError.accept(new IllegalArgumentException("Unsupported power monitor")); + IllegalArgumentException error = + new IllegalArgumentException("Unsupported power monitor"); + if (executor != null) { + executor.execute(() -> onResult.onError(error)); + } else { + onResult.onError(error); + } return; } @@ -235,18 +245,31 @@ public class SystemHealthManager { indices[i] = powerMonitorsArray[i].index; } try { - mPowerStats.getPowerMonitorReadings(indices, new ResultReceiver(handler) { + mPowerStats.getPowerMonitorReadings(indices, new ResultReceiver(null) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { if (resultCode == IPowerStatsService.RESULT_SUCCESS) { - onSuccess.accept(new PowerMonitorReadings(powerMonitorsArray, + PowerMonitorReadings result = new PowerMonitorReadings(powerMonitorsArray, resultData.getLongArray(IPowerStatsService.KEY_ENERGY), - resultData.getLongArray(IPowerStatsService.KEY_TIMESTAMPS))); - } else if (resultCode == IPowerStatsService.RESULT_UNSUPPORTED_POWER_MONITOR) { - onError.accept(new IllegalArgumentException("Unsupported power monitor")); + resultData.getLongArray(IPowerStatsService.KEY_TIMESTAMPS)); + if (executor != null) { + executor.execute(() -> onResult.onResult(result)); + } else { + onResult.onResult(result); + } } else { - onError.accept(new IllegalStateException( - "Unrecognized result code " + resultCode)); + RuntimeException error; + if (resultCode == IPowerStatsService.RESULT_UNSUPPORTED_POWER_MONITOR) { + error = new IllegalArgumentException("Unsupported power monitor"); + } else { + error = new IllegalStateException( + "Unrecognized result code " + resultCode); + } + if (executor != null) { + executor.execute(() -> onResult.onError(error)); + } else { + onResult.onError(error); + } } } }); diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl index 3ecf74e75367..54ed73c34830 100644 --- a/core/java/android/os/storage/IStorageManager.aidl +++ b/core/java/android/os/storage/IStorageManager.aidl @@ -134,16 +134,16 @@ interface IStorageManager { @EnforcePermission("MOUNT_UNMOUNT_FILESYSTEMS") void setDebugFlags(int flags, int mask) = 60; @EnforcePermission("STORAGE_INTERNAL") - void createUserStorageKeys(int userId, int serialNumber, boolean ephemeral) = 61; + void createUserStorageKeys(int userId, boolean ephemeral) = 61; @EnforcePermission("STORAGE_INTERNAL") void destroyUserStorageKeys(int userId) = 62; @EnforcePermission("STORAGE_INTERNAL") - void unlockCeStorage(int userId, int serialNumber, in byte[] secret) = 63; + void unlockCeStorage(int userId, in byte[] secret) = 63; @EnforcePermission("STORAGE_INTERNAL") void lockCeStorage(int userId) = 64; boolean isCeStorageUnlocked(int userId) = 65; @EnforcePermission("STORAGE_INTERNAL") - void prepareUserStorage(in String volumeUuid, int userId, int serialNumber, int flags) = 66; + void prepareUserStorage(in String volumeUuid, int userId, int flags) = 66; @EnforcePermission("STORAGE_INTERNAL") void destroyUserStorage(in String volumeUuid, int userId, int flags) = 67; @EnforcePermission("STORAGE_INTERNAL") diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 78a12f75a508..9587db13ea87 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -1602,14 +1602,13 @@ public class StorageManager { * This is only intended to be called by UserManagerService, as part of creating a user. * * @param userId ID of the user - * @param serialNumber serial number of the user * @param ephemeral whether the user is ephemeral * @throws RuntimeException on error. The user's keys already existing is considered an error. * @hide */ - public void createUserStorageKeys(int userId, int serialNumber, boolean ephemeral) { + public void createUserStorageKeys(int userId, boolean ephemeral) { try { - mStorageManager.createUserStorageKeys(userId, serialNumber, ephemeral); + mStorageManager.createUserStorageKeys(userId, ephemeral); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1653,9 +1652,9 @@ public class StorageManager { } /** {@hide} */ - public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) { + public void prepareUserStorage(String volumeUuid, int userId, int flags) { try { - mStorageManager.prepareUserStorage(volumeUuid, userId, serialNumber, flags); + mStorageManager.prepareUserStorage(volumeUuid, userId, flags); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig index fae7cb394906..60143cc79d2d 100644 --- a/core/java/android/permission/flags.aconfig +++ b/core/java/android/permission/flags.aconfig @@ -71,3 +71,10 @@ flag { description: "default retail demo role holder" bug: "274132354" } + +flag { + name: "wallet_role_enabled" + namespace: "wallet_integration" + description: "This flag is used to enabled the Wallet Role for all users on the device" + bug: "283989236" +} diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index e8da0d9341d3..47065e18b74b 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3622,11 +3622,11 @@ public final class Settings { } if (names.length > 0) { for (String name : names) { - String value = mValues.get(name); - if (value != null) { + // mValues can contain "null" values, need to use containsKey. + if (mValues.containsKey(name)) { keyValues.put( name.substring(substringLength), - value); + mValues.get(name)); } } } else { @@ -3700,11 +3700,11 @@ public final class Settings { // Only the flags requested by the caller if (names.length > 0) { for (String name : names) { - String value = flagsToValues.get(name); - if (value != null) { + // flagsToValues can contain "null" values, need to use containsKey. + if (flagsToValues.containsKey(name)) { keyValues.put( name.substring(substringLength), - value); + flagsToValues.get(name)); } } } else { @@ -10158,8 +10158,12 @@ public final class Settings { /** * The default NFC payment component + * + * @deprecated please use {@link android.app.role.RoleManager#getRoleHolders(String)} + * with {@link android.app.role.RoleManager#ROLE_WALLET} parameter. * @hide */ + @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component"; @@ -12176,6 +12180,36 @@ public final class Settings { */ public static final String HIDE_PRIVATESPACE_ENTRY_POINT = "hide_privatespace_entry_point"; + /** @hide */ + public static final int PRIVATE_SPACE_AUTO_LOCK_ON_DEVICE_LOCK = 0; + /** @hide */ + public static final int PRIVATE_SPACE_AUTO_LOCK_AFTER_INACTIVITY = 1; + /** @hide */ + public static final int PRIVATE_SPACE_AUTO_LOCK_NEVER = 2; + + /** + * The different auto lock options for private space. + * + * @hide + */ + @IntDef(prefix = {"PRIVATE_SPACE_AUTO_LOCK_"}, value = { + PRIVATE_SPACE_AUTO_LOCK_ON_DEVICE_LOCK, + PRIVATE_SPACE_AUTO_LOCK_AFTER_INACTIVITY, + PRIVATE_SPACE_AUTO_LOCK_NEVER, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PrivateSpaceAutoLockOption { + } + + + /** + * Store auto lock value for private space. + * The possible values are defined in {@link PrivateSpaceAutoLockOption}. + * + * @hide + */ + public static final String PRIVATE_SPACE_AUTO_LOCK = "private_space_auto_lock"; + /** * These entries are considered common between the personal and the managed profile, * since the managed profile doesn't get to change them. diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 7d9c0a37a13f..2d657c2813a5 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -445,6 +445,19 @@ public class DreamService extends Service implements Window.Callback { } /** + * Retrieves the current {@link android.app.Activity} associated with the dream. + * This method behaves similarly to calling {@link android.app.Activity#getActivity()}. + * + * @return The current activity, or null if the dream is not associated with an activity + * or not started. + * + * @hide + */ + public Activity getActivity() { + return mActivity; + } + + /** * Inflates a layout resource and set it to be the content view for this Dream. * Behaves similarly to {@link android.app.Activity#setContentView(int)}. * diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java index 4c8188801eff..a6d3bb47d9c8 100644 --- a/core/java/android/text/BoringLayout.java +++ b/core/java/android/text/BoringLayout.java @@ -454,7 +454,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback line.set(paint, source, 0, source.length(), Layout.DIR_LEFT_TO_RIGHT, Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null, mEllipsizedStart, mEllipsizedStart + mEllipsizedCount, useFallbackLineSpacing); - mMax = (int) Math.ceil(line.metrics(null, null, false)); + mMax = (int) Math.ceil(line.metrics(null, null, false, null)); TextLine.recycle(line); } @@ -603,7 +603,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback 0 /* ellipsisStart, 0 since text has not been ellipsized at this point */, 0 /* ellipsisEnd, 0 since text has not been ellipsized at this point */, useFallbackLineSpacing); - fm.width = (int) Math.ceil(line.metrics(fm, fm.mDrawingBounds, false)); + fm.width = (int) Math.ceil(line.metrics(fm, fm.mDrawingBounds, false, null)); TextLine.recycle(line); return fm; diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index c9906cc68f3d..eca848ae1ea3 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -18,6 +18,7 @@ package android.text; import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE; import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH; +import static com.android.text.flags.Flags.FLAG_INTER_CHARACTER_JUSTIFICATION; import android.annotation.FlaggedApi; import android.annotation.FloatRange; @@ -50,8 +51,10 @@ import com.android.internal.util.GrowingArrayUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.text.BreakIterator; import java.util.Arrays; import java.util.List; +import java.util.Locale; /** * A base class that manages text layout in visual elements on @@ -669,7 +672,8 @@ public abstract class Layout { int start = previousLineEnd; previousLineEnd = getLineStart(lineNum + 1); final boolean justify = isJustificationRequired(lineNum); - int end = getLineVisibleEnd(lineNum, start, previousLineEnd); + int end = getLineVisibleEnd(lineNum, start, previousLineEnd, + true /* trailingSpaceAtLastLineIsVisible */); paint.setStartHyphenEdit(getStartHyphenEdit(lineNum)); paint.setEndHyphenEdit(getEndHyphenEdit(lineNum)); @@ -1056,7 +1060,7 @@ public abstract class Layout { if (isJustificationRequired(line)) { tl.justify(getJustifyWidth(line)); } - tl.metrics(null, rectF, false); + tl.metrics(null, rectF, false, null); float lineLeft = rectF.left; float lineRight = rectF.right; @@ -1456,7 +1460,7 @@ public abstract class Layout { tl.set(mPaint, mText, start, end, dir, directions, hasTab, tabStops, getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line), isFallbackLineSpacingEnabled()); - float wid = tl.measure(offset - start, trailing, null, null); + float wid = tl.measure(offset - start, trailing, null, null, null); TextLine.recycle(tl); if (clamped && wid > mWidth) { @@ -1792,12 +1796,69 @@ public abstract class Layout { if (isJustificationRequired(line)) { tl.justify(getJustifyWidth(line)); } - final float width = tl.metrics(null, null, mUseBoundsForWidth); + final float width = tl.metrics(null, null, mUseBoundsForWidth, null); TextLine.recycle(tl); return width; } /** + * Returns the number of letter spacing unit in the line. + * + * <p> + * This API returns a number of letters that is a target of letter spacing. The letter spacing + * won't be added to the middle of the characters that are needed to be treated as a single, + * e.g., ligatured or conjunct form. Note that this value is different from the number of] + * grapheme clusters that is calculated by {@link BreakIterator#getCharacterInstance(Locale)}. + * For example, if the "fi" is ligatured, the ligatured form is treated as single uni and letter + * spacing is not added, but it has two separate grapheme cluster. + * + * <p> + * This value is used for calculating the letter spacing amount for the justification because + * the letter spacing is applied between clusters. For example, if extra {@code W} pixels needed + * to be filled by letter spacing, the amount of letter spacing to be applied is + * {@code W}/(letter spacing unit count - 1) px. + * + * @param line the index of the line + * @param includeTrailingWhitespace whether to include trailing whitespace + * @return the number of cluster count in the line. + */ + @IntRange(from = 0) + @FlaggedApi(FLAG_INTER_CHARACTER_JUSTIFICATION) + public int getLineLetterSpacingUnitCount(@IntRange(from = 0) int line, + boolean includeTrailingWhitespace) { + final int start = getLineStart(line); + final int end = includeTrailingWhitespace ? getLineEnd(line) + : getLineVisibleEnd(line, getLineStart(line), getLineStart(line + 1), + false // trailingSpaceAtLastLineIsVisible: Treating trailing whitespaces at + // the last line as a invisible chars for single line justification. + ); + + final Directions directions = getLineDirections(line); + // Returned directions can actually be null + if (directions == null) { + return 0; + } + final int dir = getParagraphDirection(line); + + final TextLine tl = TextLine.obtain(); + final TextPaint paint = mWorkPaint; + paint.set(mPaint); + paint.setStartHyphenEdit(getStartHyphenEdit(line)); + paint.setEndHyphenEdit(getEndHyphenEdit(line)); + tl.set(paint, mText, start, end, dir, directions, + false, null, // tab width is not used for cluster counting. + getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line), + isFallbackLineSpacingEnabled()); + if (mLineInfo == null) { + mLineInfo = new TextLine.LineInfo(); + } + mLineInfo.setClusterCount(0); + tl.metrics(null, null, mUseBoundsForWidth, mLineInfo); + TextLine.recycle(tl); + return mLineInfo.getClusterCount(); + } + + /** * Returns the signed horizontal extent of the specified line, excluding * leading margin. If full is false, excludes trailing whitespace. * @param line the index of the line @@ -1823,7 +1884,7 @@ public abstract class Layout { if (isJustificationRequired(line)) { tl.justify(getJustifyWidth(line)); } - final float width = tl.metrics(null, null, mUseBoundsForWidth); + final float width = tl.metrics(null, null, mUseBoundsForWidth, null); TextLine.recycle(tl); return width; } @@ -2432,14 +2493,21 @@ public abstract class Layout { * is not counted) on the specified line. */ public int getLineVisibleEnd(int line) { - return getLineVisibleEnd(line, getLineStart(line), getLineStart(line+1)); + return getLineVisibleEnd(line, getLineStart(line), getLineStart(line + 1), + true /* trailingSpaceAtLastLineIsVisible */); } - private int getLineVisibleEnd(int line, int start, int end) { + private int getLineVisibleEnd(int line, int start, int end, + boolean trailingSpaceAtLastLineIsVisible) { CharSequence text = mText; char ch; - if (line == getLineCount() - 1) { - return end; + + // Historically, trailing spaces at the last line is counted as visible. However, this + // doesn't work well for justification. + if (trailingSpaceAtLastLineIsVisible) { + if (line == getLineCount() - 1) { + return end; + } } for (; end > start; end--) { @@ -2939,7 +3007,7 @@ public abstract class Layout { tl.set(paint, text, start, end, dir, directions, hasTabs, tabStops, 0 /* ellipsisStart */, 0 /* ellipsisEnd */, false /* use fallback line spacing. unused */); - return margin + Math.abs(tl.metrics(null, null, useBoundsForWidth)); + return margin + Math.abs(tl.metrics(null, null, useBoundsForWidth, null)); } finally { TextLine.recycle(tl); if (mt != null) { @@ -3337,6 +3405,8 @@ public abstract class Layout { private boolean mUseBoundsForWidth; private @Nullable Paint.FontMetrics mMinimumFontMetrics; + private TextLine.LineInfo mLineInfo = null; + /** @hide */ @IntDef(prefix = { "DIR_" }, value = { DIR_LEFT_TO_RIGHT, diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index f9abec04e71d..135935cb0632 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -76,6 +76,21 @@ public class TextLine { private RectF mTmpRectForPaintAPI; private Rect mTmpRectForPrecompute; + // Recycling object for Paint APIs. Do not use outside getRunAdvances method. + private Paint.RunInfo mRunInfo; + + public static final class LineInfo { + private int mClusterCount; + + public int getClusterCount() { + return mClusterCount; + } + + public void setClusterCount(int clusterCount) { + mClusterCount = clusterCount; + } + }; + private boolean mUseFallbackExtent = false; // The start and end of a potentially existing ellipsis on this text line. @@ -270,7 +285,7 @@ public class TextLine { // width. return; } - final float width = Math.abs(measure(end, false, null, null)); + final float width = Math.abs(measure(end, false, null, null, null)); mAddedWidthForJustify = (justifyWidth - width) / spaces; mIsJustifying = true; } @@ -315,10 +330,12 @@ public class TextLine { * @param drawBounds output parameter for drawing bounding box. optional. * @param returnDrawWidth true for returning width of the bounding box, false for returning * total advances. + * @param lineInfo an optional output parameter for filling line information. * @return the signed width of the line */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - public float metrics(FontMetricsInt fmi, @Nullable RectF drawBounds, boolean returnDrawWidth) { + public float metrics(FontMetricsInt fmi, @Nullable RectF drawBounds, boolean returnDrawWidth, + @Nullable LineInfo lineInfo) { if (returnDrawWidth) { if (drawBounds == null) { if (mTmpRectForMeasure == null) { @@ -327,7 +344,7 @@ public class TextLine { drawBounds = mTmpRectForMeasure; } drawBounds.setEmpty(); - float w = measure(mLen, false, fmi, drawBounds); + float w = measure(mLen, false, fmi, drawBounds, lineInfo); float boundsWidth = drawBounds.width(); if (Math.abs(w) > boundsWidth) { return w; @@ -337,7 +354,7 @@ public class TextLine { return Math.signum(w) * boundsWidth; } } else { - return measure(mLen, false, fmi, drawBounds); + return measure(mLen, false, fmi, drawBounds, lineInfo); } } @@ -407,12 +424,13 @@ public class TextLine { * the edge of the preceding run's edge. See example above. * @param fmi receives metrics information about the requested character, can be null * @param drawBounds output parameter for drawing bounding box. optional. + * @param lineInfo an optional output parameter for filling line information. * @return the signed graphical offset from the leading margin to the requested character edge. * The positive value means the offset is right from the leading edge. The negative * value means the offset is left from the leading edge. */ public float measure(@IntRange(from = 0) int offset, boolean trailing, - @NonNull FontMetricsInt fmi, @Nullable RectF drawBounds) { + @NonNull FontMetricsInt fmi, @Nullable RectF drawBounds, @Nullable LineInfo lineInfo) { if (offset > mLen) { throw new IndexOutOfBoundsException( "offset(" + offset + ") should be less than line limit(" + mLen + ")"); @@ -437,16 +455,16 @@ public class TextLine { if (targetIsInThisSegment && sameDirection) { return h + measureRun(segStart, offset, j, runIsRtl, fmi, drawBounds, null, - 0, h); + 0, h, lineInfo); } final float segmentWidth = measureRun(segStart, j, j, runIsRtl, fmi, drawBounds, - null, 0, h); + null, 0, h, lineInfo); h += sameDirection ? segmentWidth : -segmentWidth; if (targetIsInThisSegment) { return h + measureRun(segStart, offset, j, runIsRtl, null, null, null, 0, - h); + h, lineInfo); } if (j != runLimit) { // charAt(j) == TAB_CHAR @@ -537,7 +555,8 @@ public class TextLine { final boolean sameDirection = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl; final float segmentWidth = - measureRun(segStart, j, j, runIsRtl, null, null, advances, segStart, 0); + measureRun(segStart, j, j, runIsRtl, null, null, advances, segStart, 0, + null); final float oldh = h; h += sameDirection ? segmentWidth : -segmentWidth; @@ -578,7 +597,7 @@ public class TextLine { } /** - * @see #measure(int, boolean, FontMetricsInt, RectF) + * @see #measure(int, boolean, FontMetricsInt, RectF, LineInfo) * @return The measure results for all possible offsets */ @VisibleForTesting @@ -610,7 +629,7 @@ public class TextLine { final float previousSegEndHorizontal = measurement[segStart]; final float width = measureRun(segStart, j, j, runIsRtl, fmi, null, measurement, segStart, - 0); + 0, null); horizontal += sameDirection ? width : -width; float currHorizontal = sameDirection ? oldHorizontal : horizontal; @@ -675,14 +694,14 @@ public class TextLine { boolean needWidth) { if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) { - float w = -measureRun(start, limit, limit, runIsRtl, null, null, null, 0, 0); + float w = -measureRun(start, limit, limit, runIsRtl, null, null, null, 0, 0, null); handleRun(start, limit, limit, runIsRtl, c, null, x + w, top, - y, bottom, null, null, false, null, 0); + y, bottom, null, null, false, null, 0, null); return w; } return handleRun(start, limit, limit, runIsRtl, c, null, x, top, - y, bottom, null, null, needWidth, null, 0); + y, bottom, null, null, needWidth, null, 0, null); } /** @@ -698,19 +717,20 @@ public class TextLine { * @param advances receives the advance information about the requested run, can be null. * @param advancesIndex the start index to fill in the advance information. * @param x horizontal offset of the run. + * @param lineInfo an optional output parameter for filling line information. * @return the signed width from the start of the run to the leading edge * of the character at offset, based on the run (not paragraph) direction */ private float measureRun(int start, int offset, int limit, boolean runIsRtl, @Nullable FontMetricsInt fmi, @Nullable RectF drawBounds, @Nullable float[] advances, - int advancesIndex, float x) { + int advancesIndex, float x, @Nullable LineInfo lineInfo) { if (drawBounds != null && (mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) { - float w = -measureRun(start, offset, limit, runIsRtl, null, null, null, 0, 0); + float w = -measureRun(start, offset, limit, runIsRtl, null, null, null, 0, 0, null); return handleRun(start, offset, limit, runIsRtl, null, null, x + w, 0, 0, 0, fmi, - drawBounds, true, advances, advancesIndex); + drawBounds, true, advances, advancesIndex, lineInfo); } return handleRun(start, offset, limit, runIsRtl, null, null, x, 0, 0, 0, fmi, drawBounds, - true, advances, advancesIndex); + true, advances, advancesIndex, lineInfo); } /** @@ -729,14 +749,14 @@ public class TextLine { int limit, boolean runIsRtl, float x, boolean needWidth) { if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) { - float w = -measureRun(start, limit, limit, runIsRtl, null, null, null, 0, 0); + float w = -measureRun(start, limit, limit, runIsRtl, null, null, null, 0, 0, null); handleRun(start, limit, limit, runIsRtl, null, consumer, x + w, 0, 0, 0, null, null, - false, null, 0); + false, null, 0, null); return w; } return handleRun(start, limit, limit, runIsRtl, null, consumer, x, 0, 0, 0, null, null, - needWidth, null, 0); + needWidth, null, 0, null); } @@ -1077,16 +1097,35 @@ public class TextLine { private float getRunAdvance(TextPaint wp, int start, int end, int contextStart, int contextEnd, boolean runIsRtl, int offset, @Nullable float[] advances, int advancesIndex, - RectF drawingBounds) { + RectF drawingBounds, @Nullable LineInfo lineInfo) { + if (lineInfo != null) { + if (mRunInfo == null) { + mRunInfo = new Paint.RunInfo(); + } + mRunInfo.setClusterCount(0); + } else { + mRunInfo = null; + } if (mCharsValid) { - return wp.getRunCharacterAdvance(mChars, start, end, contextStart, contextEnd, - runIsRtl, offset, advances, advancesIndex, drawingBounds); + float r = wp.getRunCharacterAdvance(mChars, start, end, contextStart, contextEnd, + runIsRtl, offset, advances, advancesIndex, drawingBounds, mRunInfo); + if (lineInfo != null) { + lineInfo.setClusterCount(lineInfo.getClusterCount() + mRunInfo.getClusterCount()); + } + return r; } else { final int delta = mStart; - if (mComputed == null || advances != null) { - return wp.getRunCharacterAdvance(mText, delta + start, delta + end, + // TODO: Add cluster information to the PrecomputedText for better performance of + // justification. + if (mComputed == null || advances != null || lineInfo != null) { + float r = wp.getRunCharacterAdvance(mText, delta + start, delta + end, delta + contextStart, delta + contextEnd, runIsRtl, - delta + offset, advances, advancesIndex, drawingBounds); + delta + offset, advances, advancesIndex, drawingBounds, mRunInfo); + if (lineInfo != null) { + lineInfo.setClusterCount( + lineInfo.getClusterCount() + mRunInfo.getClusterCount()); + } + return r; } else { if (drawingBounds != null) { if (mTmpRectForPrecompute == null) { @@ -1120,6 +1159,7 @@ public class TextLine { * @param decorations the list of locations and paremeters for drawing decorations * @param advances receives the advance information about the requested run, can be null. * @param advancesIndex the start index to fill in the advance information. + * @param lineInfo an optional output parameter for filling line information. * @return the signed width of the run based on the run direction; only * valid if needWidth is true */ @@ -1128,7 +1168,7 @@ public class TextLine { Canvas c, TextShaper.GlyphsConsumer consumer, float x, int top, int y, int bottom, FontMetricsInt fmi, RectF drawBounds, boolean needWidth, int offset, @Nullable ArrayList<DecorationInfo> decorations, - @Nullable float[] advances, int advancesIndex) { + @Nullable float[] advances, int advancesIndex, @Nullable LineInfo lineInfo) { if (mIsJustifying) { wp.setWordSpacing(mAddedWidthForJustify); @@ -1155,7 +1195,8 @@ public class TextLine { mTmpRectForPaintAPI = new RectF(); } totalWidth = getRunAdvance(wp, start, end, contextStart, contextEnd, runIsRtl, offset, - advances, advancesIndex, drawBounds == null ? null : mTmpRectForPaintAPI); + advances, advancesIndex, drawBounds == null ? null : mTmpRectForPaintAPI, + lineInfo); if (drawBounds != null) { if (runIsRtl) { mTmpRectForPaintAPI.offset(x - totalWidth, 0); @@ -1206,9 +1247,9 @@ public class TextLine { final int decorationStart = Math.max(info.start, start); final int decorationEnd = Math.min(info.end, offset); float decorationStartAdvance = getRunAdvance(wp, start, end, contextStart, - contextEnd, runIsRtl, decorationStart, null, 0, null); + contextEnd, runIsRtl, decorationStart, null, 0, null, null); float decorationEndAdvance = getRunAdvance(wp, start, end, contextStart, - contextEnd, runIsRtl, decorationEnd, null, 0, null); + contextEnd, runIsRtl, decorationEnd, null, 0, null, null); final float decorationXLeft, decorationXRight; if (runIsRtl) { decorationXLeft = rightX - decorationEndAdvance; @@ -1377,6 +1418,7 @@ public class TextLine { * @param needWidth true if the width is required * @param advances receives the advance information about the requested run, can be null. * @param advancesIndex the start index to fill in the advance information. + * @param lineInfo an optional output parameter for filling line information. * @return the signed width of the run based on the run direction; only * valid if needWidth is true */ @@ -1384,7 +1426,7 @@ public class TextLine { int limit, boolean runIsRtl, Canvas c, TextShaper.GlyphsConsumer consumer, float x, int top, int y, int bottom, FontMetricsInt fmi, RectF drawBounds, boolean needWidth, - @Nullable float[] advances, int advancesIndex) { + @Nullable float[] advances, int advancesIndex, @Nullable LineInfo lineInfo) { if (measureLimit < start || measureLimit > limit) { throw new IndexOutOfBoundsException("measureLimit (" + measureLimit + ") is out of " @@ -1431,7 +1473,7 @@ public class TextLine { wp.setEndHyphenEdit(adjustEndHyphenEdit(limit, wp.getEndHyphenEdit())); return handleText(wp, start, limit, start, limit, runIsRtl, c, consumer, x, top, y, bottom, fmi, drawBounds, needWidth, measureLimit, null, advances, - advancesIndex); + advancesIndex, lineInfo); } // Shaping needs to take into account context up to metric boundaries, @@ -1523,7 +1565,7 @@ public class TextLine { consumer, x, top, y, bottom, fmi, drawBounds, needWidth || activeEnd < measureLimit, Math.min(activeEnd, mlimit), mDecorations, - advances, advancesIndex + activeStart - start); + advances, advancesIndex + activeStart - start, lineInfo); activeStart = j; activePaint.set(wp); @@ -1551,7 +1593,7 @@ public class TextLine { x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, consumer, x, top, y, bottom, fmi, drawBounds, needWidth || activeEnd < measureLimit, Math.min(activeEnd, mlimit), mDecorations, - advances, advancesIndex + activeStart - start); + advances, advancesIndex + activeStart - start, lineInfo); } return x - originalX; diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 9bf43a390d70..1d81be17f580 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -370,4 +370,14 @@ interface IWindowSession { boolean transferEmbeddedTouchFocusToHost(IWindow embeddedWindow); boolean transferHostTouchGestureToEmbedded(IWindow hostWindow, IBinder transferTouchToken); + + /** + * Moves the focus to the adjacent window if there is one in the given direction. This can only + * move the focus to the window in the same leaf task. + * + * @param fromWindow The calling window that the focus is moved from. + * @param direction The {@link android.view.View.FocusDirection} that the new focus should go. + * @return {@code true} if the focus changes. Otherwise, {@code false}. + */ + boolean moveFocusToAdjacentWindow(IWindow fromWindow, int direction); } diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index b957b31a5bb7..674f22c4166e 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -28,6 +28,7 @@ import static android.view.SurfaceControlProto.NAME; import android.Manifest; import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.IntRange; @@ -71,6 +72,7 @@ import android.view.Surface.OutOfResourcesException; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; import com.android.internal.util.VirtualRefBasePtr; +import com.android.window.flags.Flags; import dalvik.system.CloseGuard; @@ -277,6 +279,8 @@ public final class SurfaceControl implements Parcelable { private static native int nativeGetLayerId(long nativeObject); private static native void nativeAddTransactionCommittedListener(long nativeObject, TransactionCommittedListener listener); + private static native void nativeAddTransactionCompletedListener(long nativeObject, + Consumer<TransactionStats> listener); private static native void nativeSanitize(long transactionObject, int pid, int uid); private static native void nativeSetDestinationFrame(long transactionObj, long nativeObject, int l, int t, int r, int b); @@ -290,6 +294,10 @@ public final class SurfaceControl implements Parcelable { private static native void nativeClearTrustedPresentationCallback(long transactionObj, long nativeObject); private static native StalledTransactionInfo nativeGetStalledTransactionInfo(int pid); + private static native void nativeSetDesiredPresentTime(long transactionObj, + long desiredPresentTime); + private static native void nativeSetFrameTimeline(long transactionObj, + long vsyncId); /** * Transforms that can be applied to buffers as they are displayed to a window. @@ -2550,6 +2558,50 @@ public final class SurfaceControl implements Parcelable { } /** + * Transaction stats given to the listener registered in + * {@link SurfaceControl.Transaction#addTransactionCompletedListener} + */ + @FlaggedApi(Flags.FLAG_SDK_DESIRED_PRESENT_TIME) + public static final class TransactionStats { + private long mLatchTime; + private SyncFence mSyncFence; + + // called from native + private TransactionStats(long latchTime, long presentFencePtr) { + mLatchTime = latchTime; + mSyncFence = new SyncFence(presentFencePtr); + } + + /** + * Close the TransactionStats. Called by the framework when the listener returns. + * @hide + */ + public void close() { + mSyncFence.close(); + } + + /** + * Returns the timestamp of when the frame was latched by the framework and queued for + * presentation. + */ + @FlaggedApi(Flags.FLAG_SDK_DESIRED_PRESENT_TIME) + public long getLatchTime() { + return mLatchTime; + } + + /** + * Returns a new SyncFence that signals when the transaction has been presented. + * The caller takes ownership of the fence and is responsible for closing + * it by calling {@link SyncFence#close}. + * If a device does not support present fences, an empty fence will be returned. + */ + @FlaggedApi(Flags.FLAG_SDK_DESIRED_PRESENT_TIME) + public @NonNull SyncFence getPresentFence() { + return new SyncFence(mSyncFence); + } + }; + + /** * Threshold values that are sent with * {@link Transaction#setTrustedPresentationCallback(SurfaceControl, * TrustedPresentationThresholds, Executor, Consumer)} @@ -4185,12 +4237,35 @@ public final class SurfaceControl implements Parcelable { } /** - * Sets the frame timeline vsync id received from choreographer - * {@link Choreographer#getVsyncId()} that corresponds to the transaction submitted on that - * surface control. + * Sets the frame timeline to use in SurfaceFlinger. + * + * A frame timeline should be chosen based on the frame deadline the application + * can meet when rendering the frame and the application's desired presentation time. + * By setting a frame timeline, SurfaceFlinger tries to present the frame at the + * corresponding expected presentation time. + * + * To receive frame timelines, a callback must be posted to Choreographer using + * {@link Choreographer#postVsyncCallback} The vsyncId can then be extracted from the + * {@link Choreographer.FrameTimeline#getVsyncId}. + * + * @param vsyncId The vsync ID received from Choreographer, setting the frame's + * presentation target to the corresponding expected presentation time + * and deadline from the frame to be rendered. A stale or invalid value + * will be ignored. * - * @hide */ + @FlaggedApi(Flags.FLAG_SDK_DESIRED_PRESENT_TIME) + @NonNull + public Transaction setFrameTimeline(long vsyncId) { + if (!Flags.sdkDesiredPresentTime()) { + Log.w(TAG, "addTransactionCompletedListener was called but flag is disabled"); + return this; + } + nativeSetFrameTimelineVsync(mNativeObject, vsyncId); + return this; + } + + /** @hide */ @NonNull public Transaction setFrameTimelineVsync(long frameTimelineVsyncId) { nativeSetFrameTimelineVsync(mNativeObject, frameTimelineVsyncId); @@ -4207,6 +4282,9 @@ public final class SurfaceControl implements Parcelable { * to avoid dropping frames (overwriting transactions), and unable to use timestamps (Which * provide a more efficient solution), then this method provides a method to pace your * transaction application. + * The listener is invoked once the transaction is applied, and never again. Multiple + * listeners can be added to the same transaction, however the order the listeners will + * be called is not guaranteed. * * @param executor The executor that the callback should be invoked on. * @param listener The callback that will be invoked when the transaction has been @@ -4223,6 +4301,33 @@ public final class SurfaceControl implements Parcelable { } /** + * Request to add a TransactionCompletedListener. + * + * The listener is invoked when transaction is presented, and never again. Multiple + * listeners can be added to the same transaction, however the order the listeners will + * be called is not guaranteed. + * + * @param executor The executor that the callback should be invoked on. + * @param listener The callback that will be invoked when the transaction has been + * completed. + */ + @FlaggedApi(Flags.FLAG_SDK_DESIRED_PRESENT_TIME) + @NonNull + public Transaction addTransactionCompletedListener( + @NonNull @CallbackExecutor Executor executor, + @NonNull Consumer<TransactionStats> listener) { + + if (!Flags.sdkDesiredPresentTime()) { + Log.w(TAG, "addTransactionCompletedListener was called but flag is disabled"); + return this; + } + Consumer<TransactionStats> listenerInner = stats -> executor.execute( + () -> listener.andThen(TransactionStats::close).accept(stats)); + nativeAddTransactionCompletedListener(mNativeObject, listenerInner); + return this; + } + + /** * Sets a callback to receive feedback about the presentation of a {@link SurfaceControl}. * When the {@link SurfaceControl} is presented according to the passed in * {@link TrustedPresentationThresholds}, it is said to "enter the state", and receives the @@ -4321,6 +4426,30 @@ public final class SurfaceControl implements Parcelable { } /** + * Specifies a desiredPresentTime for the transaction. The framework will try to present + * the transaction at or after the time specified. + * + * Transactions will not be presented until all of their acquire fences have signaled even + * if the app requests an earlier present time. + * + * If an earlier transaction has a desired present time of x, and a later transaction has + * a desired present time that is before x, the later transaction will not preempt the + * earlier transaction. + * + * @param desiredPresentTime The desired time (in CLOCK_MONOTONIC) for the transaction. + * @return This transaction + */ + @FlaggedApi(Flags.FLAG_SDK_DESIRED_PRESENT_TIME) + @NonNull + public Transaction setDesiredPresentTime(long desiredPresentTime) { + if (!Flags.sdkDesiredPresentTime()) { + Log.w(TAG, "addTransactionCompletedListener was called but flag is disabled"); + return this; + } + nativeSetDesiredPresentTime(mNativeObject, desiredPresentTime); + return this; + } + /** * Writes the transaction to parcel, clearing the transaction as if it had been applied so * it can be used to store future transactions. It's the responsibility of the parcel * reader to apply the original transaction. diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 2b99e1e9f79b..257ecc565c87 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -33170,6 +33170,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public void setFrameContentVelocity(float pixelsPerSecond) { if (viewVelocityApi()) { mFrameContentVelocity = Math.abs(pixelsPerSecond); + + if (sToolkitMetricsForFrameRateDecisionFlagValue) { + Trace.setCounter("Set frame velocity", (long) mFrameContentVelocity); + } } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 350876c828b7..c66f3c8032fd 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -16,6 +16,7 @@ package android.view; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.content.pm.ActivityInfo.OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS; import static android.graphics.HardwareRenderer.SYNC_CONTEXT_IS_STOPPED; import static android.graphics.HardwareRenderer.SYNC_LOST_SURFACE_REWARD_IF_FOUND; @@ -7236,7 +7237,7 @@ public final class ViewRootImpl implements ViewParent, } private boolean performFocusNavigation(KeyEvent event) { - int direction = 0; + @FocusDirection int direction = 0; switch (event.getKeyCode()) { case KeyEvent.KEYCODE_DPAD_LEFT: if (event.hasNoModifiers()) { @@ -7288,6 +7289,8 @@ public final class ViewRootImpl implements ViewParent, isFastScrolling)); return true; } + } else if (moveFocusToAdjacentWindow(direction)) { + return true; } // Give the focused view a last chance to handle the dpad key. @@ -7297,12 +7300,26 @@ public final class ViewRootImpl implements ViewParent, } else { if (mView.restoreDefaultFocus()) { return true; + } else if (moveFocusToAdjacentWindow(direction)) { + return true; } } } return false; } + private boolean moveFocusToAdjacentWindow(@FocusDirection int direction) { + if (getConfiguration().windowConfiguration.getWindowingMode() + != WINDOWING_MODE_MULTI_WINDOW) { + return false; + } + try { + return mWindowSession.moveFocusToAdjacentWindow(mWindow, direction); + } catch (RemoteException e) { + return false; + } + } + private boolean performKeyboardGroupNavigation(int direction) { final View focused = mView.findFocus(); if (focused == null && mView.restoreDefaultFocus()) { diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index d6ac56239aed..b95e4595d6b9 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -30,6 +30,7 @@ import android.os.RemoteCallback; import android.os.RemoteException; import android.util.Log; import android.util.MergedConfiguration; +import android.view.View.FocusDirection; import android.view.WindowInsets.Type.InsetsType; import android.window.ClientWindowFrames; import android.window.OnBackInvokedCallbackInfo; @@ -665,6 +666,13 @@ public class WindowlessWindowManager implements IWindowSession { return false; } + @Override + public boolean moveFocusToAdjacentWindow(IWindow fromWindow, @FocusDirection int direction) { + Log.e(TAG, "Received request to moveFocusToAdjacentWindow on" + + " WindowlessWindowManager. We shouldn't get here!"); + return false; + } + void setParentInterface(@Nullable ISurfaceControlViewHostParent parentInterface) { IBinder oldInterface = mParentInterface == null ? null : mParentInterface.asBinder(); IBinder newInterface = parentInterface == null ? null : parentInterface.asBinder(); diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig index 48f8f1bbe367..c7355c144c5f 100644 --- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig +++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig @@ -99,3 +99,10 @@ flag { description: "Feature flag for system pinch zoom gesture detector and related opt-out apis" bug: "283323770" } + +flag { + name: "support_system_pinch_zoom_opt_out_apis" + namespace: "accessibility" + description: "Feature flag for declaring system pinch zoom opt-out apis" + bug: "315089687" +} diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java index 70d8abe1dc27..57a3b765641d 100644 --- a/core/java/android/view/animation/AnimationUtils.java +++ b/core/java/android/view/animation/AnimationUtils.java @@ -109,6 +109,40 @@ public class AnimationUtils { } /** + * Locks AnimationUtils{@link #currentAnimationTimeMillis()} to a fixed value for the current + * thread. This is used by {@link android.view.Choreographer} to ensure that all accesses + * during a vsync update are synchronized to the timestamp of the vsync. + * + * It is also exposed to tests to allow for rapid, flake-free headless testing. + * + * Must be followed by a call to {@link #unlockAnimationClock()} to allow time to + * progress. Failing to do this will result in stuck animations, scrolls, and flings. + * + * Note that time is not allowed to "rewind" and must perpetually flow forward. So the + * lock may fail if the time is in the past from a previously returned value, however + * time will be frozen for the duration of the lock. The clock is a thread-local, so + * ensure that {@link #lockAnimationClock(long)}, {@link #unlockAnimationClock()}, and + * {@link #currentAnimationTimeMillis()} are all called on the same thread. + * + * This is also not reference counted in any way. Any call to {@link #unlockAnimationClock()} + * will unlock the clock for everyone on the same thread. It is therefore recommended + * for tests to use their own thread to ensure that there is no collision with any existing + * {@link android.view.Choreographer} instance. + * + * Have to add the method back because of b/307888459. + * Remove this method once the lockAnimationClock(long, long) change + * is landed to aosp/android14-tests-dev branch. + * + * @hide + */ + @TestApi + public static void lockAnimationClock(long vsyncMillis) { + AnimationState state = sAnimationState.get(); + state.animationClockLocked = true; + state.currentVsyncTimeMillis = vsyncMillis; + } + + /** * Frees the time lock set in place by {@link #lockAnimationClock(long)}. Must be called * to allow the animation clock to self-update. * diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java index ef5004536354..1d2f65353a65 100644 --- a/core/java/android/view/textclassifier/TextClassifier.java +++ b/core/java/android/view/textclassifier/TextClassifier.java @@ -16,6 +16,9 @@ package android.view.textclassifier; +import static android.service.notification.Flags.FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS; + +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; @@ -108,6 +111,9 @@ public interface TextClassifier { String TYPE_DATE_TIME = "datetime"; /** Flight number in IATA format. */ String TYPE_FLIGHT_NUMBER = "flight"; + /** One-time login codes */ + @FlaggedApi(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS) + String TYPE_OTP_CODE = "otp_code"; /** * Word that users may be interested to look up for meaning. * @hide @@ -126,7 +132,8 @@ public interface TextClassifier { TYPE_DATE, TYPE_DATE_TIME, TYPE_FLIGHT_NUMBER, - TYPE_DICTIONARY + TYPE_DICTIONARY, + TYPE_OTP_CODE }) @interface EntityType {} diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig index 56df49370379..3794c5d647a4 100644 --- a/core/java/android/window/flags/window_surfaces.aconfig +++ b/core/java/android/window/flags/window_surfaces.aconfig @@ -63,4 +63,12 @@ flag { description: "Enable trustedPresentationListener on windows public API" is_fixed_read_only: true bug: "278027319" -}
\ No newline at end of file +} + +flag { + namespace: "window_surfaces" + name: "sdk_desired_present_time" + description: "Feature flag for the new SDK API to set desired present time" + is_fixed_read_only: true + bug: "295038072" +} diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig index f2bce9c44001..bb16ad2cb8de 100644 --- a/core/java/android/window/flags/windowing_frontend.aconfig +++ b/core/java/android/window/flags/windowing_frontend.aconfig @@ -72,7 +72,7 @@ flag { name: "predictive_back_system_animations" namespace: "systemui" description: "Predictive back for system animations" - bug: "309545085" + bug: "319421778" is_fixed_read_only: true } diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java index 1330e1681dc4..7a79e0f7cfea 100644 --- a/core/java/com/android/internal/os/BatteryStatsHistory.java +++ b/core/java/com/android/internal/os/BatteryStatsHistory.java @@ -977,11 +977,16 @@ public class BatteryStatsHistory { /** * @return true if there is more than 100MB free disk space left. */ + @android.ravenwood.annotation.RavenwoodReplace private boolean hasFreeDiskSpace() { final StatFs stats = new StatFs(mHistoryDir.getAbsolutePath()); return stats.getAvailableBytes() > MIN_FREE_SPACE; } + private boolean hasFreeDiskSpace$ravenwood() { + return true; + } + @VisibleForTesting public List<String> getFilesNames() { List<String> names = new ArrayList<>(); diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java index 2298cbde10d1..ab982f5b67cf 100644 --- a/core/java/com/android/internal/os/PowerProfile.java +++ b/core/java/com/android/internal/os/PowerProfile.java @@ -50,6 +50,7 @@ import java.util.HashMap; * Customize the XML file for different devices. * [hidden] */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class PowerProfile { public static final String TAG = "PowerProfile"; @@ -321,6 +322,13 @@ public class PowerProfile { private int mCpuPowerBracketCount; @VisibleForTesting + public PowerProfile() { + synchronized (sLock) { + initLocked(); + } + } + + @VisibleForTesting @UnsupportedAppUsage public PowerProfile(Context context) { this(context, false); @@ -358,6 +366,10 @@ public class PowerProfile { if (sPowerItemMap.size() == 0 && sPowerArrayMap.size() == 0) { readPowerValuesFromXml(context, xmlId); } + initLocked(); + } + + private void initLocked() { initCpuClusters(); initCpuScalingPolicies(); initCpuPowerBrackets(); diff --git a/core/java/com/android/internal/power/ModemPowerProfile.java b/core/java/com/android/internal/power/ModemPowerProfile.java index a555ae38736e..b15c10e6ba20 100644 --- a/core/java/com/android/internal/power/ModemPowerProfile.java +++ b/core/java/com/android/internal/power/ModemPowerProfile.java @@ -39,6 +39,7 @@ import java.util.Arrays; /** * ModemPowerProfile for handling the modem element in the power_profile.xml */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class ModemPowerProfile { private static final String TAG = "ModemPowerProfile"; diff --git a/core/jni/android_hardware_SyncFence.cpp b/core/jni/android_hardware_SyncFence.cpp index b99665346f25..6e94616db825 100644 --- a/core/jni/android_hardware_SyncFence.cpp +++ b/core/jni/android_hardware_SyncFence.cpp @@ -66,6 +66,10 @@ static jlong SyncFence_getSignalTime(JNIEnv* env, jobject, jlong jPtr) { return fromJlong<Fence>(jPtr)->getSignalTime(); } +static void SyncFence_incRef(JNIEnv*, jobject, jlong jPtr) { + fromJlong<Fence>(jPtr)->incStrong((void*)SyncFence_incRef); +} + // ---------------------------------------------------------------------------- // JNI Glue // ---------------------------------------------------------------------------- @@ -80,6 +84,7 @@ static const JNINativeMethod gMethods[] = { { "nGetFd", "(J)I", (void*) SyncFence_getFd }, { "nWait", "(JJ)Z", (void*) SyncFence_wait }, { "nGetSignalTime", "(J)J", (void*) SyncFence_getSignalTime }, + { "nIncRef", "(J)V", (void*) SyncFence_incRef }, }; // clang-format on diff --git a/core/jni/android_os_VintfObject.cpp b/core/jni/android_os_VintfObject.cpp index b6517117ca62..a5b2f65eafc7 100644 --- a/core/jni/android_os_VintfObject.cpp +++ b/core/jni/android_os_VintfObject.cpp @@ -96,8 +96,11 @@ static jobjectArray android_os_VintfObject_report(JNIEnv* env, jclass) static jint android_os_VintfObject_verifyBuildAtBoot(JNIEnv* env, jclass) { std::string error; + // Use temporary VintfObject, not the shared instance, to release memory + // after check. int32_t status = - VintfObject::GetInstance() + VintfObject::Builder() + .build() ->checkCompatibility(&error, ENABLE_ALL_CHECKS.disableAvb().disableKernel()); if (status) LOG(WARNING) << "VintfObject.verifyBuildAtBoot() returns " << status << ": " << error; diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index db42246ca76c..55326da2c7df 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -231,6 +231,16 @@ static struct { static struct { jclass clazz; + jmethodID accept; +} gConsumerClassInfo; + +static struct { + jclass clazz; + jmethodID ctor; +} gTransactionStatsClassInfo; + +static struct { + jclass clazz; jmethodID ctor; jfieldID format; jfieldID alphaInterpretation; @@ -317,6 +327,52 @@ private: } }; +class TransactionCompletedListenerWrapper { +public: + explicit TransactionCompletedListenerWrapper(JNIEnv* env, jobject object) { + env->GetJavaVM(&mVm); + mTransactionCompletedListenerObject = env->NewGlobalRef(object); + LOG_ALWAYS_FATAL_IF(!mTransactionCompletedListenerObject, "Failed to make global ref"); + } + + ~TransactionCompletedListenerWrapper() { + getenv()->DeleteGlobalRef(mTransactionCompletedListenerObject); + } + + void callback(nsecs_t latchTime, const sp<Fence>& presentFence, + const std::vector<SurfaceControlStats>& /*stats*/) { + JNIEnv* env = getenv(); + // Adding a strong reference for java SyncFence + presentFence->incStrong(0); + + jobject stats = + env->NewObject(gTransactionStatsClassInfo.clazz, gTransactionStatsClassInfo.ctor, + latchTime, presentFence.get()); + env->CallVoidMethod(mTransactionCompletedListenerObject, gConsumerClassInfo.accept, stats); + env->DeleteLocalRef(stats); + DieIfException(env, "Uncaught exception in TransactionCompletedListener."); + } + + static void transactionCallbackThunk(void* context, nsecs_t latchTime, + const sp<Fence>& presentFence, + const std::vector<SurfaceControlStats>& stats) { + TransactionCompletedListenerWrapper* listener = + reinterpret_cast<TransactionCompletedListenerWrapper*>(context); + listener->callback(latchTime, presentFence, stats); + delete listener; + } + +private: + jobject mTransactionCompletedListenerObject; + JavaVM* mVm; + + JNIEnv* getenv() { + JNIEnv* env; + mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6); + return env; + } +}; + class WindowInfosReportedListenerWrapper : public gui::BnWindowInfosReportedListener { public: explicit WindowInfosReportedListenerWrapper(JNIEnv* env, jobject listener) { @@ -1879,10 +1935,16 @@ static void nativeSetFrameTimelineVsync(JNIEnv* env, jclass clazz, jlong transac FrameTimelineInfo ftInfo; ftInfo.vsyncId = frameTimelineVsyncId; - ftInfo.inputEventId = android::os::IInputConstants::INVALID_INPUT_EVENT_ID; transaction->setFrameTimelineInfo(ftInfo); } +static void nativeSetDesiredPresentTime(JNIEnv* env, jclass clazz, jlong transactionObj, + jlong desiredPresentTime) { + auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); + + transaction->setDesiredPresentTime(desiredPresentTime); +} + static void nativeAddTransactionCommittedListener(JNIEnv* env, jclass clazz, jlong transactionObj, jobject transactionCommittedListenerObject) { auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); @@ -1894,6 +1956,17 @@ static void nativeAddTransactionCommittedListener(JNIEnv* env, jclass clazz, jlo context); } +static void nativeAddTransactionCompletedListener(JNIEnv* env, jclass clazz, jlong transactionObj, + jobject transactionCompletedListenerObject) { + auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); + + void* context = + new TransactionCompletedListenerWrapper(env, transactionCompletedListenerObject); + transaction->addTransactionCompletedCallback(TransactionCompletedListenerWrapper:: + transactionCallbackThunk, + context); +} + static void nativeSetTrustedPresentationCallback(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject, jlong trustedPresentationCallbackObject, @@ -2318,6 +2391,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSurfaceFlushJankData }, {"nativeAddTransactionCommittedListener", "(JLandroid/view/SurfaceControl$TransactionCommittedListener;)V", (void*) nativeAddTransactionCommittedListener }, + {"nativeAddTransactionCompletedListener", "(JLjava/util/function/Consumer;)V", + (void*) nativeAddTransactionCompletedListener }, {"nativeSetTrustedPresentationCallback", "(JJJLandroid/view/SurfaceControl$TrustedPresentationThresholds;)V", (void*) nativeSetTrustedPresentationCallback }, {"nativeClearTrustedPresentationCallback", "(JJ)V", @@ -2337,6 +2412,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = { {"getNativeTrustedPresentationCallbackFinalizer", "()J", (void*)getNativeTrustedPresentationCallbackFinalizer }, {"nativeGetStalledTransactionInfo", "(I)Landroid/gui/StalledTransactionInfo;", (void*) nativeGetStalledTransactionInfo }, + {"nativeSetDesiredPresentTime", "(JJ)V", + (void*) nativeSetDesiredPresentTime }, // clang-format on }; @@ -2539,6 +2616,16 @@ int register_android_view_SurfaceControl(JNIEnv* env) gTransactionCommittedListenerClassInfo.onTransactionCommitted = GetMethodIDOrDie(env, transactionCommittedListenerClazz, "onTransactionCommitted", "()V"); + jclass consumerClazz = FindClassOrDie(env, "java/util/function/Consumer"); + gConsumerClassInfo.clazz = MakeGlobalRefOrDie(env, consumerClazz); + gConsumerClassInfo.accept = + GetMethodIDOrDie(env, consumerClazz, "accept", "(Ljava/lang/Object;)V"); + + jclass transactionStatsClazz = + FindClassOrDie(env, "android/view/SurfaceControl$TransactionStats"); + gTransactionStatsClassInfo.clazz = MakeGlobalRefOrDie(env, transactionStatsClazz); + gTransactionStatsClassInfo.ctor = + GetMethodIDOrDie(env, gTransactionStatsClassInfo.clazz, "<init>", "(JJ)V"); jclass displayDecorationSupportClazz = FindClassOrDie(env, "android/hardware/graphics/common/DisplayDecorationSupport"); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index ef6caefd3daf..5f3f6419418a 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -5214,6 +5214,14 @@ <permission android:name="android.permission.BIND_REMOTE_DISPLAY" android:protectionLevel="signature" /> + <!-- Must be required by a android.media.tv.ad.TvAdService to ensure that only the system can + bind to it. + <p>Protection level: signature|privileged + @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") + --> + <permission android:name="android.permission.BIND_TV_AD_SERVICE" + android:protectionLevel="signature|privileged" /> + <!-- Must be required by a {@link android.media.tv.TvInputService} to ensure that only the system can bind to it. <p>Protection level: signature|privileged diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 806be9471ae5..5be29a6d68b8 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2349,6 +2349,8 @@ <string name="config_defaultCallRedirection" translatable="false"></string> <!-- The name of the package that will hold the call screening role by default. --> <string name="config_defaultCallScreening" translatable="false"></string> + <!-- The name of the package that will hold the wallet role by default. --> + <string name="config_defaultWallet" translatable="false" /> <!-- The name of the package that will hold the system gallery role. --> <string name="config_systemGallery" translatable="false">com.android.gallery3d</string> <!-- The names of the packages that will hold the automotive projection role. --> diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml index 17bb86a7c423..53b473e0ac1f 100644 --- a/core/res/res/values/public-staging.xml +++ b/core/res/res/values/public-staging.xml @@ -130,6 +130,8 @@ <staging-public-group type="string" first-id="0x01ba0000"> <!-- @hide @SystemApi @FlaggedApi("android.permission.flags.retail_demo_role_enabled") --> <public name="config_defaultRetailDemo" /> + <!-- @hide @SystemApi @FlaggedApi("android.permission.flags.wallet_role_enabled") --> + <public name="config_defaultWallet" /> </staging-public-group> <staging-public-group type="dimen" first-id="0x01b90000"> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 542e9d6f936f..f2858066b55b 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1825,7 +1825,7 @@ <!-- Message shown during fingerprint acquisision when the fingerprint cannot be recognized --> <string name="fingerprint_acquired_partial">Press firmly on the sensor</string> <!-- Message shown during fingerprint acquisision when the fingerprint cannot be recognized --> - <string name="fingerprint_acquired_insufficient">Can\u2019t recognize fingerprint. Try again.</string> + <string name="fingerprint_acquired_insufficient">Fingerprint not recognized. Try again.</string> <!-- Message shown during fingerprint acquisision when the fingerprint sensor needs cleaning --> <string name="fingerprint_acquired_imager_dirty">Clean fingerprint sensor and try again</string> <string name="fingerprint_acquired_imager_dirty_alt">Clean sensor and try again</string> @@ -1959,7 +1959,7 @@ <!-- Message shown during face acquisition when the sensor needs to be recalibrated [CHAR LIMIT=50] --> <string name="face_acquired_recalibrate">Please re-enroll your face.</string> <!-- Message shown during face enrollment when a different person's face is detected [CHAR LIMIT=50] --> - <string name="face_acquired_too_different">Can\u2019t recognize face. Try again.</string> + <string name="face_acquired_too_different">Face not recognized. Try again.</string> <!-- Message shown during face enrollment when the face is too similar to a previous acquisition [CHAR LIMIT=50] --> <string name="face_acquired_too_similar">Change the position of your head slightly</string> <!-- Message shown during acqusition when the user's face is turned too far left or right [CHAR LIMIT=50] --> diff --git a/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java b/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java index 43266a51502b..cb3f99c37a4f 100644 --- a/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java +++ b/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java @@ -17,6 +17,7 @@ package android.animation; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.util.PollingCheck; @@ -343,6 +344,20 @@ public class AnimatorSetCallsTest { } @Test + public void childAnimatorCancelsDuringUpdate_animatorSetIsEnded() throws Throwable { + mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + animation.cancel(); + } + }); + mActivity.runOnUiThread(() -> { + mSet1.start(); + assertFalse(mSet1.isRunning()); + }); + } + + @Test public void reentrantStart() throws Throwable { CountDownLatch latch = new CountDownLatch(3); AnimatorListenerAdapter listener = new AnimatorListenerAdapter() { diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java index bd5f809dc689..e118c98dd4da 100644 --- a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java +++ b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java @@ -255,6 +255,7 @@ public class SQLiteDatabaseTest { final String query = "--comment\nSELECT count(*) from t1"; + database.beginTransactionReadOnly(); try { for (int i = count; i > 0; i--) { ticker.arriveAndAwaitAdvance(); @@ -268,6 +269,7 @@ public class SQLiteDatabaseTest { } catch (Throwable t) { errors.add(t); } finally { + database.endTransaction(); ticker.arriveAndDeregister(); } } diff --git a/core/tests/coretests/src/android/graphics/PaintTest.java b/core/tests/coretests/src/android/graphics/PaintTest.java index bf56df1c9441..0dec756d7611 100644 --- a/core/tests/coretests/src/android/graphics/PaintTest.java +++ b/core/tests/coretests/src/android/graphics/PaintTest.java @@ -19,6 +19,7 @@ package android.graphics; import static org.junit.Assert.assertNotEquals; import android.test.InstrumentationTestCase; +import android.text.TextUtils; import androidx.test.filters.SmallTest; @@ -362,4 +363,44 @@ public class PaintTest extends InstrumentationTestCase { // = 30 assertEquals(30.0f, p.getUnderlineThickness(), 0.5f); } + + private int getClusterCount(Paint p, String text) { + Paint.RunInfo runInfo = new Paint.RunInfo(); + p.getRunCharacterAdvance(text, 0, text.length(), 0, text.length(), false, 0, null, 0, null, + runInfo); + int ccByString = runInfo.getClusterCount(); + runInfo.setClusterCount(0); + char[] buf = new char[text.length()]; + TextUtils.getChars(text, 0, text.length(), buf, 0); + p.getRunCharacterAdvance(buf, 0, buf.length, 0, buf.length, false, 0, null, 0, null, + runInfo); + int ccByChars = runInfo.getClusterCount(); + assertEquals(ccByChars, ccByString); + return ccByChars; + } + + public void testCluster() { + final Paint p = new Paint(); + p.setTextSize(100); + + // Regular String + assertEquals(1, getClusterCount(p, "A")); + assertEquals(2, getClusterCount(p, "AB")); + + // Ligature is in the same cluster + assertEquals(1, getClusterCount(p, "fi")); // Ligature + p.setFontFeatureSettings("'liga' off"); + assertEquals(2, getClusterCount(p, "fi")); // Ligature is disabled + p.setFontFeatureSettings(""); + + // Combining character + assertEquals(1, getClusterCount(p, "\u0061\u0300")); // A + COMBINING GRAVE ACCENT + + // BiDi + final String rtlStr = "\u05D0\u05D1\u05D2"; + final String ltrStr = "abc"; + assertEquals(3, getClusterCount(p, rtlStr)); + assertEquals(6, getClusterCount(p, rtlStr + ltrStr)); + assertEquals(9, getClusterCount(p, ltrStr + rtlStr + ltrStr)); + } } diff --git a/core/tests/coretests/src/android/os/BuildTest.java b/core/tests/coretests/src/android/os/BuildTest.java index 3162e6da15c5..2d3e12331e23 100644 --- a/core/tests/coretests/src/android/os/BuildTest.java +++ b/core/tests/coretests/src/android/os/BuildTest.java @@ -45,7 +45,8 @@ public class BuildTest { public final RavenwoodRule mRavenwood = new RavenwoodRule(); @Rule - public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule( + SetFlagsRule.DefaultInitValueType.NULL_DEFAULT); /** * Asserts that a String is non-null and non-empty. If it is not, diff --git a/core/tests/coretests/src/android/text/TextLineTest.java b/core/tests/coretests/src/android/text/TextLineTest.java index 34842a0b7597..a31992c8cfa1 100644 --- a/core/tests/coretests/src/android/text/TextLineTest.java +++ b/core/tests/coretests/src/android/text/TextLineTest.java @@ -50,11 +50,11 @@ public class TextLineTest { tl.set(paint, line, 0, line.length(), Layout.DIR_LEFT_TO_RIGHT, Layout.DIRS_ALL_LEFT_TO_RIGHT, false /* hasTabs */, null /* tabStops */, 0, 0 /* no ellipsis */, false /* useFallbackLinespace */); - final float originalWidth = tl.metrics(null, null, false); + final float originalWidth = tl.metrics(null, null, false, null); final float expandedWidth = 2 * originalWidth; tl.justify(expandedWidth); - final float newWidth = tl.metrics(null, null, false); + final float newWidth = tl.metrics(null, null, false, null); TextLine.recycle(tl); return Math.abs(newWidth - expandedWidth) < 0.5; } @@ -128,7 +128,7 @@ public class TextLineTest { private void assertMeasurements(final TextLine tl, final int length, boolean trailing, final float[] expected) { for (int offset = 0; offset <= length; ++offset) { - assertEquals(expected[offset], tl.measure(offset, trailing, null, null), 0.0f); + assertEquals(expected[offset], tl.measure(offset, trailing, null, null, null), 0.0f); } final boolean[] trailings = new boolean[length + 1]; @@ -318,7 +318,7 @@ public class TextLineTest { tl.set(new TextPaint(), text, 0, text.length(), 1, Layout.DIRS_ALL_LEFT_TO_RIGHT, false /* hasTabs */, null /* tabStops */, 9, 12, false /* useFallbackLineSpacing */); - tl.measure(text.length(), false /* trailing */, null /* fmi */, null); + tl.measure(text.length(), false /* trailing */, null /* fmi */, null, null); assertFalse(span.mIsUsed); } @@ -335,7 +335,7 @@ public class TextLineTest { tl.set(new TextPaint(), text, 0, text.length(), 1, Layout.DIRS_ALL_LEFT_TO_RIGHT, false /* hasTabs */, null /* tabStops */, 9, 12, false /* useFallbackLineSpacing */); - tl.measure(text.length(), false /* trailing */, null /* fmi */, null); + tl.measure(text.length(), false /* trailing */, null /* fmi */, null, null); assertTrue(span.mIsUsed); } @@ -352,7 +352,7 @@ public class TextLineTest { tl.set(new TextPaint(), text, 0, text.length(), 1, Layout.DIRS_ALL_LEFT_TO_RIGHT, false /* hasTabs */, null /* tabStops */, 9, 12, false /* useFallbackLineSpacing */); - tl.measure(text.length(), false /* trailing */, null /* fmi */, null); + tl.measure(text.length(), false /* trailing */, null /* fmi */, null, null); assertTrue(span.mIsUsed); } diff --git a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java index c9536b9b8129..533b799341c1 100644 --- a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java +++ b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java @@ -22,7 +22,6 @@ import static org.junit.Assert.assertThrows; import android.os.BadParcelableException; import android.os.Parcel; -import android.platform.test.annotations.IgnoreUnderRavenwood; import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.filters.SmallTest; @@ -34,7 +33,6 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) @SmallTest -@IgnoreUnderRavenwood(blockedBy = LongArrayMultiStateCounter.class) public class LongArrayMultiStateCounterTest { @Rule public final RavenwoodRule mRavenwood = new RavenwoodRule(); diff --git a/data/keyboards/Vendor_0957_Product_0031.idc b/data/keyboards/Vendor_0957_Product_0031.idc new file mode 100644 index 000000000000..f0320c8a78d4 --- /dev/null +++ b/data/keyboards/Vendor_0957_Product_0031.idc @@ -0,0 +1,21 @@ +# Copyright 2024 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Input Device Configuration file for Google Reference RCU Remote. +# PID 0031 is for old GPIO pin + +# Basic Parameters +keyboard.doNotWakeByDefault = 1 +audio.mic = 1
\ No newline at end of file diff --git a/data/keyboards/Vendor_0957_Product_0034.idc b/data/keyboards/Vendor_0957_Product_0034.idc new file mode 100644 index 000000000000..52ed0bc74fd4 --- /dev/null +++ b/data/keyboards/Vendor_0957_Product_0034.idc @@ -0,0 +1,23 @@ +# Copyright 2024 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Input Device Configuration file for Google Reference RCU Remote. +# PID 0034 is for new GPIO pin PCB. + +# Basic Parameters +keyboard.layout = Vendor_0957_Product_0031 +# The reason why we set is follow https://docs.partner.android.com/tv/build/gtv/boot-resume +keyboard.doNotWakeByDefault = 1 +audio.mic = 1
\ No newline at end of file diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index f10cdb82022e..c5a2f983ae00 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -65,6 +65,8 @@ public class Paint { private long mNativeShader; private long mNativeColorFilter; + private static boolean sIsRobolectric = Build.FINGERPRINT.equals("robolectric"); + // Use a Holder to allow static initialization of Paint in the boot image. private static class NoImagePreloadHolder { public static final NativeAllocationRegistry sRegistry = @@ -2474,6 +2476,19 @@ public class Paint { nGetFontMetricsInt(mNativePaint, metrics, true); } + /** @hide */ + public static final class RunInfo { + private int mClusterCount = 0; + + public int getClusterCount() { + return mClusterCount; + } + + public void setClusterCount(int clusterCount) { + mClusterCount = clusterCount; + } + } + /** * Return the recommend line spacing based on the current typeface and * text size. @@ -3320,7 +3335,7 @@ public class Paint { int contextEnd, boolean isRtl, int offset, @Nullable float[] advances, int advancesIndex) { return getRunCharacterAdvance(text, start, end, contextStart, contextEnd, isRtl, offset, - advances, advancesIndex, null); + advances, advancesIndex, null, null); } /** @@ -3339,12 +3354,14 @@ public class Paint { * @param advances the array that receives the computed character advances * @param advancesIndex the start index from which the advances array is filled * @param drawBounds the output parameter for the bounding box of drawing text, optional + * @param runInfo the output parameter for storing run information. * @return width measurement between start and offset - * @hide + * @hide TODO: Reorganize APIs */ public float getRunCharacterAdvance(@NonNull char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset, - @Nullable float[] advances, int advancesIndex, @Nullable RectF drawBounds) { + @Nullable float[] advances, int advancesIndex, @Nullable RectF drawBounds, + @Nullable RunInfo runInfo) { if (text == null) { throw new IllegalArgumentException("text cannot be null"); } @@ -3370,11 +3387,19 @@ public class Paint { } if (end == start) { + if (runInfo != null) { + runInfo.setClusterCount(0); + } return 0.0f; } - return nGetRunCharacterAdvance(mNativePaint, text, start, end, contextStart, contextEnd, - isRtl, offset, advances, advancesIndex, drawBounds); + if (sIsRobolectric) { + return nGetRunCharacterAdvance(mNativePaint, text, start, end, + contextStart, contextEnd, isRtl, offset, advances, advancesIndex, drawBounds); + } else { + return nGetRunCharacterAdvance(mNativePaint, text, start, end, contextStart, contextEnd, + isRtl, offset, advances, advancesIndex, drawBounds, runInfo); + } } /** @@ -3402,7 +3427,7 @@ public class Paint { int contextStart, int contextEnd, boolean isRtl, int offset, @Nullable float[] advances, int advancesIndex) { return getRunCharacterAdvance(text, start, end, contextStart, contextEnd, isRtl, offset, - advances, advancesIndex, null); + advances, advancesIndex, null, null); } /** @@ -3418,12 +3443,14 @@ public class Paint { * @param advances the array that receives the computed character advances * @param advancesIndex the start index from which the advances array is filled * @param drawBounds the output parameter for the bounding box of drawing text, optional + * @param runInfo an optional output parameter for filling run information. * @return width measurement between start and offset - * @hide + * @hide TODO: Reorganize APIs */ public float getRunCharacterAdvance(@NonNull CharSequence text, int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset, - @Nullable float[] advances, int advancesIndex, @Nullable RectF drawBounds) { + @Nullable float[] advances, int advancesIndex, @Nullable RectF drawBounds, + @Nullable RunInfo runInfo) { if (text == null) { throw new IllegalArgumentException("text cannot be null"); } @@ -3456,7 +3483,7 @@ public class Paint { TextUtils.getChars(text, contextStart, contextEnd, buf, 0); final float result = getRunCharacterAdvance(buf, start - contextStart, end - contextStart, 0, contextEnd - contextStart, isRtl, offset - contextStart, - advances, advancesIndex, drawBounds); + advances, advancesIndex, drawBounds, runInfo); TemporaryBuffer.recycle(buf); return result; } @@ -3574,7 +3601,7 @@ public class Paint { int contextStart, int contextEnd, boolean isRtl, int offset); private static native float nGetRunCharacterAdvance(long paintPtr, char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset, float[] advances, - int advancesIndex, RectF drawingBounds); + int advancesIndex, RectF drawingBounds, RunInfo runInfo); private static native int nGetOffsetForAdvance(long paintPtr, char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, float advance); private static native void nGetFontMetricsIntForText(long paintPtr, char[] text, @@ -3729,4 +3756,11 @@ public class Paint { private static native void nSetTextSize(long paintPtr, float textSize); @CriticalNative private static native boolean nEqualsForTextMeasurement(long leftPaintPtr, long rightPaintPtr); + + + // Following Native methods are kept for old Robolectric JNI signature used by + // SystemUIGoogleRoboRNGTests + private static native float nGetRunCharacterAdvance(long paintPtr, char[] text, + int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset, + float[] advances, int advancesIndex, RectF drawingBounds); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java index 06210ff98642..44ee5615ac74 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java @@ -331,7 +331,9 @@ class ActivityEmbeddingAnimationRunner { if (!animation.hasExtension()) { continue; } - if (adapter.mChange.hasFlags(FLAG_TRANSLUCENT)) { + if (adapter.mChange.hasFlags(FLAG_TRANSLUCENT) + && adapter.mChange.getActivityComponent() != null) { + // Skip edge extension for translucent activity. continue; } final TransitionInfo.Change change = adapter.mChange; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java index 80fc3a867d48..ac2a1c867462 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java @@ -136,6 +136,9 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { mStartTaskRect.set(mClosingTarget.windowConfiguration.getBounds()); mStartTaskRect.offsetTo(0, 0); + // inset bottom in case of pinned taskbar being present + mStartTaskRect.inset(0, 0, 0, mClosingTarget.contentInsets.bottom); + // Draw background. mBackground.ensureBackground(mClosingTarget.windowConfiguration.getBounds(), BACKGROUNDCOLOR, mTransaction); 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 470a82511481..ca283128891c 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 @@ -1177,14 +1177,17 @@ public class BubbleStackView extends FrameLayout if (mStackAnimationController.isStackOnLeftSide()) { int availableRectOffsetX = mPositioner.getAvailableRect().left - mPositioner.getScreenRect().left; - animate().translationX(-(mBubbleSize + availableRectOffsetX)).start(); + mBubbleContainer + .animate() + .translationX(-(mBubbleSize + availableRectOffsetX)) + .start(); } else { int availableRectOffsetX = mPositioner.getAvailableRect().right - mPositioner.getScreenRect().right; - animate().translationX(mBubbleSize - availableRectOffsetX).start(); + mBubbleContainer.animate().translationX(mBubbleSize - availableRectOffsetX).start(); } } else { - animate().translationX(0).start(); + mBubbleContainer.animate().translationX(0).start(); } }; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 89dcc4c1d261..8e375a9ef5ee 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -987,10 +987,12 @@ public class PipTransition extends PipTransitionController { 0 /* startingAngle */, rotationDelta); if (sourceHintRect == null) { // We use content overlay when there is no source rect hint to enter PiP use bounds - // animation. + // animation. We also temporarily disallow app icon overlay and use color overlay + // instead when in fixed rotation enter PiP in button nav with no sourceRectHint. + // TODO(b/319286295): Fix App Icon Overlay animation in fixed rotation in btn nav. // TODO(b/272819817): cleanup the null-check and extra logging. final boolean hasTopActivityInfo = taskInfo.topActivityInfo != null; - if (hasTopActivityInfo) { + if (hasTopActivityInfo && mFixedRotationState != FIXED_ROTATION_TRANSITION) { animator.setAppIconContentOverlay( mContext, currentBounds, destinationBounds, taskInfo.topActivityInfo, mPipBoundsState.getLauncherState().getAppIconSizePx()); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java index bc1a57572c63..5de8a9be9576 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java @@ -481,18 +481,20 @@ class SplitScreenTransitions { private void startFadeAnimation(@NonNull SurfaceControl leash, boolean show) { final float end = show ? 1.f : 0.f; final float start = 1.f - end; - final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); final ValueAnimator va = ValueAnimator.ofFloat(start, end); va.setDuration(FADE_DURATION); va.setInterpolator(show ? ALPHA_IN : ALPHA_OUT); va.addUpdateListener(animation -> { float fraction = animation.getAnimatedFraction(); + final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); transaction.setAlpha(leash, start * (1.f - fraction) + end * fraction); transaction.apply(); + mTransactionPool.release(transaction); }); va.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { + final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); transaction.setAlpha(leash, end); transaction.apply(); mTransactionPool.release(transaction); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java index 0eb7c2d98e0a..a7843e218a8a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java @@ -293,11 +293,7 @@ public class ShellController { private class ShellInterfaceImpl implements ShellInterface { @Override public void onInit() { - try { - mMainExecutor.executeBlocking(() -> ShellController.this.handleInit()); - } catch (InterruptedException e) { - throw new RuntimeException("Failed to initialize the Shell in 2s", e); - } + mMainExecutor.execute(ShellController.this::handleInit); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java index db845137540b..8c861c63a70d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java @@ -124,6 +124,16 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, int mAnimType = ANIM_TYPE_DEFAULT; final IBinder mTransition; + private final Transitions mPlayer; + private final DefaultMixedHandler mMixedHandler; + private final PipTransitionController mPipHandler; + private final RecentsTransitionHandler mRecentsHandler; + private final StageCoordinator mSplitHandler; + private final KeyguardTransitionHandler mKeyguardHandler; + private final DesktopTasksController mDesktopTasksController; + private final UnfoldTransitionHandler mUnfoldHandler; + private final ActivityEmbeddingController mActivityEmbeddingController; + Transitions.TransitionHandler mLeftoversHandler = null; TransitionInfo mInfo = null; WindowContainerTransaction mFinishWCT = null; @@ -144,9 +154,386 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, */ int mInFlightSubAnimations = 0; - MixedTransition(int type, IBinder transition) { + MixedTransition(int type, IBinder transition, Transitions player, + DefaultMixedHandler mixedHandler, PipTransitionController pipHandler, + RecentsTransitionHandler recentsHandler, StageCoordinator splitHandler, + KeyguardTransitionHandler keyguardHandler, + DesktopTasksController desktopTasksController, + UnfoldTransitionHandler unfoldHandler, + ActivityEmbeddingController activityEmbeddingController) { mType = type; mTransition = transition; + mPlayer = player; + mMixedHandler = mixedHandler; + mPipHandler = pipHandler; + mRecentsHandler = recentsHandler; + mSplitHandler = splitHandler; + mKeyguardHandler = keyguardHandler; + mDesktopTasksController = desktopTasksController; + mUnfoldHandler = unfoldHandler; + mActivityEmbeddingController = activityEmbeddingController; + + switch (type) { + case TYPE_RECENTS_DURING_DESKTOP: + case TYPE_RECENTS_DURING_KEYGUARD: + case TYPE_RECENTS_DURING_SPLIT: + mLeftoversHandler = mRecentsHandler; + break; + case TYPE_UNFOLD: + mLeftoversHandler = mUnfoldHandler; + break; + case TYPE_DISPLAY_AND_SPLIT_CHANGE: + case TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING: + case TYPE_ENTER_PIP_FROM_SPLIT: + case TYPE_KEYGUARD: + case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE: + default: + break; + } + } + + boolean startAnimation( + @NonNull IBinder transition, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + if (mType == TYPE_ENTER_PIP_FROM_SPLIT) { + return animateEnterPipFromSplit(this, info, startTransaction, finishTransaction, + finishCallback, mPlayer, mMixedHandler, mPipHandler, mSplitHandler); + } else if (mType == TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) { + return animateEnterPipFromActivityEmbedding( + info, startTransaction, finishTransaction, finishCallback); + } else if (mType == TYPE_DISPLAY_AND_SPLIT_CHANGE) { + return false; + } else if (mType == TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { + final boolean handledToPip = animateOpenIntentWithRemoteAndPip( + info, startTransaction, finishTransaction, finishCallback); + // Consume the transition on remote handler if the leftover handler already handle + // this transition. And if it cannot, the transition will be handled by remote + // handler, so don't consume here. + // Need to check leftOverHandler as it may change in + // #animateOpenIntentWithRemoteAndPip + if (handledToPip && mHasRequestToRemote + && mLeftoversHandler != mPlayer.getRemoteTransitionHandler()) { + mPlayer.getRemoteTransitionHandler().onTransitionConsumed( + transition, false, null); + } + return handledToPip; + } else if (mType == TYPE_RECENTS_DURING_SPLIT) { + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + // Pip auto-entering info might be appended to recent transition like pressing + // home-key in 3-button navigation. This offers split handler the opportunity to + // handle split to pip animation. + if (mPipHandler.isEnteringPip(change, info.getType()) + && mSplitHandler.getSplitItemPosition(change.getLastParent()) + != SPLIT_POSITION_UNDEFINED) { + return animateEnterPipFromSplit( + this, info, startTransaction, finishTransaction, finishCallback, + mPlayer, mMixedHandler, mPipHandler, mSplitHandler); + } + } + + return animateRecentsDuringSplit( + info, startTransaction, finishTransaction, finishCallback); + } else if (mType == TYPE_KEYGUARD) { + return animateKeyguard(this, info, startTransaction, finishTransaction, + finishCallback, mKeyguardHandler, mPipHandler); + } else if (mType == TYPE_RECENTS_DURING_KEYGUARD) { + return animateRecentsDuringKeyguard( + info, startTransaction, finishTransaction, finishCallback); + } else if (mType == TYPE_RECENTS_DURING_DESKTOP) { + return animateRecentsDuringDesktop( + info, startTransaction, finishTransaction, finishCallback); + } else if (mType == TYPE_UNFOLD) { + return animateUnfold( + info, startTransaction, finishTransaction, finishCallback); + } else { + throw new IllegalStateException( + "Starting mixed animation without a known mixed type? " + mType); + } + } + + private boolean animateEnterPipFromActivityEmbedding( + @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for " + + "entering PIP from an Activity Embedding window"); + // Split into two transitions (wct) + TransitionInfo.Change pipChange = null; + final TransitionInfo everythingElse = + subCopy(info, TRANSIT_TO_BACK, true /* changes */); + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + TransitionInfo.Change change = info.getChanges().get(i); + if (mPipHandler.isEnteringPip(change, info.getType())) { + if (pipChange != null) { + throw new IllegalStateException("More than 1 pip-entering changes in one" + + " transition? " + info); + } + pipChange = change; + // going backwards, so remove-by-index is fine. + everythingElse.getChanges().remove(i); + } + } + + final Transitions.TransitionFinishCallback finishCB = (wct) -> { + --mInFlightSubAnimations; + joinFinishArgs(wct); + if (mInFlightSubAnimations > 0) return; + finishCallback.onTransitionFinished(mFinishWCT); + }; + + if (!mActivityEmbeddingController.shouldAnimate(everythingElse)) { + // Fallback to dispatching to other handlers. + return false; + } + + // PIP window should always be on the highest Z order. + if (pipChange != null) { + mInFlightSubAnimations = 2; + mPipHandler.startEnterAnimation( + pipChange, + startTransaction.setLayer(pipChange.getLeash(), Integer.MAX_VALUE), + finishTransaction, + finishCB); + } else { + mInFlightSubAnimations = 1; + } + + mActivityEmbeddingController.startAnimation(mTransition, everythingElse, + startTransaction, finishTransaction, finishCB); + return true; + } + + private boolean animateOpenIntentWithRemoteAndPip( + @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + TransitionInfo.Change pipChange = null; + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + TransitionInfo.Change change = info.getChanges().get(i); + if (mPipHandler.isEnteringPip(change, info.getType())) { + if (pipChange != null) { + throw new IllegalStateException("More than 1 pip-entering changes in one" + + " transition? " + info); + } + pipChange = change; + info.getChanges().remove(i); + } + } + Transitions.TransitionFinishCallback finishCB = (wct) -> { + --mInFlightSubAnimations; + joinFinishArgs(wct); + if (mInFlightSubAnimations > 0) return; + finishCallback.onTransitionFinished(mFinishWCT); + }; + if (pipChange == null) { + if (mLeftoversHandler != null) { + mInFlightSubAnimations = 1; + if (mLeftoversHandler.startAnimation( + mTransition, info, startTransaction, finishTransaction, finishCB)) { + return true; + } + } + return false; + } + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Splitting PIP into a separate" + + " animation because remote-animation likely doesn't support it"); + // Split the transition into 2 parts: the pip part and the rest. + mInFlightSubAnimations = 2; + // make a new startTransaction because pip's startEnterAnimation "consumes" it so + // we need a separate one to send over to launcher. + SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction(); + + mPipHandler.startEnterAnimation(pipChange, otherStartT, finishTransaction, finishCB); + + // Dispatch the rest of the transition normally. + if (mLeftoversHandler != null + && mLeftoversHandler.startAnimation( + mTransition, info, startTransaction, finishTransaction, finishCB)) { + return true; + } + mLeftoversHandler = mPlayer.dispatchTransition(mTransition, info, + startTransaction, finishTransaction, finishCB, mMixedHandler); + return true; + } + + private boolean animateRecentsDuringSplit( + @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + // Split-screen is only interested in the recents transition finishing (and merging), so + // just wrap finish and start recents animation directly. + Transitions.TransitionFinishCallback finishCB = (wct) -> { + mInFlightSubAnimations = 0; + // If pair-to-pair switching, the post-recents clean-up isn't needed. + wct = wct != null ? wct : new WindowContainerTransaction(); + if (mAnimType != ANIM_TYPE_PAIR_TO_PAIR) { + mSplitHandler.onRecentsInSplitAnimationFinish(wct, finishTransaction); + } else { + // notify pair-to-pair recents animation finish + mSplitHandler.onRecentsPairToPairAnimationFinish(wct); + } + mSplitHandler.onTransitionAnimationComplete(); + finishCallback.onTransitionFinished(wct); + }; + mInFlightSubAnimations = 1; + mSplitHandler.onRecentsInSplitAnimationStart(info); + final boolean handled = mLeftoversHandler.startAnimation(mTransition, info, + startTransaction, finishTransaction, finishCB); + if (!handled) { + mSplitHandler.onRecentsInSplitAnimationCanceled(); + } + return handled; + } + + private boolean animateRecentsDuringKeyguard( + @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + if (mInfo == null) { + mInfo = info; + mFinishT = finishTransaction; + mFinishCB = finishCallback; + } + return startSubAnimation(mRecentsHandler, info, startTransaction, finishTransaction); + } + + private boolean animateRecentsDuringDesktop( + @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + Transitions.TransitionFinishCallback finishCB = wct -> { + mInFlightSubAnimations--; + if (mInFlightSubAnimations == 0) { + finishCallback.onTransitionFinished(wct); + } + }; + + mInFlightSubAnimations++; + boolean consumed = mRecentsHandler.startAnimation( + mTransition, info, startTransaction, finishTransaction, finishCB); + if (!consumed) { + mInFlightSubAnimations--; + return false; + } + if (mDesktopTasksController != null) { + mDesktopTasksController.syncSurfaceState(info, finishTransaction); + return true; + } + + return false; + } + + private boolean animateUnfold( + @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + final Transitions.TransitionFinishCallback finishCB = (wct) -> { + mInFlightSubAnimations--; + if (mInFlightSubAnimations > 0) return; + finishCallback.onTransitionFinished(wct); + }; + mInFlightSubAnimations = 1; + // Sync pip state. + if (mPipHandler != null) { + mPipHandler.syncPipSurfaceState(info, startTransaction, finishTransaction); + } + if (mSplitHandler != null && mSplitHandler.isSplitActive()) { + mSplitHandler.updateSurfaces(startTransaction); + } + return mUnfoldHandler.startAnimation( + mTransition, info, startTransaction, finishTransaction, finishCB); + } + + void mergeAnimation( + @NonNull IBinder transition, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + if (mType == TYPE_DISPLAY_AND_SPLIT_CHANGE) { + // queue since no actual animation. + } else if (mType == TYPE_ENTER_PIP_FROM_SPLIT) { + if (mAnimType == ANIM_TYPE_GOING_HOME) { + boolean ended = mSplitHandler.end(); + // If split couldn't end (because it is remote), then don't end everything else + // since we have to play out the animation anyways. + if (!ended) return; + mPipHandler.end(); + if (mLeftoversHandler != null) { + mLeftoversHandler.mergeAnimation( + transition, info, t, mergeTarget, finishCallback); + } + } else { + mPipHandler.end(); + } + } else if (mType == TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) { + mPipHandler.end(); + mActivityEmbeddingController.mergeAnimation(transition, info, t, mergeTarget, + finishCallback); + } else if (mType == TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { + mPipHandler.end(); + if (mLeftoversHandler != null) { + mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, + finishCallback); + } + } else if (mType == TYPE_RECENTS_DURING_SPLIT) { + if (mSplitHandler.isPendingEnter(transition)) { + // Recents -> enter-split means that we are switching from one pair to + // another pair. + mAnimType = ANIM_TYPE_PAIR_TO_PAIR; + } + mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); + } else if (mType == TYPE_KEYGUARD) { + mKeyguardHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); + } else if (mType == TYPE_RECENTS_DURING_KEYGUARD) { + if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0) { + DefaultMixedHandler.handoverTransitionLeashes(mInfo, info, t, mFinishT); + if (animateKeyguard( + this, info, t, mFinishT, mFinishCB, mKeyguardHandler, mPipHandler)) { + finishCallback.onTransitionFinished(null); + } + } + mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); + } else if (mType == TYPE_RECENTS_DURING_DESKTOP) { + mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); + } else if (mType == TYPE_UNFOLD) { + mUnfoldHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); + } else { + throw new IllegalStateException( + "Playing a mixed transition with unknown type? " + mType); + } + } + + void onTransitionConsumed( + @NonNull IBinder transition, boolean aborted, + @Nullable SurfaceControl.Transaction finishT) { + if (mType == TYPE_ENTER_PIP_FROM_SPLIT) { + mPipHandler.onTransitionConsumed(transition, aborted, finishT); + } else if (mType == TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) { + mPipHandler.onTransitionConsumed(transition, aborted, finishT); + mActivityEmbeddingController.onTransitionConsumed(transition, aborted, finishT); + } else if (mType == TYPE_RECENTS_DURING_SPLIT) { + mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); + } else if (mType == TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { + mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); + } else if (mType == TYPE_KEYGUARD) { + mKeyguardHandler.onTransitionConsumed(transition, aborted, finishT); + } else if (mType == TYPE_RECENTS_DURING_DESKTOP) { + mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); + } else if (mType == TYPE_UNFOLD) { + mUnfoldHandler.onTransitionConsumed(transition, aborted, finishT); + } + if (mHasRequestToRemote) { + mPlayer.getRemoteTransitionHandler().onTransitionConsumed( + transition, aborted, finishT); + } } boolean startSubAnimation(Transitions.TransitionHandler handler, TransitionInfo info, @@ -235,8 +622,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, throw new IllegalStateException("Unexpected remote transition in" + "pip-enter-from-split request"); } - mActiveTransitions.add(new MixedTransition(MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT, - transition)); + mActiveTransitions.add(createMixedTransition( + MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT, transition)); WindowContainerTransaction out = new WindowContainerTransaction(); mPipHandler.augmentRequest(transition, request, out); @@ -247,7 +634,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, mActivityEmbeddingController != null)) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a PiP-enter request from an Activity Embedding split"); - mActiveTransitions.add(new MixedTransition( + mActiveTransitions.add(createMixedTransition( MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING, transition)); // Postpone transition splitting to later. WindowContainerTransaction out = new WindowContainerTransaction(); @@ -266,7 +653,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, if (handler == null) { return null; } - final MixedTransition mixed = new MixedTransition( + final MixedTransition mixed = createMixedTransition( MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE, transition); mixed.mLeftoversHandler = handler.first; mActiveTransitions.add(mixed); @@ -292,7 +679,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, mPlayer.getRemoteTransitionHandler(), new WindowContainerTransaction()); } - final MixedTransition mixed = new MixedTransition( + final MixedTransition mixed = createMixedTransition( MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition); mixed.mLeftoversHandler = handler.first; mActiveTransitions.add(mixed); @@ -301,10 +688,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, final WindowContainerTransaction wct = mUnfoldHandler.handleRequest(transition, request); if (wct != null) { - final MixedTransition mixed = new MixedTransition( - MixedTransition.TYPE_UNFOLD, transition); - mixed.mLeftoversHandler = mUnfoldHandler; - mActiveTransitions.add(mixed); + mActiveTransitions.add(createMixedTransition( + MixedTransition.TYPE_UNFOLD, transition)); } return wct; } @@ -330,31 +715,31 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, private void setRecentsTransitionDuringSplit(IBinder transition) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while " + "Split-Screen is foreground, so treat it as Mixed."); - final MixedTransition mixed = new MixedTransition( - MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition); - mixed.mLeftoversHandler = mRecentsHandler; - mActiveTransitions.add(mixed); + mActiveTransitions.add(createMixedTransition( + MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition)); } private void setRecentsTransitionDuringKeyguard(IBinder transition) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while " + "keyguard is visible, so treat it as Mixed."); - final MixedTransition mixed = new MixedTransition( - MixedTransition.TYPE_RECENTS_DURING_KEYGUARD, transition); - mixed.mLeftoversHandler = mRecentsHandler; - mActiveTransitions.add(mixed); + mActiveTransitions.add(createMixedTransition( + MixedTransition.TYPE_RECENTS_DURING_KEYGUARD, transition)); } private void setRecentsTransitionDuringDesktop(IBinder transition) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while " + "desktop mode is active, so treat it as Mixed."); - final MixedTransition mixed = new MixedTransition( - MixedTransition.TYPE_RECENTS_DURING_DESKTOP, transition); - mixed.mLeftoversHandler = mRecentsHandler; - mActiveTransitions.add(mixed); + mActiveTransitions.add(createMixedTransition( + MixedTransition.TYPE_RECENTS_DURING_DESKTOP, transition)); + } + + private MixedTransition createMixedTransition(int type, IBinder transition) { + return new MixedTransition(type, transition, mPlayer, this, mPipHandler, mRecentsHandler, + mSplitHandler, mKeyguardHandler, mDesktopTasksController, mUnfoldHandler, + mActivityEmbeddingController); } - private TransitionInfo subCopy(@NonNull TransitionInfo info, + private static TransitionInfo subCopy(@NonNull TransitionInfo info, @WindowManager.TransitionType int newType, boolean withChanges) { final TransitionInfo out = new TransitionInfo(newType, withChanges ? info.getFlags() : 0); out.setTrack(info.getTrack()); @@ -371,12 +756,12 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, return out; } - private boolean isHomeOpening(@NonNull TransitionInfo.Change change) { + private static boolean isHomeOpening(@NonNull TransitionInfo.Change change) { return change.getTaskInfo() != null && change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_HOME; } - private boolean isWallpaper(@NonNull TransitionInfo.Change change) { + private static boolean isWallpaper(@NonNull TransitionInfo.Change change) { return (change.getFlags() & FLAG_IS_WALLPAPER) != 0; } @@ -398,14 +783,15 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, if (KeyguardTransitionHandler.handles(info)) { if (mixed != null && mixed.mType != MixedTransition.TYPE_KEYGUARD) { final MixedTransition keyguardMixed = - new MixedTransition(MixedTransition.TYPE_KEYGUARD, transition); + createMixedTransition(MixedTransition.TYPE_KEYGUARD, transition); mActiveTransitions.add(keyguardMixed); Transitions.TransitionFinishCallback callback = wct -> { mActiveTransitions.remove(keyguardMixed); finishCallback.onTransitionFinished(wct); }; final boolean hasAnimateKeyguard = animateKeyguard( - keyguardMixed, info, startTransaction, finishTransaction, callback); + keyguardMixed, info, startTransaction, finishTransaction, callback, + mKeyguardHandler, mPipHandler); if (hasAnimateKeyguard) { ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Converting mixed transition into a keyguard transition"); @@ -429,175 +815,21 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, finishCallback.onTransitionFinished(wct); }; - if (chosenTransition.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) { - return animateEnterPipFromSplit( - chosenTransition, info, startTransaction, finishTransaction, callback); - } else if (chosenTransition.mType - == MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) { - return animateEnterPipFromActivityEmbedding( - chosenTransition, info, startTransaction, finishTransaction, callback); - } else if (chosenTransition.mType == MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE) { - return false; - } else if (chosenTransition.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { - final boolean handledToPip = animateOpenIntentWithRemoteAndPip( - chosenTransition, info, startTransaction, finishTransaction, callback); - // Consume the transition on remote handler if the leftover handler already handle this - // transition. And if it cannot, the transition will be handled by remote handler, so - // don't consume here. - // Need to check leftOverHandler as it may change in #animateOpenIntentWithRemoteAndPip - if (handledToPip && chosenTransition.mHasRequestToRemote - && chosenTransition.mLeftoversHandler != mPlayer.getRemoteTransitionHandler()) { - mPlayer.getRemoteTransitionHandler().onTransitionConsumed(transition, false, null); - } - return handledToPip; - } else if (chosenTransition.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) { - for (int i = info.getChanges().size() - 1; i >= 0; --i) { - final TransitionInfo.Change change = info.getChanges().get(i); - // Pip auto-entering info might be appended to recent transition like pressing - // home-key in 3-button navigation. This offers split handler the opportunity to - // handle split to pip animation. - if (mPipHandler.isEnteringPip(change, info.getType()) - && mSplitHandler.getSplitItemPosition(change.getLastParent()) - != SPLIT_POSITION_UNDEFINED) { - return animateEnterPipFromSplit( - chosenTransition, info, startTransaction, finishTransaction, callback); - } - } - - return animateRecentsDuringSplit( - chosenTransition, info, startTransaction, finishTransaction, callback); - } else if (chosenTransition.mType == MixedTransition.TYPE_KEYGUARD) { - return animateKeyguard( - chosenTransition, info, startTransaction, finishTransaction, callback); - } else if (chosenTransition.mType == MixedTransition.TYPE_RECENTS_DURING_KEYGUARD) { - return animateRecentsDuringKeyguard( - chosenTransition, info, startTransaction, finishTransaction, callback); - } else if (chosenTransition.mType == MixedTransition.TYPE_RECENTS_DURING_DESKTOP) { - return animateRecentsDuringDesktop( - chosenTransition, info, startTransaction, finishTransaction, callback); - } else if (chosenTransition.mType == MixedTransition.TYPE_UNFOLD) { - return animateUnfold( - chosenTransition, info, startTransaction, finishTransaction, callback); - } else { + boolean handled = chosenTransition.startAnimation( + transition, info, startTransaction, finishTransaction, callback); + if (!handled) { mActiveTransitions.remove(chosenTransition); - throw new IllegalStateException("Starting mixed animation without a known mixed type? " - + chosenTransition.mType); - } - } - - private boolean animateEnterPipFromActivityEmbedding(@NonNull MixedTransition mixed, - @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction startTransaction, - @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for " - + "entering PIP from an Activity Embedding window"); - // Split into two transitions (wct) - TransitionInfo.Change pipChange = null; - final TransitionInfo everythingElse = subCopy(info, TRANSIT_TO_BACK, true /* changes */); - for (int i = info.getChanges().size() - 1; i >= 0; --i) { - TransitionInfo.Change change = info.getChanges().get(i); - if (mPipHandler.isEnteringPip(change, info.getType())) { - if (pipChange != null) { - throw new IllegalStateException("More than 1 pip-entering changes in one" - + " transition? " + info); - } - pipChange = change; - // going backwards, so remove-by-index is fine. - everythingElse.getChanges().remove(i); - } - } - - final Transitions.TransitionFinishCallback finishCB = (wct) -> { - --mixed.mInFlightSubAnimations; - mixed.joinFinishArgs(wct); - if (mixed.mInFlightSubAnimations > 0) return; - mActiveTransitions.remove(mixed); - finishCallback.onTransitionFinished(mixed.mFinishWCT); - }; - - if (!mActivityEmbeddingController.shouldAnimate(everythingElse)) { - // Fallback to dispatching to other handlers. - return false; - } - - // PIP window should always be on the highest Z order. - if (pipChange != null) { - mixed.mInFlightSubAnimations = 2; - mPipHandler.startEnterAnimation( - pipChange, startTransaction.setLayer(pipChange.getLeash(), Integer.MAX_VALUE), - finishTransaction, - finishCB); - } else { - mixed.mInFlightSubAnimations = 1; - } - - mActivityEmbeddingController.startAnimation(mixed.mTransition, everythingElse, - startTransaction, finishTransaction, finishCB); - return true; - } - - private boolean animateOpenIntentWithRemoteAndPip(@NonNull MixedTransition mixed, - @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction startTransaction, - @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback) { - TransitionInfo.Change pipChange = null; - for (int i = info.getChanges().size() - 1; i >= 0; --i) { - TransitionInfo.Change change = info.getChanges().get(i); - if (mPipHandler.isEnteringPip(change, info.getType())) { - if (pipChange != null) { - throw new IllegalStateException("More than 1 pip-entering changes in one" - + " transition? " + info); - } - pipChange = change; - info.getChanges().remove(i); - } - } - Transitions.TransitionFinishCallback finishCB = (wct) -> { - --mixed.mInFlightSubAnimations; - mixed.joinFinishArgs(wct); - if (mixed.mInFlightSubAnimations > 0) return; - mActiveTransitions.remove(mixed); - finishCallback.onTransitionFinished(mixed.mFinishWCT); - }; - if (pipChange == null) { - if (mixed.mLeftoversHandler != null) { - mixed.mInFlightSubAnimations = 1; - if (mixed.mLeftoversHandler.startAnimation(mixed.mTransition, - info, startTransaction, finishTransaction, finishCB)) { - return true; - } - } - mActiveTransitions.remove(mixed); - return false; } - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Splitting PIP into a separate" - + " animation because remote-animation likely doesn't support it"); - // Split the transition into 2 parts: the pip part and the rest. - mixed.mInFlightSubAnimations = 2; - // make a new startTransaction because pip's startEnterAnimation "consumes" it so - // we need a separate one to send over to launcher. - SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction(); - - mPipHandler.startEnterAnimation(pipChange, otherStartT, finishTransaction, finishCB); - - // Dispatch the rest of the transition normally. - if (mixed.mLeftoversHandler != null - && mixed.mLeftoversHandler.startAnimation(mixed.mTransition, info, - startTransaction, finishTransaction, finishCB)) { - return true; - } - mixed.mLeftoversHandler = mPlayer.dispatchTransition(mixed.mTransition, info, - startTransaction, finishTransaction, finishCB, this); - return true; + return handled; } - private boolean animateEnterPipFromSplit(@NonNull final MixedTransition mixed, + private static boolean animateEnterPipFromSplit(@NonNull final MixedTransition mixed, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback) { + @NonNull Transitions.TransitionFinishCallback finishCallback, + @NonNull Transitions player, @NonNull DefaultMixedHandler mixedHandler, + @NonNull PipTransitionController pipHandler, @NonNull StageCoordinator splitHandler) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for " + "entering PIP while Split-Screen is foreground."); TransitionInfo.Change pipChange = null; @@ -606,7 +838,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, boolean homeIsOpening = false; for (int i = info.getChanges().size() - 1; i >= 0; --i) { TransitionInfo.Change change = info.getChanges().get(i); - if (mPipHandler.isEnteringPip(change, info.getType())) { + if (pipHandler.isEnteringPip(change, info.getType())) { if (pipChange != null) { throw new IllegalStateException("More than 1 pip-entering changes in one" + " transition? " + info); @@ -622,7 +854,6 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, } if (pipChange == null) { // um, something probably went wrong. - mActiveTransitions.remove(mixed); return false; } final boolean isGoingHome = homeIsOpening; @@ -630,13 +861,12 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, --mixed.mInFlightSubAnimations; mixed.joinFinishArgs(wct); if (mixed.mInFlightSubAnimations > 0) return; - mActiveTransitions.remove(mixed); if (isGoingHome) { - mSplitHandler.onTransitionAnimationComplete(); + splitHandler.onTransitionAnimationComplete(); } finishCallback.onTransitionFinished(mixed.mFinishWCT); }; - if (isGoingHome || mSplitHandler.getSplitItemPosition(pipChange.getLastParent()) + if (isGoingHome || splitHandler.getSplitItemPosition(pipChange.getLastParent()) != SPLIT_POSITION_UNDEFINED) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animation is actually mixed " + "since entering-PiP caused us to leave split and return home."); @@ -652,7 +882,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, // we need a separate one to send over to launcher. SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction(); @SplitScreen.StageType int topStageToKeep = STAGE_TYPE_UNDEFINED; - if (mSplitHandler.isSplitScreenVisible()) { + if (splitHandler.isSplitScreenVisible()) { // The non-going home case, we could be pip-ing one of the split stages and keep // showing the other for (int i = info.getChanges().size() - 1; i >= 0; --i) { @@ -662,7 +892,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, continue; } @SplitScreen.StageType int splitItemStage = - mSplitHandler.getSplitItemStage(change.getLastParent()); + splitHandler.getSplitItemStage(change.getLastParent()); if (splitItemStage != STAGE_TYPE_UNDEFINED) { topStageToKeep = splitItemStage; break; @@ -670,7 +900,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, } } // Let split update internal state for dismiss. - mSplitHandler.prepareDismissAnimation(topStageToKeep, + splitHandler.prepareDismissAnimation(topStageToKeep, EXIT_REASON_CHILD_TASK_ENTER_PIP, everythingElse, otherStartT, finishTransaction); @@ -684,13 +914,13 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, } } - mPipHandler.setEnterAnimationType(ANIM_TYPE_ALPHA); - mPipHandler.startEnterAnimation(pipChange, startTransaction, finishTransaction, + pipHandler.setEnterAnimationType(ANIM_TYPE_ALPHA); + pipHandler.startEnterAnimation(pipChange, startTransaction, finishTransaction, finishCB); // Dispatch the rest of the transition normally. This will most-likely be taken by // recents or default handler. - mixed.mLeftoversHandler = mPlayer.dispatchTransition(mixed.mTransition, everythingElse, - otherStartT, finishTransaction, finishCB, this); + mixed.mLeftoversHandler = player.dispatchTransition(mixed.mTransition, everythingElse, + otherStartT, finishTransaction, finishCB, mixedHandler); } else { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Not leaving split, so just " + "forward animation to Pip-Handler."); @@ -698,7 +928,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, // new pip task is spawned). In this case, we don't actually exit split so we can // just let pip transition handle the animation verbatim. mixed.mInFlightSubAnimations = 1; - mPipHandler.startAnimation(mixed.mTransition, info, startTransaction, finishTransaction, + pipHandler.startAnimation(mixed.mTransition, info, startTransaction, finishTransaction, finishCB); } return true; @@ -735,14 +965,15 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, public boolean animatePendingEnterPipFromSplit(IBinder transition, TransitionInfo info, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, Transitions.TransitionFinishCallback finishCallback) { - final MixedTransition mixed = new MixedTransition( + final MixedTransition mixed = createMixedTransition( MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT, transition); mActiveTransitions.add(mixed); Transitions.TransitionFinishCallback callback = wct -> { mActiveTransitions.remove(mixed); finishCallback.onTransitionFinished(wct); }; - return animateEnterPipFromSplit(mixed, info, startT, finishT, callback); + return animateEnterPipFromSplit(mixed, info, startT, finishT, finishCallback, mPlayer, this, + mPipHandler, mSplitHandler); } /** @@ -765,7 +996,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, } if (displayPart.getChanges().isEmpty()) return false; unlinkMissingParents(everythingElse); - final MixedTransition mixed = new MixedTransition( + final MixedTransition mixed = createMixedTransition( MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE, transition); mActiveTransitions.add(mixed); ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animation is a mix of display change " @@ -794,116 +1025,22 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, return true; } - private boolean animateRecentsDuringSplit(@NonNull final MixedTransition mixed, - @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction startTransaction, - @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback) { - // Split-screen is only interested in the recents transition finishing (and merging), so - // just wrap finish and start recents animation directly. - Transitions.TransitionFinishCallback finishCB = (wct) -> { - mixed.mInFlightSubAnimations = 0; - mActiveTransitions.remove(mixed); - // If pair-to-pair switching, the post-recents clean-up isn't needed. - wct = wct != null ? wct : new WindowContainerTransaction(); - if (mixed.mAnimType != MixedTransition.ANIM_TYPE_PAIR_TO_PAIR) { - mSplitHandler.onRecentsInSplitAnimationFinish(wct, finishTransaction); - } else { - // notify pair-to-pair recents animation finish - mSplitHandler.onRecentsPairToPairAnimationFinish(wct); - } - mSplitHandler.onTransitionAnimationComplete(); - finishCallback.onTransitionFinished(wct); - }; - mixed.mInFlightSubAnimations = 1; - mSplitHandler.onRecentsInSplitAnimationStart(info); - final boolean handled = mixed.mLeftoversHandler.startAnimation(mixed.mTransition, info, - startTransaction, finishTransaction, finishCB); - if (!handled) { - mSplitHandler.onRecentsInSplitAnimationCanceled(); - mActiveTransitions.remove(mixed); - } - return handled; - } - - private boolean animateKeyguard(@NonNull final MixedTransition mixed, + private static boolean animateKeyguard(@NonNull final MixedTransition mixed, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback) { + @NonNull Transitions.TransitionFinishCallback finishCallback, + @NonNull KeyguardTransitionHandler keyguardHandler, + PipTransitionController pipHandler) { if (mixed.mFinishT == null) { mixed.mFinishT = finishTransaction; mixed.mFinishCB = finishCallback; } // Sync pip state. - if (mPipHandler != null) { - mPipHandler.syncPipSurfaceState(info, startTransaction, finishTransaction); - } - return mixed.startSubAnimation(mKeyguardHandler, info, startTransaction, finishTransaction); - } - - private boolean animateRecentsDuringKeyguard(@NonNull final MixedTransition mixed, - @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction startTransaction, - @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback) { - if (mixed.mInfo == null) { - mixed.mInfo = info; - mixed.mFinishT = finishTransaction; - mixed.mFinishCB = finishCallback; - } - return mixed.startSubAnimation(mRecentsHandler, info, startTransaction, finishTransaction); - } - - private boolean animateRecentsDuringDesktop(@NonNull final MixedTransition mixed, - @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction startTransaction, - @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback) { - Transitions.TransitionFinishCallback finishCB = wct -> { - mixed.mInFlightSubAnimations--; - if (mixed.mInFlightSubAnimations == 0) { - mActiveTransitions.remove(mixed); - finishCallback.onTransitionFinished(wct); - } - }; - - mixed.mInFlightSubAnimations++; - boolean consumed = mRecentsHandler.startAnimation( - mixed.mTransition, info, startTransaction, finishTransaction, finishCB); - if (!consumed) { - mixed.mInFlightSubAnimations--; - return false; - } - if (mDesktopTasksController != null) { - mDesktopTasksController.syncSurfaceState(info, finishTransaction); - return true; - } - - return false; - } - - private boolean animateUnfold(@NonNull final MixedTransition mixed, - @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction startTransaction, - @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback) { - final Transitions.TransitionFinishCallback finishCB = (wct) -> { - mixed.mInFlightSubAnimations--; - if (mixed.mInFlightSubAnimations > 0) return; - mActiveTransitions.remove(mixed); - finishCallback.onTransitionFinished(wct); - }; - mixed.mInFlightSubAnimations = 1; - // Sync pip state. - if (mPipHandler != null) { - mPipHandler.syncPipSurfaceState(info, startTransaction, finishTransaction); - } - if (mSplitHandler != null && mSplitHandler.isSplitActive()) { - mSplitHandler.updateSurfaces(startTransaction); + if (pipHandler != null) { + pipHandler.syncPipSurfaceState(info, startTransaction, finishTransaction); } - return mUnfoldHandler.startAnimation( - mixed.mTransition, info, startTransaction, finishTransaction, finishCB); + return mixed.startSubAnimation(keyguardHandler, info, startTransaction, finishTransaction); } /** Use to when split use intent to enter, check if this enter transition should be mixed or @@ -947,65 +1084,13 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, @NonNull Transitions.TransitionFinishCallback finishCallback) { for (int i = 0; i < mActiveTransitions.size(); ++i) { if (mActiveTransitions.get(i).mTransition != mergeTarget) continue; + MixedTransition mixed = mActiveTransitions.get(i); if (mixed.mInFlightSubAnimations <= 0) { // Already done, so no need to end it. return; } - if (mixed.mType == MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE) { - // queue since no actual animation. - } else if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) { - if (mixed.mAnimType == MixedTransition.ANIM_TYPE_GOING_HOME) { - boolean ended = mSplitHandler.end(); - // If split couldn't end (because it is remote), then don't end everything else - // since we have to play out the animation anyways. - if (!ended) return; - mPipHandler.end(); - if (mixed.mLeftoversHandler != null) { - mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, - finishCallback); - } - } else { - mPipHandler.end(); - } - } else if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) { - mPipHandler.end(); - mActivityEmbeddingController.mergeAnimation(transition, info, t, mergeTarget, - finishCallback); - } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { - mPipHandler.end(); - if (mixed.mLeftoversHandler != null) { - mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, - finishCallback); - } - } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) { - if (mSplitHandler.isPendingEnter(transition)) { - // Recents -> enter-split means that we are switching from one pair to - // another pair. - mixed.mAnimType = MixedTransition.ANIM_TYPE_PAIR_TO_PAIR; - } - mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, - finishCallback); - } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) { - mKeyguardHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); - } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_KEYGUARD) { - if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0) { - handoverTransitionLeashes(mixed, info, t, mixed.mFinishT); - if (animateKeyguard(mixed, info, t, mixed.mFinishT, mixed.mFinishCB)) { - finishCallback.onTransitionFinished(null); - } - } - mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, - finishCallback); - } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_DESKTOP) { - mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, - finishCallback); - } else if (mixed.mType == MixedTransition.TYPE_UNFOLD) { - mUnfoldHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); - } else { - throw new IllegalStateException("Playing a mixed transition with unknown type? " - + mixed.mType); - } + mixed.mergeAnimation(transition, info, t, mergeTarget, finishCallback); } } @@ -1018,46 +1103,30 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, mixed = mActiveTransitions.remove(i); break; } - if (mixed == null) return; - if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) { - mPipHandler.onTransitionConsumed(transition, aborted, finishT); - } else if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) { - mPipHandler.onTransitionConsumed(transition, aborted, finishT); - mActivityEmbeddingController.onTransitionConsumed(transition, aborted, finishT); - } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) { - mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); - } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { - mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); - } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) { - mKeyguardHandler.onTransitionConsumed(transition, aborted, finishT); - } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_DESKTOP) { - mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); - } else if (mixed.mType == MixedTransition.TYPE_UNFOLD) { - mUnfoldHandler.onTransitionConsumed(transition, aborted, finishT); - } - if (mixed.mHasRequestToRemote) { - mPlayer.getRemoteTransitionHandler().onTransitionConsumed(transition, aborted, finishT); + if (mixed != null) { + mixed.onTransitionConsumed(transition, aborted, finishT); } } /** - * Update an incoming {@link TransitionInfo} with the leashes from an ongoing - * {@link MixedTransition} so that it can take over some parts of the animation without + * Update an incoming {@link TransitionInfo} with the leashes from an existing + * {@link TransitionInfo} so that it can take over some parts of the animation without * reparenting to new transition roots. */ - private static void handoverTransitionLeashes(@NonNull MixedTransition mixed, - @NonNull TransitionInfo info, + private static void handoverTransitionLeashes( + @NonNull TransitionInfo from, + @NonNull TransitionInfo to, @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT) { // Show the roots in case they contain new changes not present in the original transition. - for (int j = info.getRootCount() - 1; j >= 0; --j) { - startT.show(info.getRoot(j).getLeash()); + for (int j = to.getRootCount() - 1; j >= 0; --j) { + startT.show(to.getRoot(j).getLeash()); } // Find all of the leashes from the original transition. Map<WindowContainerToken, TransitionInfo.Change> originalChanges = new ArrayMap<>(); - for (TransitionInfo.Change oldChange : mixed.mInfo.getChanges()) { + for (TransitionInfo.Change oldChange : from.getChanges()) { if (oldChange.getContainer() != null) { originalChanges.put(oldChange.getContainer(), oldChange); } @@ -1065,9 +1134,10 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, // Merge the animation leashes by re-using the original ones if we see the same container // in the new transition and the old. - for (TransitionInfo.Change newChange : info.getChanges()) { + for (TransitionInfo.Change newChange : to.getChanges()) { if (originalChanges.containsKey(newChange.getContainer())) { - final TransitionInfo.Change oldChange = originalChanges.get(newChange.getContainer()); + final TransitionInfo.Change oldChange = originalChanges.get( + newChange.getContainer()); startT.reparent(newChange.getLeash(), null); newChange.setLeash(oldChange.getLeash()); } diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt index 182a9089d040..be771712834f 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt @@ -101,7 +101,8 @@ abstract class AppsEnterPipTransition(flicker: LegacyFlickerTest) : EnterPipTran override fun pipLayerReduces() { flicker.assertLayers { val pipLayerList = - this.layers { standardAppHelper.layerMatchesAnyOf(it) && it.isVisible } + this.layers { standardAppHelper.packageNameMatcher.layerMatchesAnyOf(it) + && it.isVisible } pipLayerList.zipWithNext { previous, current -> current.visibleRegion.notBiggerThan(previous.visibleRegion.region) } diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 2f28363aedc7..77800a305f02 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -31,6 +31,12 @@ license { ], } +cc_aconfig_library { + name: "backup_flags_cc_lib", + host_supported: true, + aconfig_declarations: "backup_flags", +} + cc_defaults { name: "libandroidfw_defaults", cpp_std: "gnu++2b", @@ -115,7 +121,10 @@ cc_library { "libutils", "libz", ], - static_libs: ["libziparchive_for_incfs"], + static_libs: [ + "libziparchive_for_incfs", + "backup_flags_cc_lib", + ], static: { enabled: false, }, diff --git a/libs/androidfw/BackupHelpers.cpp b/libs/androidfw/BackupHelpers.cpp index 1a6a952492f6..a1e7c2ffc1b1 100644 --- a/libs/androidfw/BackupHelpers.cpp +++ b/libs/androidfw/BackupHelpers.cpp @@ -36,6 +36,9 @@ #include <utils/KeyedVector.h> #include <utils/String8.h> +#include <com_android_server_backup.h> +namespace backup_flags = com::android::server::backup; + namespace android { #define MAGIC0 0x70616e53 // Snap @@ -214,7 +217,7 @@ write_update_file(BackupDataWriter* dataStream, int fd, int mode, const String8& { LOGP("write_update_file %s (%s) : mode 0%o\n", realFilename, key.c_str(), mode); - const int bufsize = 4*1024; + const int bufsize = backup_flags::enable_max_size_writes_to_pipes() ? (64*1024) : (4*1024); int err; int amt; int fileSize; @@ -550,7 +553,8 @@ int write_tarfile(const String8& packageName, const String8& domain, } // read/write up to this much at a time. - const size_t BUFSIZE = 32 * 1024; + const size_t BUFSIZE = backup_flags::enable_max_size_writes_to_pipes() ? (64*1024) : (32*1024); + char* buf = (char *)calloc(1,BUFSIZE); const size_t PAXHEADER_OFFSET = 512; const size_t PAXHEADER_SIZE = 512; @@ -726,7 +730,7 @@ done: -#define RESTORE_BUF_SIZE (8*1024) +const size_t RESTORE_BUF_SIZE = backup_flags::enable_max_size_writes_to_pipes() ? 64*1024 : 8*1024; RestoreHelperBase::RestoreHelperBase() { diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig index c156c46a5a9b..72ddeccd26b2 100644 --- a/libs/hwui/aconfig/hwui_flags.aconfig +++ b/libs/hwui/aconfig/hwui_flags.aconfig @@ -22,6 +22,13 @@ flag { } flag { + name: "high_contrast_text_small_text_rect" + namespace: "accessibility" + description: "Draw a solid rectangle background behind text instead of a stroke outline" + bug: "186567103" +} + +flag { name: "hdr_10bit_plus" namespace: "core_graphics" description: "Use 10101010 and FP16 formats for HDR-UI when available" diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp index 80b6c0385fca..e9f4b81c7624 100644 --- a/libs/hwui/hwui/Canvas.cpp +++ b/libs/hwui/hwui/Canvas.cpp @@ -18,6 +18,7 @@ #include <SkFontMetrics.h> #include <SkRRect.h> +#include <minikin/MinikinRect.h> #include "FeatureFlags.h" #include "MinikinUtils.h" @@ -107,7 +108,13 @@ void Canvas::drawText(const uint16_t* text, int textSize, int start, int count, // care of all alignment. paint.setTextAlign(Paint::kLeft_Align); - DrawTextFunctor f(layout, this, paint, x, y, layout.getAdvance()); + minikin::MinikinRect bounds; + // We only need the bounds to draw a rectangular background in high contrast mode. Let's save + // the cycles otherwise. + if (flags::high_contrast_text_small_text_rect() && isHighContrastText()) { + MinikinUtils::getBounds(&paint, bidiFlags, typeface, text, textSize, &bounds); + } + DrawTextFunctor f(layout, this, paint, x, y, layout.getAdvance(), bounds); MinikinUtils::forFontRun(layout, &paint, f); if (text_feature::fix_double_underline()) { diff --git a/libs/hwui/hwui/DrawTextFunctor.h b/libs/hwui/hwui/DrawTextFunctor.h index 8f999904a8ab..ba6543988a7b 100644 --- a/libs/hwui/hwui/DrawTextFunctor.h +++ b/libs/hwui/hwui/DrawTextFunctor.h @@ -33,6 +33,8 @@ namespace flags = com::android::graphics::hwui::flags; namespace android { +inline constexpr int kHighContrastTextBorderWidth = 4; + static inline void drawStroke(SkScalar left, SkScalar right, SkScalar top, SkScalar thickness, const Paint& paint, Canvas* canvas) { const SkScalar strokeWidth = fmax(thickness, 1.0f); @@ -45,15 +47,26 @@ static void simplifyPaint(int color, Paint* paint) { paint->setShader(nullptr); paint->setColorFilter(nullptr); paint->setLooper(nullptr); - paint->setStrokeWidth(4 + 0.04 * paint->getSkFont().getSize()); + paint->setStrokeWidth(kHighContrastTextBorderWidth + 0.04 * paint->getSkFont().getSize()); paint->setStrokeJoin(SkPaint::kRound_Join); paint->setLooper(nullptr); } class DrawTextFunctor { public: + /** + * Creates a Functor to draw the given text layout. + * + * @param layout + * @param canvas + * @param paint + * @param x + * @param y + * @param totalAdvance + * @param bounds bounds of the text. Only required if high contrast text mode is enabled. + */ DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas, const Paint& paint, float x, - float y, float totalAdvance) + float y, float totalAdvance, const minikin::MinikinRect& bounds) : layout(layout) , canvas(canvas) , paint(paint) @@ -61,7 +74,8 @@ public: , y(y) , totalAdvance(totalAdvance) , underlinePosition(0) - , underlineThickness(0) {} + , underlineThickness(0) + , bounds(bounds) {} void operator()(size_t start, size_t end) { auto glyphFunc = [&](uint16_t* text, float* positions) { @@ -91,7 +105,16 @@ public: Paint outlinePaint(paint); simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint); outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style); - canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance); + if (flags::high_contrast_text_small_text_rect()) { + auto bgBounds(bounds); + auto padding = kHighContrastTextBorderWidth + 0.1f * paint.getSkFont().getSize(); + bgBounds.offset(x, y); + canvas->drawRect(bgBounds.mLeft - padding, bgBounds.mTop - padding, + bgBounds.mRight + padding, bgBounds.mBottom + padding, + outlinePaint); + } else { + canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance); + } // inner gDrawTextBlobMode = DrawTextBlobMode::HctInner; @@ -146,6 +169,7 @@ private: float totalAdvance; float underlinePosition; float underlineThickness; + const minikin::MinikinRect& bounds; }; } // namespace android diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp index 7552b56d2ad6..833069f363c8 100644 --- a/libs/hwui/hwui/MinikinUtils.cpp +++ b/libs/hwui/hwui/MinikinUtils.cpp @@ -96,7 +96,7 @@ void MinikinUtils::getBounds(const Paint* paint, minikin::Bidi bidiFlags, const float MinikinUtils::measureText(const Paint* paint, minikin::Bidi bidiFlags, const Typeface* typeface, const uint16_t* buf, size_t start, size_t count, size_t bufSize, float* advances, - minikin::MinikinRect* bounds) { + minikin::MinikinRect* bounds, uint32_t* clusterCount) { minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface); const minikin::U16StringPiece textBuf(buf, bufSize); const minikin::Range range(start, start + count); @@ -104,7 +104,7 @@ float MinikinUtils::measureText(const Paint* paint, minikin::Bidi bidiFlags, const minikin::EndHyphenEdit endHyphen = paint->getEndHyphenEdit(); return minikin::Layout::measureText(textBuf, range, bidiFlags, minikinPaint, startHyphen, - endHyphen, advances, bounds); + endHyphen, advances, bounds, clusterCount); } minikin::MinikinExtent MinikinUtils::getFontExtent(const Paint* paint, minikin::Bidi bidiFlags, diff --git a/libs/hwui/hwui/MinikinUtils.h b/libs/hwui/hwui/MinikinUtils.h index 61bc881faa54..f8574ee50525 100644 --- a/libs/hwui/hwui/MinikinUtils.h +++ b/libs/hwui/hwui/MinikinUtils.h @@ -53,7 +53,7 @@ public: static float measureText(const Paint* paint, minikin::Bidi bidiFlags, const Typeface* typeface, const uint16_t* buf, size_t start, size_t count, size_t bufSize, - float* advances, minikin::MinikinRect* bounds); + float* advances, minikin::MinikinRect* bounds, uint32_t* clusterCount); static minikin::MinikinExtent getFontExtent(const Paint* paint, minikin::Bidi bidiFlags, const Typeface* typeface, const uint16_t* buf, diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp index 7cc48661619a..8315c4c0dd4d 100644 --- a/libs/hwui/jni/Graphics.cpp +++ b/libs/hwui/jni/Graphics.cpp @@ -247,6 +247,9 @@ static jfieldID gFontMetricsInt_descent; static jfieldID gFontMetricsInt_bottom; static jfieldID gFontMetricsInt_leading; +static jclass gRunInfo_class; +static jfieldID gRunInfo_clusterCount; + /////////////////////////////////////////////////////////////////////////////// void GraphicsJNI::get_jrect(JNIEnv* env, jobject obj, int* L, int* T, int* R, int* B) @@ -511,6 +514,10 @@ int GraphicsJNI::set_metrics_int(JNIEnv* env, jobject metrics, const SkFontMetri return descent - ascent + leading; } +void GraphicsJNI::set_cluster_count_to_run_info(JNIEnv* env, jobject runInfo, jint clusterCount) { + env->SetIntField(runInfo, gRunInfo_clusterCount, clusterCount); +} + /////////////////////////////////////////////////////////////////////////////////////////// jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, BitmapRegionDecoderWrapper* bitmap) { @@ -834,5 +841,10 @@ int register_android_graphics_Graphics(JNIEnv* env) gFontMetricsInt_bottom = GetFieldIDOrDie(env, gFontMetricsInt_class, "bottom", "I"); gFontMetricsInt_leading = GetFieldIDOrDie(env, gFontMetricsInt_class, "leading", "I"); + gRunInfo_class = FindClassOrDie(env, "android/graphics/Paint$RunInfo"); + gRunInfo_class = MakeGlobalRefOrDie(env, gRunInfo_class); + + gRunInfo_clusterCount = GetFieldIDOrDie(env, gRunInfo_class, "mClusterCount", "I"); + return 0; } diff --git a/libs/hwui/jni/GraphicsJNI.h b/libs/hwui/jni/GraphicsJNI.h index b9fff36d372e..b0a1074d6693 100644 --- a/libs/hwui/jni/GraphicsJNI.h +++ b/libs/hwui/jni/GraphicsJNI.h @@ -77,6 +77,8 @@ public: static SkRect* jrect_to_rect(JNIEnv*, jobject jrect, SkRect*); static void rect_to_jrectf(const SkRect&, JNIEnv*, jobject jrectf); + static void set_cluster_count_to_run_info(JNIEnv* env, jobject runInfo, jint clusterCount); + static void set_jpoint(JNIEnv*, jobject jrect, int x, int y); static SkIPoint* jpoint_to_ipoint(JNIEnv*, jobject jpoint, SkIPoint* point); diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp index d84b73d1a1ca..58d9d8b9def3 100644 --- a/libs/hwui/jni/Paint.cpp +++ b/libs/hwui/jni/Paint.cpp @@ -114,7 +114,7 @@ namespace PaintGlue { std::unique_ptr<float[]> advancesArray(new float[count]); MinikinUtils::measureText(&paint, static_cast<minikin::Bidi>(bidiFlags), typeface, text, 0, - count, count, advancesArray.get(), nullptr); + count, count, advancesArray.get(), nullptr, nullptr); for (int i = 0; i < count; i++) { // traverse in the given direction @@ -206,7 +206,7 @@ namespace PaintGlue { } const float advance = MinikinUtils::measureText( paint, static_cast<minikin::Bidi>(bidiFlags), typeface, text, start, count, - contextCount, advancesArray.get(), nullptr); + contextCount, advancesArray.get(), nullptr, nullptr); if (advances) { env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray.get()); } @@ -244,7 +244,7 @@ namespace PaintGlue { minikin::Bidi bidiFlags = dir == 1 ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR; std::unique_ptr<float[]> advancesArray(new float[count]); MinikinUtils::measureText(paint, bidiFlags, typeface, text, start, count, start + count, - advancesArray.get(), nullptr); + advancesArray.get(), nullptr, nullptr); size_t result = minikin::GraphemeBreak::getTextRunCursor(advancesArray.get(), text, start, count, offset, moveOpt); return static_cast<jint>(result); @@ -508,7 +508,7 @@ namespace PaintGlue { static jfloat doRunAdvance(JNIEnv* env, const Paint* paint, const Typeface* typeface, const jchar buf[], jint start, jint count, jint bufSize, jboolean isRtl, jint offset, jfloatArray advances, - jint advancesIndex, SkRect* drawBounds) { + jint advancesIndex, SkRect* drawBounds, uint32_t* clusterCount) { if (advances) { size_t advancesLength = env->GetArrayLength(advances); if ((size_t)(count + advancesIndex) > advancesLength) { @@ -519,9 +519,9 @@ namespace PaintGlue { minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR; minikin::MinikinRect bounds; if (offset == start + count && advances == nullptr) { - float result = - MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, - bufSize, nullptr, drawBounds ? &bounds : nullptr); + float result = MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, + bufSize, nullptr, + drawBounds ? &bounds : nullptr, clusterCount); if (drawBounds) { copyMinikinRectToSkRect(bounds, drawBounds); } @@ -529,7 +529,8 @@ namespace PaintGlue { } std::unique_ptr<float[]> advancesArray(new float[count]); MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, bufSize, - advancesArray.get(), drawBounds ? &bounds : nullptr); + advancesArray.get(), drawBounds ? &bounds : nullptr, + clusterCount); if (drawBounds) { copyMinikinRectToSkRect(bounds, drawBounds); @@ -549,7 +550,7 @@ namespace PaintGlue { ScopedCharArrayRO textArray(env, text); jfloat result = doRunAdvance(env, paint, typeface, textArray.get() + contextStart, start - contextStart, end - start, contextEnd - contextStart, - isRtl, offset - contextStart, nullptr, 0, nullptr); + isRtl, offset - contextStart, nullptr, 0, nullptr, nullptr); return result; } @@ -558,27 +559,41 @@ namespace PaintGlue { jint contextStart, jint contextEnd, jboolean isRtl, jint offset, jfloatArray advances, jint advancesIndex, - jobject drawBounds) { + jobject drawBounds, jobject runInfo) { const Paint* paint = reinterpret_cast<Paint*>(paintHandle); const Typeface* typeface = paint->getAndroidTypeface(); ScopedCharArrayRO textArray(env, text); SkRect skDrawBounds; + uint32_t clusterCount = 0; jfloat result = doRunAdvance(env, paint, typeface, textArray.get() + contextStart, start - contextStart, end - start, contextEnd - contextStart, isRtl, offset - contextStart, advances, advancesIndex, - drawBounds ? &skDrawBounds : nullptr); + drawBounds ? &skDrawBounds : nullptr, &clusterCount); if (drawBounds != nullptr) { GraphicsJNI::rect_to_jrectf(skDrawBounds, env, drawBounds); } + if (runInfo) { + GraphicsJNI::set_cluster_count_to_run_info(env, runInfo, clusterCount); + } return result; } + // This method is kept for old Robolectric JNI signature used by SystemUIGoogleRoboRNGTests. + static jfloat getRunCharacterAdvance___CIIIIZI_FI_F_ForRobolectric( + JNIEnv* env, jclass cls, jlong paintHandle, jcharArray text, jint start, jint end, + jint contextStart, jint contextEnd, jboolean isRtl, jint offset, jfloatArray advances, + jint advancesIndex, jobject drawBounds) { + return getRunCharacterAdvance___CIIIIZI_FI_F(env, cls, paintHandle, text, start, end, + contextStart, contextEnd, isRtl, offset, + advances, advancesIndex, drawBounds, nullptr); + } + static jint doOffsetForAdvance(const Paint* paint, const Typeface* typeface, const jchar buf[], jint start, jint count, jint bufSize, jboolean isRtl, jfloat advance) { minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR; std::unique_ptr<float[]> advancesArray(new float[count]); MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, bufSize, - advancesArray.get(), nullptr); + advancesArray.get(), nullptr, nullptr); return minikin::getOffsetForAdvance(advancesArray.get(), buf, start, count, advance); } @@ -1145,8 +1160,11 @@ static const JNINativeMethod methods[] = { (void*)PaintGlue::getCharArrayBounds}, {"nHasGlyph", "(JILjava/lang/String;)Z", (void*)PaintGlue::hasGlyph}, {"nGetRunAdvance", "(J[CIIIIZI)F", (void*)PaintGlue::getRunAdvance___CIIIIZI_F}, - {"nGetRunCharacterAdvance", "(J[CIIIIZI[FILandroid/graphics/RectF;)F", + {"nGetRunCharacterAdvance", + "(J[CIIIIZI[FILandroid/graphics/RectF;Landroid/graphics/Paint$RunInfo;)F", (void*)PaintGlue::getRunCharacterAdvance___CIIIIZI_FI_F}, + {"nGetRunCharacterAdvance", "(J[CIIIIZI[FILandroid/graphics/RectF;)F", + (void*)PaintGlue::getRunCharacterAdvance___CIIIIZI_FI_F_ForRobolectric}, {"nGetOffsetForAdvance", "(J[CIIIIZF)I", (void*)PaintGlue::getOffsetForAdvance___CIIIIZF_I}, {"nGetFontMetricsIntForText", "(J[CIIIIZLandroid/graphics/Paint$FontMetricsInt;)V", (void*)PaintGlue::getFontMetricsIntForText___C}, diff --git a/libs/hwui/tests/unit/UnderlineTest.cpp b/libs/hwui/tests/unit/UnderlineTest.cpp index c70a30477ecf..9911bfa70443 100644 --- a/libs/hwui/tests/unit/UnderlineTest.cpp +++ b/libs/hwui/tests/unit/UnderlineTest.cpp @@ -103,8 +103,9 @@ DrawTextFunctor processFunctor(const std::vector<uint16_t>& text, Paint* paint) // Create minikin::Layout std::unique_ptr<Typeface> typeface(makeTypeface()); minikin::Layout layout = doLayout(text, *paint, typeface.get()); + minikin::MinikinRect bounds; - DrawTextFunctor f(layout, &canvas, *paint, 0, 0, layout.getAdvance()); + DrawTextFunctor f(layout, &canvas, *paint, 0, 0, layout.getAdvance(), bounds); MinikinUtils::forFontRun(layout, paint, f); return f; } diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java index ee2510ff9695..0d5af50d08b5 100644 --- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java +++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java @@ -19,6 +19,7 @@ package com.android.internal.location; import android.Manifest; import android.annotation.RequiresPermission; import android.content.Context; +import android.content.pm.PackageManager; import android.location.LocationManager; import android.os.SystemClock; import android.telephony.TelephonyCallback; @@ -26,6 +27,8 @@ import android.telephony.TelephonyManager; import android.telephony.emergency.EmergencyNumber; import android.util.Log; +import com.android.internal.telephony.flags.Flags; + import java.util.concurrent.TimeUnit; /** @@ -139,8 +142,20 @@ public class GpsNetInitiatedHandler { (mCallEndElapsedRealtimeMillis > 0) && ((SystemClock.elapsedRealtime() - mCallEndElapsedRealtimeMillis) < emergencyExtensionMillis); - boolean isInEmergencyCallback = mTelephonyManager.getEmergencyCallbackMode(); - boolean isInEmergencySmsMode = mTelephonyManager.isInEmergencySmsMode(); + boolean isInEmergencyCallback = false; + boolean isInEmergencySmsMode = false; + if (!Flags.enforceTelephonyFeatureMappingForPublicApis()) { + isInEmergencyCallback = mTelephonyManager.getEmergencyCallbackMode(); + isInEmergencySmsMode = mTelephonyManager.isInEmergencySmsMode(); + } else { + PackageManager pm = mContext.getPackageManager(); + if (pm != null && pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING)) { + isInEmergencyCallback = mTelephonyManager.getEmergencyCallbackMode(); + } + if (pm != null && pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)) { + isInEmergencySmsMode = mTelephonyManager.isInEmergencySmsMode(); + } + } return mIsInEmergencyCall || isInEmergencyCallback || isInEmergencyExtension || isInEmergencySmsMode; } diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index 89792c7638cb..8f8cd0b44220 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -48,6 +48,7 @@ import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -141,7 +142,9 @@ public final class MediaRouter2 { * dispatch. This is only used to determine what callback a route should be assigned to (added, * removed, changed) in {@link #dispatchFilteredRoutesUpdatedOnHandler(List)}. */ - private volatile ArrayMap<String, MediaRoute2Info> mPreviousRoutes = new ArrayMap<>(); + private volatile ArrayMap<String, MediaRoute2Info> mPreviousFilteredRoutes = new ArrayMap<>(); + + private final Map<String, MediaRoute2Info> mPreviousUnfilteredRoutes = new ArrayMap<>(); /** * Stores the latest copy of exposed routes after filtering, sorting, and deduplication. Can be @@ -282,6 +285,8 @@ public final class MediaRouter2 { MediaRouter2 instance = sAppToProxyRouterMap.get(key); if (instance == null) { instance = new MediaRouter2(context, looper, clientPackageName, user); + // Register proxy router after instantiation to avoid race condition. + ((ProxyMediaRouter2Impl) instance.mImpl).registerProxyRouter(); sAppToProxyRouterMap.put(key, instance); } return instance; @@ -368,6 +373,7 @@ public final class MediaRouter2 { new SystemRoutingController( ProxyMediaRouter2Impl.getSystemSessionInfoImpl( mMediaRouterService, clientPackageName)); + mImpl = new ProxyMediaRouter2Impl(context, clientPackageName, user); } @@ -883,7 +889,7 @@ public final class MediaRouter2 { newRoutes.stream().map(MediaRoute2Info::getId).collect(Collectors.toSet()); for (MediaRoute2Info route : newRoutes) { - MediaRoute2Info prevRoute = mPreviousRoutes.get(route.getId()); + MediaRoute2Info prevRoute = mPreviousFilteredRoutes.get(route.getId()); if (prevRoute == null) { addedRoutes.add(route); } else if (!prevRoute.equals(route)) { @@ -891,21 +897,21 @@ public final class MediaRouter2 { } } - for (int i = 0; i < mPreviousRoutes.size(); i++) { - if (!newRouteIds.contains(mPreviousRoutes.keyAt(i))) { - removedRoutes.add(mPreviousRoutes.valueAt(i)); + for (int i = 0; i < mPreviousFilteredRoutes.size(); i++) { + if (!newRouteIds.contains(mPreviousFilteredRoutes.keyAt(i))) { + removedRoutes.add(mPreviousFilteredRoutes.valueAt(i)); } } // update previous routes for (MediaRoute2Info route : removedRoutes) { - mPreviousRoutes.remove(route.getId()); + mPreviousFilteredRoutes.remove(route.getId()); } for (MediaRoute2Info route : addedRoutes) { - mPreviousRoutes.put(route.getId(), route); + mPreviousFilteredRoutes.put(route.getId(), route); } for (MediaRoute2Info route : changedRoutes) { - mPreviousRoutes.put(route.getId(), route); + mPreviousFilteredRoutes.put(route.getId(), route); } if (!addedRoutes.isEmpty()) { @@ -924,6 +930,27 @@ public final class MediaRouter2 { } } + void dispatchControllerUpdatedIfNeededOnHandler(Map<String, MediaRoute2Info> routesMap) { + List<RoutingController> controllers = getControllers(); + for (RoutingController controller : controllers) { + + for (String selectedRoute : controller.getRoutingSessionInfo().getSelectedRoutes()) { + if (routesMap.containsKey(selectedRoute) + && mPreviousUnfilteredRoutes.containsKey(selectedRoute)) { + MediaRoute2Info currentRoute = routesMap.get(selectedRoute); + MediaRoute2Info oldRoute = mPreviousUnfilteredRoutes.get(selectedRoute); + if (!currentRoute.equals(oldRoute)) { + notifyControllerUpdated(controller); + break; + } + } + } + } + + mPreviousUnfilteredRoutes.clear(); + mPreviousUnfilteredRoutes.putAll(routesMap); + } + void updateRoutesOnHandler(List<MediaRoute2Info> newRoutes) { synchronized (mLock) { mRoutes.clear(); @@ -945,6 +972,11 @@ public final class MediaRouter2 { MediaRouter2::dispatchFilteredRoutesUpdatedOnHandler, this, mFilteredRoutes)); + mHandler.sendMessage( + obtainMessage( + MediaRouter2::dispatchControllerUpdatedIfNeededOnHandler, + this, + new HashMap<>(mRoutes))); } /** @@ -2153,18 +2185,19 @@ public final class MediaRouter2 { mClientUser = user; mClientPackageName = clientPackageName; mClient = new Client(); + mDiscoveryPreference = RouteDiscoveryPreference.EMPTY; + } + public void registerProxyRouter() { try { mMediaRouterService.registerProxyRouter( mClient, - context.getApplicationContext().getPackageName(), - clientPackageName, - user); + mContext.getApplicationContext().getPackageName(), + mClientPackageName, + mClientUser); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } - - mDiscoveryPreference = RouteDiscoveryPreference.EMPTY; } @Override diff --git a/media/java/android/media/projection/IMediaProjectionManager.aidl b/media/java/android/media/projection/IMediaProjectionManager.aidl index a7ec6c692416..8ce1b6d5264d 100644 --- a/media/java/android/media/projection/IMediaProjectionManager.aidl +++ b/media/java/android/media/projection/IMediaProjectionManager.aidl @@ -37,31 +37,41 @@ interface IMediaProjectionManager { const String EXTRA_PACKAGE_REUSING_GRANTED_CONSENT = "extra_media_projection_package_reusing_consent"; + /** + * Returns whether a combination of process UID and package has the projection permission. + * + * @param processUid the process UID as returned by {@link android.os.Process.myUid()}. + */ @UnsupportedAppUsage - boolean hasProjectionPermission(int uid, String packageName); + boolean hasProjectionPermission(int processUid, String packageName); /** * Returns a new {@link IMediaProjection} instance associated with the given package. + * + * @param processUid the process UID as returned by {@link android.os.Process.myUid()}. */ @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") - IMediaProjection createProjection(int uid, String packageName, int type, + IMediaProjection createProjection(int processUid, String packageName, int type, boolean permanentGrant); /** * Returns the current {@link IMediaProjection} instance associated with the given - * package, or {@code null} if it is not possible to re-use the current projection. + * package and process UID, or {@code null} if it is not possible to re-use the current + * projection. * * <p>Should only be invoked when the user has reviewed consent for a re-used projection token. * Requires that there is a prior session waiting for the user to review consent, and the given * package details match those on the current projection. * * @see {@link #isCurrentProjection} + * + * @param processUid the process UID as returned by {@link android.os.Process.myUid()}. */ @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") - IMediaProjection getProjection(int uid, String packageName); + IMediaProjection getProjection(int processUid, String packageName); /** * Returns {@code true} if the given {@link IMediaProjection} corresponds to the current @@ -162,8 +172,8 @@ interface IMediaProjectionManager { * * <p>Only used for emitting atoms. * - * @param hostUid The uid of the process requesting consent to capture, may be an app or - * SystemUI. + * @param hostProcessUid The uid of the process requesting consent to capture, may be an + * app or SystemUI. * @param sessionCreationSource Only set if the state is MEDIA_PROJECTION_STATE_INITIATED. * Indicates the entry point for requesting the permission. Must be * a valid state defined @@ -172,49 +182,49 @@ interface IMediaProjectionManager { @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") - oneway void notifyPermissionRequestInitiated(int hostUid, int sessionCreationSource); + oneway void notifyPermissionRequestInitiated(int hostProcessUid, int sessionCreationSource); /** * Notifies system server that the permission request was displayed. * * <p>Only used for emitting atoms. * - * @param hostUid The uid of the process requesting consent to capture, may be an app or - * SystemUI. + * @param hostProcessUid The uid of the process requesting consent to capture, may be an app or + * SystemUI. */ @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") - oneway void notifyPermissionRequestDisplayed(int hostUid); + oneway void notifyPermissionRequestDisplayed(int hostProcessUid); /** * Notifies system server that the permission request was cancelled. * * <p>Only used for emitting atoms. * - * @param hostUid The uid of the process requesting consent to capture, may be an app or - * SystemUI. + * @param hostProcessUid The uid of the process requesting consent to capture, may be an app or + * SystemUI. */ @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") - oneway void notifyPermissionRequestCancelled(int hostUid); + oneway void notifyPermissionRequestCancelled(int hostProcessUid); /** * Notifies system server that the app selector was displayed. * * <p>Only used for emitting atoms. * - * @param hostUid The uid of the process requesting consent to capture, may be an app or - * SystemUI. + * @param hostProcessUid The uid of the process requesting consent to capture, may be an app or + * SystemUI. */ @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") - oneway void notifyAppSelectorDisplayed(int hostUid); + oneway void notifyAppSelectorDisplayed(int hostProcessUid); @EnforcePermission("MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") - void notifyWindowingModeChanged(int contentToRecord, int targetUid, int windowingMode); + void notifyWindowingModeChanged(int contentToRecord, int targetProcessUid, int windowingMode); } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt index 8ac364e72fef..b2c23a401117 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt @@ -69,7 +69,8 @@ class CredentialAutofillService : AutofillService() { companion object { private const val TAG = "CredAutofill" - private const val SESSION_ID_KEY = "session_id" + private const val SESSION_ID_KEY = "autofill_session_id" + private const val REQUEST_ID_KEY = "autofill_request_id" private const val CRED_HINT_PREFIX = "credential=" private const val REQUEST_DATA_KEY = "requestData" private const val CANDIDATE_DATA_KEY = "candidateQueryData" @@ -97,16 +98,23 @@ class CredentialAutofillService : AutofillService() { val callingPackage = structure.activityComponent.packageName Log.i(TAG, "onFillCredentialRequest called for $callingPackage") - var sessionId = request.clientState?.getInt(SESSION_ID_KEY) - - Log.i(TAG, "Autofill sessionId: " + sessionId) - if (sessionId == null) { - Log.i(TAG, "Session Id not found") - callback.onFailure("Session Id not found") + val clientState = request.clientState + if (clientState == null) { + Log.i(TAG, "Client state not found") + callback.onFailure("Client state not found") + return + } + val sessionId = clientState.getInt(SESSION_ID_KEY) + val requestId = clientState.getInt(REQUEST_ID_KEY) + Log.i(TAG, "Autofill sessionId: $sessionId, autofill requestId: $requestId") + if (sessionId == 0 || requestId == 0) { + Log.i(TAG, "Session Id or request Id not found") + callback.onFailure("Session Id or request Id not found") return } - val getCredRequest: GetCredentialRequest? = getCredManRequest(structure) + val getCredRequest: GetCredentialRequest? = getCredManRequest(structure, sessionId, + requestId) if (getCredRequest == null) { Log.i(TAG, "No credential manager request found") callback.onFailure("No credential manager request found") @@ -515,12 +523,19 @@ class CredentialAutofillService : AutofillService() { TODO("Not yet implemented") } - private fun getCredManRequest(structure: AssistStructure): GetCredentialRequest? { + private fun getCredManRequest( + structure: AssistStructure, + sessionId: Int, + requestId: Int + ): GetCredentialRequest? { val credentialOptions: MutableList<CredentialOption> = mutableListOf() traverseStructure(structure, credentialOptions) if (credentialOptions.isNotEmpty()) { - return GetCredentialRequest.Builder(Bundle.EMPTY) + val dataBundle = Bundle() + dataBundle.putInt(SESSION_ID_KEY, sessionId) + dataBundle.putInt(REQUEST_ID_KEY, requestId) + return GetCredentialRequest.Builder(dataBundle) .setCredentialOptions(credentialOptions) .build() } diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java index 170cb4546d0c..9ad3e3c0af0f 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java @@ -91,7 +91,8 @@ public class UninstallerActivity extends Activity { // be stale, if e.g. the app was uninstalled while the activity was destroyed. super.onCreate(null); - if (usePiaV2() && !isTv()) { + // TODO(b/318521110) Enable PIA v2 for archive dialog. + if (usePiaV2() && !isTv() && !isArchiveDialog(getIntent())) { Log.i(TAG, "Using Pia V2"); boolean returnResult = getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false); @@ -224,6 +225,11 @@ public class UninstallerActivity extends Activity { showConfirmationDialog(); } + private boolean isArchiveDialog(Intent intent) { + return (intent.getIntExtra(PackageInstaller.EXTRA_DELETE_FLAGS, 0) + & PackageManager.DELETE_ARCHIVE) != 0; + } + /** * Parses specific {@link android.content.pm.PackageManager.DeleteFlags} from {@link Intent} * to archive an app if requested. diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index ffe3d1d633d7..8b16d64bcfe7 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -62,6 +62,7 @@ android_library { ], lint: { baseline_filename: "lint-baseline.xml", + extra_check_modules: ["SettingsLibLintChecker"], }, } diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java index 8e5396fef744..d9024575f247 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java @@ -19,6 +19,7 @@ package com.android.settingslib; import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE; import static android.app.admin.DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY; import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER; +import static android.app.role.RoleManager.ROLE_FINANCED_DEVICE_KIOSK; import static com.android.settingslib.Utils.getColorAttrDefaultColor; @@ -27,6 +28,7 @@ import android.annotation.UserIdInt; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.admin.DevicePolicyManager; +import android.app.role.RoleManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -70,6 +72,10 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG); private static final Set<String> ECM_KEYS = new ArraySet<>(); + // TODO(b/281701062): reference role name from role manager once its exposed. + private static final String ROLE_DEVICE_LOCK_CONTROLLER = + "android.app.role.SYSTEM_FINANCED_DEVICE_CONTROLLER"; + static { if (android.security.Flags.extendEcmToAllSettings()) { ECM_KEYS.add(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW); @@ -476,16 +482,27 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { } /** - * Check if {@param packageName} is restricted by the profile or device owner from using - * metered data. + * Check if user control over metered data usage of {@code packageName} is disabled by the + * profile or device owner. * * @return EnforcedAdmin object containing the enforced admin component and admin user details, - * or {@code null} if the {@param packageName} is not restricted. + * or {@code null} if the user control is not disabled. */ - public static EnforcedAdmin checkIfMeteredDataRestricted(Context context, + public static EnforcedAdmin checkIfMeteredDataUsageUserControlDisabled(Context context, String packageName, int userId) { + RoleManager roleManager = context.getSystemService(RoleManager.class); + UserHandle userHandle = getUserHandleOf(userId); + if (roleManager.getRoleHoldersAsUser(ROLE_FINANCED_DEVICE_KIOSK, userHandle) + .contains(packageName) + || roleManager.getRoleHoldersAsUser(ROLE_DEVICE_LOCK_CONTROLLER, userHandle) + .contains(packageName)) { + // There is no actual device admin for a financed device, but metered data usage + // control should still be disabled for both controller and kiosk apps. + return new EnforcedAdmin(); + } + final EnforcedAdmin enforcedAdmin = getProfileOrDeviceOwner(context, - getUserHandleOf(userId)); + userHandle); if (enforcedAdmin == null) { return null; } diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 1481d97a04fa..d26c5fffd932 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -867,6 +867,7 @@ public class SettingsBackupTest { Settings.Secure.NEARBY_SHARING_SLICE_URI, Settings.Secure.NOTIFIED_NON_ACCESSIBILITY_CATEGORY_SERVICES, Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, + Settings.Secure.PRIVATE_SPACE_AUTO_LOCK, Settings.Secure.RELEASE_COMPRESS_BLOCKS_ON_INSTALL, Settings.Secure.SCREENSAVER_COMPLICATIONS_ENABLED, Settings.Secure.SHOW_QR_CODE_SCANNER_SETTING, diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index d3a89f447d1f..d61ae7eccc42 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -438,6 +438,7 @@ android_robolectric_test { "androidx.core_core-animation-testing", "androidx.test.ext.junit", "inline-mockito-robolectric-prebuilt", + "platform-parametric-runner-lib", ], libs: [ "android.test.runner", diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig index 21263a92ae26..f7b1a26c9df9 100644 --- a/packages/SystemUI/aconfig/accessibility.aconfig +++ b/packages/SystemUI/aconfig/accessibility.aconfig @@ -10,6 +10,13 @@ flag { } flag { + name: "floating_menu_drag_to_hide" + namespace: "accessibility" + description: "Allows users to hide the FAB then use notification to dismiss or bring it back." + bug: "298718415" +} + +flag { name: "floating_menu_ime_displacement_animation" namespace: "accessibility" description: "Adds an animation for when the FAB is displaced by an IME becoming visible." @@ -28,4 +35,4 @@ flag { namespace: "accessibility" description: "Animates the floating menu's transition between curved and jagged edges." bug: "281140482" -}
\ No newline at end of file +} diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 5b218549de43..5fbffbed7965 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -258,6 +258,15 @@ flag { } flag { + name: "centralized_status_bar_dimens_refactor" + namespace: "systemui" + description: "Refactors shade header and keyguard status bar to read status bar dimens from a" + " central place, instead of reading resources directly. This is to take into account display" + " cutouts and other special cases. " + bug: "317199366" +} + +flag { name: "enable_layout_tracing" namespace: "systemui" description: "Enables detailed traversal slices during measure and layout in perfetto traces" diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt index 249b3e14ec72..d47527a0a191 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt @@ -33,11 +33,11 @@ import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneScope import com.android.compose.animation.scene.SceneTransitionLayout -import com.android.compose.animation.scene.SceneTransitionLayoutState import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.SwipeDirection import com.android.compose.animation.scene.observableTransitionState import com.android.compose.animation.scene.transitions +import com.android.compose.animation.scene.updateSceneTransitionLayoutState import com.android.systemui.communal.shared.model.CommunalSceneKey import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel @@ -76,7 +76,13 @@ fun CommunalContainer( viewModel.currentScene .transform { value -> emit(value.toTransitionSceneKey()) } .collectAsState(TransitionSceneKey.Blank) - val sceneTransitionLayoutState = remember { SceneTransitionLayoutState(currentScene) } + val sceneTransitionLayoutState = + updateSceneTransitionLayoutState( + currentScene, + onChangeScene = { viewModel.onSceneChanged(it.toCommunalSceneKey()) }, + transitions = sceneTransitions, + ) + // Don't show hub mode UI if keyguard is present. This is important since we're in the shade, // which can be opened from many locations. val isKeyguardShowing by viewModel.isKeyguardVisible.collectAsState(initial = false) @@ -98,12 +104,9 @@ fun CommunalContainer( Box(modifier = modifier.fillMaxSize()) { SceneTransitionLayout( - modifier = Modifier.fillMaxSize(), - currentScene = currentScene, - onChangeScene = { sceneKey -> viewModel.onSceneChanged(sceneKey.toCommunalSceneKey()) }, - transitions = sceneTransitions, state = sceneTransitionLayoutState, - edgeDetector = FixedSizeEdgeDetector(ContainerDimensions.EdgeSwipeSize) + modifier = Modifier.fillMaxSize(), + edgeDetector = FixedSizeEdgeDetector(ContainerDimensions.EdgeSwipeSize), ) { scene( TransitionSceneKey.Blank, diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt index d20154437e02..d76f0ff3ec18 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt @@ -45,6 +45,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.outlined.Delete +import androidx.compose.material.icons.outlined.TouchApp import androidx.compose.material.icons.outlined.Widgets import androidx.compose.material3.Button import androidx.compose.material3.ButtonColors @@ -76,10 +77,12 @@ import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView +import androidx.compose.ui.window.Popup import com.android.compose.theme.LocalAndroidColorScheme import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.shared.model.CommunalContentSize @@ -97,6 +100,8 @@ fun CommunalHub( onEditDone: (() -> Unit)? = null, ) { val communalContent by viewModel.communalContent.collectAsState(initial = emptyList()) + val isPopupOnDismissCtaShowing by + viewModel.isPopupOnDismissCtaShowing.collectAsState(initial = false) var removeButtonCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) } var toolbarSize: IntSize? by remember { mutableStateOf(null) } var gridCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) } @@ -133,6 +138,10 @@ fun CommunalHub( } } + if (isPopupOnDismissCtaShowing) { + PopupOnDismissCtaTile(viewModel::onHidePopupAfterDismissCta) + } + // This spacer covers the edge of the LazyHorizontalGrid and prevents it from receiving // touches, so that the SceneTransitionLayout can intercept the touches and allow an edge // swipe back to the blank scene. @@ -172,7 +181,7 @@ private fun BoxScope.CommunalHubLazyGrid( gridModifier = gridModifier .fillMaxSize() - .dragContainer(dragDropState, beforeContentPadding(contentPadding)) + .dragContainer(dragDropState, beforeContentPadding(contentPadding), viewModel) .onGloballyPositioned { setGridCoordinates(it) } // for widgets dropped from other activities val dragAndDropTargetState = @@ -319,8 +328,40 @@ private fun Toolbar( } @Composable +private fun PopupOnDismissCtaTile(onHidePopupAfterDismissCta: () -> Unit) { + Popup( + alignment = Alignment.TopCenter, + offset = IntOffset(0, 40), + onDismissRequest = onHidePopupAfterDismissCta + ) { + val colors = LocalAndroidColorScheme.current + Row( + modifier = + Modifier.height(56.dp) + .background(colors.secondary, RoundedCornerShape(50.dp)) + .padding(16.dp), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + imageVector = Icons.Outlined.TouchApp, + contentDescription = stringResource(R.string.popup_on_dismiss_cta_tile_text), + tint = colors.onSecondary, + modifier = Modifier.size(20.dp) + ) + Spacer(modifier = Modifier.size(8.dp)) + Text( + text = stringResource(R.string.popup_on_dismiss_cta_tile_text), + style = MaterialTheme.typography.titleSmall, + color = colors.onSecondary, + ) + } + } +} + +@Composable private fun RemoveButtonContent(spacerModifier: Modifier) { - Icon(Icons.Outlined.Delete, stringResource(R.string.button_to_open_widget_editor)) + Icon(Icons.Outlined.Delete, stringResource(R.string.button_to_remove_widget)) Spacer(spacerModifier) Text( text = stringResource(R.string.button_to_remove_widget), diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt index 1b40de4ef5df..113822167ca7 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt @@ -40,6 +40,7 @@ import androidx.compose.ui.unit.toOffset import androidx.compose.ui.unit.toSize import androidx.compose.ui.zIndex import com.android.systemui.communal.ui.compose.extensions.plus +import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.launch @@ -207,7 +208,8 @@ internal constructor( fun Modifier.dragContainer( dragDropState: GridDragDropState, - beforeContentPadding: ContentPaddingInPx + beforeContentPadding: ContentPaddingInPx, + viewModel: BaseCommunalViewModel, ): Modifier { return pointerInput(dragDropState, beforeContentPadding) { detectDragGesturesAfterLongPress( @@ -220,9 +222,16 @@ fun Modifier.dragContainer( offset, Offset(beforeContentPadding.startPadding, beforeContentPadding.topPadding) ) + viewModel.onReorderWidgetStart() }, - onDragEnd = { dragDropState.onDragInterrupted() }, - onDragCancel = { dragDropState.onDragInterrupted() } + onDragEnd = { + dragDropState.onDragInterrupted() + viewModel.onReorderWidgetEnd() + }, + onDragCancel = { + dragDropState.onDragInterrupted() + viewModel.onReorderWidgetCancel() + } ) } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt index 4eb9089dc589..c35202cd830a 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt @@ -25,7 +25,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier @@ -38,11 +37,11 @@ import com.android.compose.animation.scene.Edge as SceneTransitionEdge import com.android.compose.animation.scene.ObservableTransitionState as SceneTransitionObservableTransitionState import com.android.compose.animation.scene.SceneKey as SceneTransitionSceneKey import com.android.compose.animation.scene.SceneTransitionLayout -import com.android.compose.animation.scene.SceneTransitionLayoutState import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.SwipeDirection import com.android.compose.animation.scene.UserAction as SceneTransitionUserAction import com.android.compose.animation.scene.observableTransitionState +import com.android.compose.animation.scene.updateSceneTransitionLayoutState import com.android.systemui.ribbon.ui.composable.BottomRightCornerRibbon import com.android.systemui.scene.shared.model.Direction import com.android.systemui.scene.shared.model.Edge @@ -82,7 +81,12 @@ fun SceneContainer( val currentScene = checkNotNull(sceneByKey[currentSceneKey]) val currentDestinations: Map<UserAction, SceneModel> by currentScene.destinationScenes.collectAsState() - val state = remember { SceneTransitionLayoutState(currentSceneKey.toTransitionSceneKey()) } + val state = + updateSceneTransitionLayoutState( + currentSceneKey.toTransitionSceneKey(), + onChangeScene = viewModel::onSceneChanged, + transitions = SceneContainerTransitions, + ) DisposableEffect(viewModel, state) { viewModel.setTransitionState(state.observableTransitionState().map { it.toModel() }) @@ -93,9 +97,6 @@ fun SceneContainer( modifier = Modifier.fillMaxSize(), ) { SceneTransitionLayout( - currentScene = currentSceneKey.toTransitionSceneKey(), - onChangeScene = viewModel::onSceneChanged, - transitions = SceneContainerTransitions, state = state, modifier = modifier diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt index ba6d00e3b7f5..7d3b0fbe1725 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt @@ -28,9 +28,9 @@ import kotlinx.coroutines.launch * the currently running transition, if there is one. */ internal fun CoroutineScope.animateToScene( - layoutState: SceneTransitionLayoutStateImpl, + layoutState: BaseSceneTransitionLayoutState, target: SceneKey, -) { +): TransitionState.Transition? { val transitionState = layoutState.transitionState if (transitionState.currentScene == target) { // This can happen in 3 different situations, for which there isn't anything else to do: @@ -41,10 +41,10 @@ internal fun CoroutineScope.animateToScene( // a. didn't release their pointer yet. // b. released their pointer such that the swipe gesture was cancelled and the // transition is currently animating back to [target]. - return + return null } - when (transitionState) { + return when (transitionState) { is TransitionState.Idle -> animate(layoutState, target) is TransitionState.Transition -> { // A transition is currently running: first check whether `transition.toScene` or @@ -62,47 +62,43 @@ internal fun CoroutineScope.animateToScene( // finish the current transition early to make sure that the current state // change is committed. layoutState.finishTransition(transitionState, transitionState.currentScene) + null } else { // The transition is in progress: start the canned animation at the same // progress as it was in. // TODO(b/290184746): Also take the current velocity into account. animate(layoutState, target, startProgress = progress) } - - return - } - - if (transitionState.fromScene == target) { + } else if (transitionState.fromScene == target) { // There is a transition from [target] to another scene: simply animate the same // transition progress to `0`. - check(transitionState.toScene == transitionState.currentScene) + val progress = transitionState.progress if (progress.absoluteValue < ProgressVisibilityThreshold) { // The transition is at progress ~= 0: no need to animate.We finish the current // transition early to make sure that the current state change is committed. layoutState.finishTransition(transitionState, transitionState.currentScene) + null } else { // TODO(b/290184746): Also take the current velocity into account. animate(layoutState, target, startProgress = progress, reversed = true) } - - return + } else { + // Generic interruption; the current transition is neither from or to [target]. + // TODO(b/290930950): Better handle interruptions here. + animate(layoutState, target) } - - // Generic interruption; the current transition is neither from or to [target]. - // TODO(b/290930950): Better handle interruptions here. - animate(layoutState, target) } } } private fun CoroutineScope.animate( - layoutState: SceneTransitionLayoutStateImpl, + layoutState: BaseSceneTransitionLayoutState, target: SceneKey, startProgress: Float = 0f, reversed: Boolean = false, -) { +): TransitionState.Transition { val fromScene = layoutState.transitionState.currentScene val isUserInput = (layoutState.transitionState as? TransitionState.Transition)?.isInitiatedByUserInput @@ -143,10 +139,15 @@ private fun CoroutineScope.animate( } // Animate the progress to its target value. - launch { - animatable.animateTo(targetProgress, animationSpec) - layoutState.finishTransition(transition, target) - } + launch { animatable.animateTo(targetProgress, animationSpec) } + .invokeOnCompletion { + // Settle the state to Idle(target). Note that this will do nothing if this transition + // was replaced/interrupted by another one, and this also runs if this coroutine is + // cancelled, i.e. if [this] coroutine scope is cancelled. + layoutState.finishTransition(transition, target) + } + + return transition } private class OneOffTransition( diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt index 280fbfb7d3d3..a910bca078e8 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt @@ -20,10 +20,10 @@ import androidx.compose.runtime.Stable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshots.SnapshotStateMap import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.geometry.isSpecified import androidx.compose.ui.geometry.isUnspecified import androidx.compose.ui.geometry.lerp import androidx.compose.ui.graphics.drawscope.ContentDrawScope @@ -46,41 +46,18 @@ import kotlinx.coroutines.launch /** An element on screen, that can be composed in one or more scenes. */ @Stable internal class Element(val key: ElementKey) { - /** - * The last state of this element, coming from any scene. Note that this state will be unstable - * if this element is present in multiple scenes but the shared element animation is disabled, - * given that multiple instances of the element with different states will write to this state. - * You should prefer using [SceneState.lastState] in the current scene when it is defined. - */ - val lastSharedState = State() - /** The mapping between a scene and the state this element has in that scene, if any. */ - val sceneStates = mutableMapOf<SceneKey, SceneState>() + // TODO(b/316901148): Make this a normal map instead once we can make sure that new transitions + // are first seen by composition then layout/drawing code. See 316901148#comment2 for details. + val sceneStates = SnapshotStateMap<SceneKey, SceneState>() override fun toString(): String { return "Element(key=$key)" } - /** The state of this element, either in a specific scene or in a shared context. */ - class State { - /** The offset of the element, relative to the SceneTransitionLayout containing it. */ - var offset = Offset.Unspecified - - /** The size of this element. */ - var size = SizeUnspecified - - /** The draw scale of this element. */ - var drawScale = Scale.Default - - /** The alpha of this element. */ - var alpha = AlphaUnspecified - } - /** The last and target state of this element in a given scene. */ @Stable class SceneState(val scene: SceneKey) { - val lastState = State() - var targetSize by mutableStateOf(SizeUnspecified) var targetOffset by mutableStateOf(Offset.Unspecified) @@ -94,7 +71,6 @@ internal class Element(val key: ElementKey) { companion object { val SizeUnspecified = IntSize(Int.MAX_VALUE, Int.MAX_VALUE) - val AlphaUnspecified = Float.MIN_VALUE } } @@ -219,7 +195,7 @@ internal class ElementNode( } override fun ContentDrawScope.draw() { - val drawScale = getDrawScale(layoutImpl, element, scene, sceneState) + val drawScale = getDrawScale(layoutImpl, element, scene) if (drawScale == Scale.Default) { drawContent() } else { @@ -264,7 +240,6 @@ private fun shouldDrawElement( // Always draw the element if there is no ongoing transition or if the element is not shared. if ( transition == null || - !layoutImpl.isTransitionReady(transition) || transition.fromScene !in element.sceneStates || transition.toScene !in element.sceneStates ) { @@ -304,7 +279,7 @@ internal fun shouldDrawOrComposeSharedElement( } private fun isSharedElementEnabled( - layoutState: SceneTransitionLayoutStateImpl, + layoutState: BaseSceneTransitionLayoutState, transition: TransitionState.Transition, element: ElementKey, ): Boolean { @@ -312,7 +287,7 @@ private fun isSharedElementEnabled( } internal fun sharedElementTransformation( - layoutState: SceneTransitionLayoutStateImpl, + layoutState: BaseSceneTransitionLayoutState, transition: TransitionState.Transition, element: ElementKey, ): SharedElementTransformation? { @@ -342,18 +317,9 @@ private fun isElementOpaque( layoutImpl: SceneTransitionLayoutImpl, element: Element, scene: Scene, - sceneState: Element.SceneState, ): Boolean { val transition = layoutImpl.state.currentTransition ?: return true - if (!layoutImpl.isTransitionReady(transition)) { - val lastValue = - sceneState.lastState.alpha.takeIf { it != Element.AlphaUnspecified } - ?: element.lastSharedState.alpha.takeIf { it != Element.AlphaUnspecified } ?: 1f - - return lastValue == 1f - } - val fromScene = transition.fromScene val toScene = transition.toScene val fromState = element.sceneStates[fromScene] @@ -383,7 +349,6 @@ private fun elementAlpha( layoutImpl: SceneTransitionLayoutImpl, element: Element, scene: Scene, - sceneState: Element.SceneState, ): Float { return computeValue( layoutImpl, @@ -393,10 +358,7 @@ private fun elementAlpha( transformation = { it.alpha }, idleValue = 1f, currentValue = { 1f }, - lastValue = { - sceneState.lastState.alpha.takeIf { it != Element.AlphaUnspecified } - ?: element.lastSharedState.alpha.takeIf { it != Element.AlphaUnspecified } ?: 1f - }, + isSpecified = { true }, ::lerp, ) .coerceIn(0f, 1f) @@ -434,34 +396,23 @@ private fun IntermediateMeasureScope.measure( transformation = { it.size }, idleValue = lookaheadSize, currentValue = { measurable.measure(constraints).also { maybePlaceable = it }.size() }, - lastValue = { - sceneState.lastState.size.takeIf { it != Element.SizeUnspecified } - ?: element.lastSharedState.size.takeIf { it != Element.SizeUnspecified } - ?: measurable.measure(constraints).also { maybePlaceable = it }.size() - }, + isSpecified = { it != Element.SizeUnspecified }, ::lerp, ) - val placeable = - maybePlaceable - ?: measurable.measure( - Constraints.fixed( - targetSize.width.coerceAtLeast(0), - targetSize.height.coerceAtLeast(0), - ) + return maybePlaceable + ?: measurable.measure( + Constraints.fixed( + targetSize.width.coerceAtLeast(0), + targetSize.height.coerceAtLeast(0), ) - - val size = placeable.size() - element.lastSharedState.size = size - sceneState.lastState.size = size - return placeable + ) } private fun getDrawScale( layoutImpl: SceneTransitionLayoutImpl, element: Element, - scene: Scene, - sceneState: Element.SceneState + scene: Scene ): Scale { return computeValue( layoutImpl, @@ -471,10 +422,7 @@ private fun getDrawScale( transformation = { it.drawScale }, idleValue = Scale.Default, currentValue = { Scale.Default }, - lastValue = { - sceneState.lastState.drawScale.takeIf { it != Scale.Default } - ?: element.lastSharedState.drawScale - }, + isSpecified = { true }, ::lerp, ) } @@ -498,9 +446,12 @@ private fun IntermediateMeasureScope.place( sceneState.targetOffset = targetOffsetInScene } + // No need to place the element in this scene if we don't want to draw it anyways. + if (!shouldDrawElement(layoutImpl, scene, element)) { + return + } + val currentOffset = lookaheadScopeCoordinates.localPositionOf(coords, Offset.Zero) - val lastSharedState = element.lastSharedState - val lastSceneState = sceneState.lastState val targetOffset = computeValue( layoutImpl, @@ -510,37 +461,19 @@ private fun IntermediateMeasureScope.place( transformation = { it.offset }, idleValue = targetOffsetInScene, currentValue = { currentOffset }, - lastValue = { - lastSceneState.offset.takeIf { it.isSpecified } - ?: lastSharedState.offset.takeIf { it.isSpecified } ?: currentOffset - }, + isSpecified = { it != Offset.Unspecified }, ::lerp, ) - lastSharedState.offset = targetOffset - lastSceneState.offset = targetOffset - - // No need to place the element in this scene if we don't want to draw it anyways. Note that - // it's still important to compute the target offset and update last(Shared|Scene)State, - // otherwise they will be out of date. - if (!shouldDrawElement(layoutImpl, scene, element)) { - return - } - val offset = (targetOffset - currentOffset).round() - if (isElementOpaque(layoutImpl, element, scene, sceneState)) { + if (isElementOpaque(layoutImpl, element, scene)) { // TODO(b/291071158): Call placeWithLayer() if offset != IntOffset.Zero and size is not // animated once b/305195729 is fixed. Test that drawing is not invalidated in that // case. placeable.place(offset) - lastSharedState.alpha = 1f - lastSceneState.alpha = 1f } else { placeable.placeWithLayer(offset) { - val alpha = elementAlpha(layoutImpl, element, scene, sceneState) - this.alpha = alpha - lastSharedState.alpha = alpha - lastSceneState.alpha = alpha + this.alpha = elementAlpha(layoutImpl, element, scene) } } } @@ -563,8 +496,6 @@ private fun IntermediateMeasureScope.place( * different than [idleValue] even if the value is not transformed directly because it could be * impacted by the transformations on other elements, like a parent that is being translated or * resized. - * @param lastValue the last value that was used. This should be equal to [currentValue] if this is - * the first time the value is set. * @param lerp the linear interpolation function used to interpolate between two values of this * value type. */ @@ -576,7 +507,7 @@ private inline fun <T> computeValue( transformation: (ElementTransformations) -> PropertyTransformation<T>?, idleValue: T, currentValue: () -> T, - lastValue: () -> T, + isSpecified: (T) -> Boolean, lerp: (T, T, Float) -> T, ): T { val transition = @@ -587,21 +518,16 @@ private inline fun <T> computeValue( // layout phase. ?: return currentValue() - // A transition was started but it's not ready yet (not all elements have been composed/laid - // out yet). Use the last value that was set, to make sure elements don't unexpectedly jump. - if (!layoutImpl.isTransitionReady(transition)) { - return lastValue() - } - val fromScene = transition.fromScene val toScene = transition.toScene + val fromState = element.sceneStates[fromScene] val toState = element.sceneStates[toScene] if (fromState == null && toState == null) { // TODO(b/311600838): Throw an exception instead once layers of disposed elements are not // run anymore. - return lastValue() + return idleValue } // The element is shared: interpolate between the value in fromScene and the value in toScene. @@ -612,6 +538,11 @@ private inline fun <T> computeValue( val start = sceneValue(fromState!!) val end = sceneValue(toState!!) + // TODO(b/316901148): Remove checks to isSpecified() once the lookahead pass runs for all + // nodes before the intermediate layout pass. + if (!isSpecified(start)) return end + if (!isSpecified(end)) return start + // Make sure we don't read progress if values are the same and we don't need to interpolate, // so we don't invalidate the phase where this is read. return if (start == end) start else lerp(start, end, transition.progress) diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt index af3c0999c97b..cdc4778dbf4d 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt @@ -174,22 +174,6 @@ private fun shouldComposeMovableElement( // If we are idle, there is only one [scene] that is composed so we can compose our // movable content here. ?: return true - val fromScene = transition.fromScene - val toScene = transition.toScene - - val fromReady = layoutImpl.isSceneReady(fromScene) - val toReady = layoutImpl.isSceneReady(toScene) - - if (!fromReady && !toReady) { - // Neither of the scenes will be drawn, so where we compose it doesn't really matter. Note - // that we could have slightly more complicated logic here to optimize for this case, but - // it's not worth it given that readyScenes should disappear soon (b/316901148). - return scene == toScene - } - - // If one of the scenes is not ready, compose it in the other one to make sure it is drawn. - if (!fromReady) return scene == toScene - if (!toReady) return scene == fromScene // Always compose movable elements in the scene picked by their scene picker. return shouldDrawOrComposeSharedElement( diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PunchHole.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PunchHole.kt index 454c0ecf8ac5..105f5728a836 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PunchHole.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PunchHole.kt @@ -16,96 +16,121 @@ package com.android.compose.animation.scene +import androidx.compose.runtime.Stable import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Size -import androidx.compose.ui.geometry.toRect import androidx.compose.ui.graphics.BlendMode import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.CompositingStrategy import androidx.compose.ui.graphics.Outline -import androidx.compose.ui.graphics.Paint import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.drawOutline import androidx.compose.ui.graphics.drawscope.ContentDrawScope import androidx.compose.ui.graphics.drawscope.DrawScope -import androidx.compose.ui.graphics.drawscope.drawIntoCanvas import androidx.compose.ui.graphics.drawscope.translate -import androidx.compose.ui.graphics.withSaveLayer +import androidx.compose.ui.layout.LayoutCoordinates +import androidx.compose.ui.layout.Measurable +import androidx.compose.ui.layout.MeasureResult +import androidx.compose.ui.layout.MeasureScope +import androidx.compose.ui.node.DelegatingNode import androidx.compose.ui.node.DrawModifierNode +import androidx.compose.ui.node.GlobalPositionAwareModifierNode +import androidx.compose.ui.node.LayoutModifierNode import androidx.compose.ui.node.ModifierNodeElement +import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.toSize -internal fun Modifier.punchHole( - layoutImpl: SceneTransitionLayoutImpl, - element: ElementKey, - bounds: ElementKey, - shape: Shape, -): Modifier = this.then(PunchHoleElement(layoutImpl, element, bounds, shape)) +/** + * Punch a hole in this node with the given [size], [offset] and [shape]. + * + * Punching a hole in an element will "remove" any pixel drawn by that element in the hole area. + * This can be used to make content drawn below an opaque element visible. For example, if we have + * [this lockscreen scene](http://shortn/_VYySFnJDhN) drawn below + * [this shade scene](http://shortn/_fpxGUk0Rg7) and punch a hole in the latter using the big clock + * time bounds and a RoundedCornerShape(10dp), [this](http://shortn/_qt80IvORFj) would be the + * result. + */ +@Stable +fun Modifier.punchHole( + size: () -> Size, + offset: () -> Offset, + shape: Shape = RectangleShape, +): Modifier = this.then(PunchHoleElement(size, offset, shape)) + +/** + * Punch a hole in this node using the bounds of [coords] and the given [shape]. + * + * You can use [androidx.compose.ui.layout.onGloballyPositioned] to get the last coordinates of a + * node. + */ +@Stable +fun Modifier.punchHole( + coords: () -> LayoutCoordinates?, + shape: Shape = RectangleShape, +): Modifier = this.then(PunchHoleWithBoundsElement(coords, shape)) private data class PunchHoleElement( - private val layoutImpl: SceneTransitionLayoutImpl, - private val element: ElementKey, - private val bounds: ElementKey, + private val size: () -> Size, + private val offset: () -> Offset, private val shape: Shape, ) : ModifierNodeElement<PunchHoleNode>() { - override fun create(): PunchHoleNode = PunchHoleNode(layoutImpl, element, bounds, shape) + override fun create(): PunchHoleNode = PunchHoleNode(size, offset, { shape }) override fun update(node: PunchHoleNode) { - node.layoutImpl = layoutImpl - node.element = element - node.bounds = bounds - node.shape = shape + node.size = size + node.offset = offset + node.shape = { shape } } } private class PunchHoleNode( - var layoutImpl: SceneTransitionLayoutImpl, - var element: ElementKey, - var bounds: ElementKey, - var shape: Shape, -) : Modifier.Node(), DrawModifierNode { + var size: () -> Size, + var offset: () -> Offset, + var shape: () -> Shape, +) : Modifier.Node(), DrawModifierNode, LayoutModifierNode { private var lastSize: Size = Size.Unspecified private var lastLayoutDirection: LayoutDirection = LayoutDirection.Ltr private var lastOutline: Outline? = null - override fun ContentDrawScope.draw() { - val bounds = layoutImpl.elements[bounds] + override fun MeasureScope.measure( + measurable: Measurable, + constraints: Constraints + ): MeasureResult { + return measurable.measure(constraints).run { + layout(width, height) { + placeWithLayer(0, 0) { compositingStrategy = CompositingStrategy.Offscreen } + } + } + } - if ( - bounds == null || - bounds.lastSharedState.size == Element.SizeUnspecified || - bounds.lastSharedState.offset == Offset.Unspecified - ) { + override fun ContentDrawScope.draw() { + val holeSize = size() + if (holeSize == Size.Zero) { drawContent() return } - val element = layoutImpl.elements.getValue(element) - drawIntoCanvas { canvas -> - canvas.withSaveLayer(size.toRect(), Paint()) { - drawContent() + drawContent() - val offset = bounds.lastSharedState.offset - element.lastSharedState.offset - translate(offset.x, offset.y) { drawHole(bounds) } - } - } + val offset = offset() + translate(offset.x, offset.y) { drawHole(holeSize) } } - private fun DrawScope.drawHole(bounds: Element) { - val boundsSize = bounds.lastSharedState.size.toSize() + private fun DrawScope.drawHole(size: Size) { if (shape == RectangleShape) { - drawRect(Color.Black, size = boundsSize, blendMode = BlendMode.DstOut) + drawRect(Color.Black, size = size, blendMode = BlendMode.DstOut) return } val outline = - if (boundsSize == lastSize && layoutDirection == lastLayoutDirection) { + if (size == lastSize && layoutDirection == lastLayoutDirection) { lastOutline!! } else { - val newOutline = shape.createOutline(boundsSize, layoutDirection, this) - lastSize = boundsSize + val newOutline = shape().createOutline(size, layoutDirection, this) + lastSize = size lastLayoutDirection = layoutDirection lastOutline = newOutline newOutline @@ -118,3 +143,39 @@ private class PunchHoleNode( ) } } + +private data class PunchHoleWithBoundsElement( + private val coords: () -> LayoutCoordinates?, + private val shape: Shape, +) : ModifierNodeElement<PunchHoleWithBoundsNode>() { + override fun create(): PunchHoleWithBoundsNode = PunchHoleWithBoundsNode(coords, shape) + + override fun update(node: PunchHoleWithBoundsNode) { + node.holeCoords = coords + node.shape = shape + } +} + +private class PunchHoleWithBoundsNode( + var holeCoords: () -> LayoutCoordinates?, + var shape: Shape, +) : DelegatingNode(), DrawModifierNode, GlobalPositionAwareModifierNode { + private val delegate = delegate(PunchHoleNode(::holeSize, ::holeOffset, ::shape)) + private var lastCoords: LayoutCoordinates? = null + + override fun onGloballyPositioned(coordinates: LayoutCoordinates) { + this.lastCoords = coordinates + } + + override fun ContentDrawScope.draw() = with(delegate) { draw() } + + private fun holeSize(): Size { + return holeCoords()?.size?.toSize() ?: Size.Zero + } + + private fun holeOffset(): Offset { + val holeCoords = holeCoords() ?: return Offset.Zero + val lastCoords = lastCoords ?: error("draw() was called before onGloballyPositioned()") + return lastCoords.localPositionOf(holeCoords, relativeToSource = Offset.Zero) + } +} diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt index 3537b7989ed5..f67df54b088c 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt @@ -26,7 +26,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Shape import androidx.compose.ui.layout.intermediateLayout import androidx.compose.ui.platform.testTag import androidx.compose.ui.unit.IntSize @@ -139,12 +138,6 @@ internal class SceneScopeImpl( bottomOrRightBehavior = bottomBehavior, ) - override fun Modifier.punchHole( - element: ElementKey, - bounds: ElementKey, - shape: Shape - ): Modifier = punchHole(layoutImpl, element, bounds, shape) - override fun Modifier.noResizeDuringTransitions(): Modifier { return noResizeDuringTransitions(layoutState = layoutImpl.state) } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt index 338557d0942e..64388b7653e0 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt @@ -312,13 +312,16 @@ internal class SceneGestureHandler( // immediately go back B => A. if (targetScene != swipeTransition._currentScene) { swipeTransition._currentScene = targetScene - layoutImpl.onChangeScene(targetScene.key) + with(layoutImpl.state) { coroutineScope.onChangeScene(targetScene.key) } } - animateOffset( + swipeTransition.animateOffset( + coroutineScope = coroutineScope, initialVelocity = velocity, targetOffset = targetOffset, - targetScene = targetScene.key + onAnimationCompleted = { + layoutState.finishTransition(swipeTransition, idleScene = targetScene.key) + } ) } @@ -410,34 +413,6 @@ internal class SceneGestureHandler( } } - private fun animateOffset( - initialVelocity: Float, - targetOffset: Float, - targetScene: SceneKey, - ) { - swipeTransition.startOffsetAnimation { - coroutineScope.launch { - if (!swipeTransition.isAnimatingOffset) { - swipeTransition.offsetAnimatable.snapTo(swipeTransition.dragOffset) - } - swipeTransition.isAnimatingOffset = true - - swipeTransition.offsetAnimatable.animateTo( - targetOffset, - // TODO(b/290184746): Make this spring spec configurable. - spring( - stiffness = Spring.StiffnessMediumLow, - visibilityThreshold = OffsetVisibilityThreshold - ), - initialVelocity = initialVelocity, - ) - - swipeTransition.finishOffsetAnimation() - layoutState.finishTransition(swipeTransition, targetScene) - } - } - } - internal class SwipeTransition( val _fromScene: Scene, val _toScene: Scene, @@ -479,12 +454,14 @@ internal class SceneGestureHandler( private var offsetAnimationJob: Job? = null /** Ends any previous [offsetAnimationJob] and runs the new [job]. */ - fun startOffsetAnimation(job: () -> Job) { + private fun startOffsetAnimation(job: () -> Job) { cancelOffsetAnimation() offsetAnimationJob = job() } /** Cancel any ongoing offset animation. */ + // TODO(b/317063114) This should be a suspended function to avoid multiple jobs running at + // the same time. fun cancelOffsetAnimation() { offsetAnimationJob?.cancel() finishOffsetAnimation() @@ -496,6 +473,43 @@ internal class SceneGestureHandler( dragOffset = offsetAnimatable.value } } + + // TODO(b/290184746): Make this spring spec configurable. + private val animationSpec = + spring( + stiffness = Spring.StiffnessMediumLow, + visibilityThreshold = OffsetVisibilityThreshold + ) + + fun animateOffset( + // TODO(b/317063114) The CoroutineScope should be removed. + coroutineScope: CoroutineScope, + initialVelocity: Float, + targetOffset: Float, + onAnimationCompleted: () -> Unit, + ) { + startOffsetAnimation { + coroutineScope.launch { + animateOffset(targetOffset, initialVelocity) + onAnimationCompleted() + } + } + } + + private suspend fun animateOffset(targetOffset: Float, initialVelocity: Float) { + if (!isAnimatingOffset) { + offsetAnimatable.snapTo(dragOffset) + } + isAnimatingOffset = true + + offsetAnimatable.animateTo( + targetValue = targetOffset, + animationSpec = animationSpec, + initialVelocity = initialVelocity, + ) + + finishOffsetAnimation() + } } companion object { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt index 84fade8937ff..80f8c1c9e987 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt @@ -19,17 +19,48 @@ package com.android.compose.animation.scene import androidx.annotation.FloatRange import androidx.compose.foundation.gestures.Orientation import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.SideEffect import androidx.compose.runtime.Stable import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Shape import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.platform.LocalDensity -import kotlinx.coroutines.channels.Channel + +/** + * [SceneTransitionLayout] is a container that automatically animates its content whenever its state + * changes. + * + * Note: You should use [androidx.compose.animation.AnimatedContent] instead of + * [SceneTransitionLayout] if it fits your need. Use [SceneTransitionLayout] over AnimatedContent if + * you need support for swipe gestures, shared elements or transitions defined declaratively outside + * UI code. + * + * @param state the state of this layout. + * @param edgeDetector the edge detector used to detect which edge a swipe is started from, if any. + * @param transitionInterceptionThreshold used during a scene transition. For the scene to be + * intercepted, the progress value must be above the threshold, and below (1 - threshold). + * @param scenes the configuration of the different scenes of this layout. + * @see updateSceneTransitionLayoutState + */ +@Composable +fun SceneTransitionLayout( + state: SceneTransitionLayoutState, + modifier: Modifier = Modifier, + edgeDetector: EdgeDetector = DefaultEdgeDetector, + @FloatRange(from = 0.0, to = 0.5) transitionInterceptionThreshold: Float = 0f, + scenes: SceneTransitionLayoutScope.() -> Unit, +) { + SceneTransitionLayoutForTesting( + state, + modifier, + edgeDetector, + transitionInterceptionThreshold, + onLayoutImpl = null, + scenes, + ) +} /** * [SceneTransitionLayout] is a container that automatically animates its content whenever @@ -45,7 +76,6 @@ import kotlinx.coroutines.channels.Channel * This is called when the user commits a transition to a new scene because of a [UserAction], for * instance by triggering back navigation or by swiping to a new scene. * @param transitions the definition of the transitions used to animate a change of scene. - * @param state the observable state of this layout. * @param edgeDetector the edge detector used to detect which edge a swipe is started from, if any. * @param transitionInterceptionThreshold used during a scene transition. For the scene to be * intercepted, the progress value must be above the threshold, and below (1 - threshold). @@ -57,20 +87,16 @@ fun SceneTransitionLayout( onChangeScene: (SceneKey) -> Unit, transitions: SceneTransitions, modifier: Modifier = Modifier, - state: SceneTransitionLayoutState = remember { SceneTransitionLayoutState(currentScene) }, edgeDetector: EdgeDetector = DefaultEdgeDetector, @FloatRange(from = 0.0, to = 0.5) transitionInterceptionThreshold: Float = 0f, scenes: SceneTransitionLayoutScope.() -> Unit, ) { - SceneTransitionLayoutForTesting( - currentScene, - onChangeScene, - modifier, - transitions, + val state = updateSceneTransitionLayoutState(currentScene, onChangeScene, transitions) + SceneTransitionLayout( state, + modifier, edgeDetector, transitionInterceptionThreshold, - onLayoutImpl = null, scenes, ) } @@ -203,18 +229,6 @@ interface BaseSceneScope { ): Modifier /** - * Punch a hole in this [element] using the bounds of [bounds] in [scene] and the given [shape]. - * - * Punching a hole in an element will "remove" any pixel drawn by that element in the hole area. - * This can be used to make content drawn below an opaque element visible. For example, if we - * have [this lockscreen scene](http://shortn/_VYySFnJDhN) drawn below - * [this shade scene](http://shortn/_fpxGUk0Rg7) and punch a hole in the latter using the big - * clock time bounds and a RoundedCornerShape(10dp), [this](http://shortn/_qt80IvORFj) would be - * the result. - */ - fun Modifier.punchHole(element: ElementKey, bounds: ElementKey, shape: Shape): Modifier - - /** * Don't resize during transitions. This can for instance be used to make sure that scrollable * lists keep a constant size during transitions even if its elements are growing/shrinking. */ @@ -346,11 +360,8 @@ enum class SwipeDirection(val orientation: Orientation) { */ @Composable internal fun SceneTransitionLayoutForTesting( - currentScene: SceneKey, - onChangeScene: (SceneKey) -> Unit, + state: SceneTransitionLayoutState, modifier: Modifier = Modifier, - transitions: SceneTransitions = transitions {}, - state: SceneTransitionLayoutState = remember { SceneTransitionLayoutState(currentScene) }, edgeDetector: EdgeDetector = DefaultEdgeDetector, transitionInterceptionThreshold: Float = 0f, onLayoutImpl: ((SceneTransitionLayoutImpl) -> Unit)? = null, @@ -360,8 +371,7 @@ internal fun SceneTransitionLayoutForTesting( val coroutineScope = rememberCoroutineScope() val layoutImpl = remember { SceneTransitionLayoutImpl( - state = state as SceneTransitionLayoutStateImpl, - onChangeScene = onChangeScene, + state = state as BaseSceneTransitionLayoutState, density = density, edgeDetector = edgeDetector, transitionInterceptionThreshold = transitionInterceptionThreshold, @@ -375,7 +385,6 @@ internal fun SceneTransitionLayoutForTesting( // SnapshotStateMap anymore. layoutImpl.updateScenes(scenes) - val targetSceneChannel = remember { Channel<SceneKey>(Channel.CONFLATED) } SideEffect { if (state != layoutImpl.state) { error( @@ -384,23 +393,8 @@ internal fun SceneTransitionLayoutForTesting( ) } - layoutImpl.onChangeScene = onChangeScene - (state as SceneTransitionLayoutStateImpl).transitions = transitions layoutImpl.density = density layoutImpl.edgeDetector = edgeDetector - - state.transitions = transitions - - targetSceneChannel.trySend(currentScene) - } - - LaunchedEffect(targetSceneChannel) { - for (newKey in targetSceneChannel) { - // Inspired by AnimateAsState.kt: let's poll the last value to avoid being one frame - // late. - val newKey = targetSceneChannel.tryReceive().getOrNull() ?: newKey - animateToScene(layoutImpl.state, newKey) - } } layoutImpl.Content(modifier) diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt index 0227aba94b53..7cc9d2623e9c 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt @@ -20,14 +20,11 @@ import androidx.activity.compose.BackHandler import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.Stable import androidx.compose.runtime.key import androidx.compose.runtime.snapshots.SnapshotStateMap import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.layout.LookaheadScope import androidx.compose.ui.layout.intermediateLayout import androidx.compose.ui.unit.Density @@ -48,13 +45,12 @@ internal typealias MovableElementContent = @Stable internal class SceneTransitionLayoutImpl( - internal val state: SceneTransitionLayoutStateImpl, - internal var onChangeScene: (SceneKey) -> Unit, + internal val state: BaseSceneTransitionLayoutState, internal var density: Density, internal var edgeDetector: EdgeDetector, internal var transitionInterceptionThreshold: Float, builder: SceneTransitionLayoutScope.() -> Unit, - coroutineScope: CoroutineScope, + private val coroutineScope: CoroutineScope, ) { /** * The map of [Scene]s. @@ -100,16 +96,6 @@ internal class SceneTransitionLayoutImpl( ?: mutableMapOf<ValueKey, MutableMap<ElementKey?, SnapshotStateMap<SceneKey, *>>>() .also { _sharedValues = it } - /** - * The scenes that are "ready", i.e. they were composed and fully laid-out at least once. - * - * Note that this map is *read* during composition, so it is a [SnapshotStateMap] to make sure - * that we recompose when modifications are made to this map. - * - * TODO(b/316901148): Remove this map. - */ - private val readyScenes = SnapshotStateMap<SceneKey, Boolean>() - private val horizontalGestureHandler: SceneGestureHandler private val verticalGestureHandler: SceneGestureHandler @@ -244,49 +230,19 @@ internal class SceneTransitionLayoutImpl( // TODO(b/290184746): Make sure that this works with SystemUI once we use // SceneTransitionLayout in Flexiglass. scene(state.transitionState.currentScene).userActions[Back]?.let { backScene -> - BackHandler { onChangeScene(backScene) } + BackHandler { with(state) { coroutineScope.onChangeScene(backScene) } } } Box { scenesToCompose.fastForEach { scene -> val key = scene.key - key(key) { - // Mark this scene as ready once it has been composed, laid out and - // drawn the first time. We have to do this in a LaunchedEffect here - // because DisposableEffect runs between composition and layout. - LaunchedEffect(key) { readyScenes[key] = true } - DisposableEffect(key) { onDispose { readyScenes.remove(key) } } - - scene.Content( - Modifier.drawWithContent { - if (state.currentTransition == null) { - drawContent() - } else { - // Don't draw scenes that are not ready yet. - if (readyScenes.containsKey(key)) { - drawContent() - } - } - } - ) - } + key(key) { scene.Content() } } } } } } - /** - * Return whether [transition] is ready, i.e. the elements of both scenes of the transition were - * laid out at least once. - */ - internal fun isTransitionReady(transition: TransitionState.Transition): Boolean { - return readyScenes.containsKey(transition.fromScene) && - readyScenes.containsKey(transition.toScene) - } - - internal fun isSceneReady(scene: SceneKey): Boolean = readyScenes.containsKey(scene) - internal fun setScenesTargetSizeForTest(size: IntSize) { scenes.values.forEach { it.targetSize = size } } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt index 0607aa148157..956e326dc03e 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt @@ -16,12 +16,23 @@ package com.android.compose.animation.scene +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.SideEffect import androidx.compose.runtime.Stable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.Channel -/** The state of a [SceneTransitionLayout]. */ +/** + * The state of a [SceneTransitionLayout]. + * + * @see MutableSceneTransitionLayoutState + * @see updateSceneTransitionLayoutState + */ @Stable sealed interface SceneTransitionLayoutState { /** @@ -36,6 +47,9 @@ sealed interface SceneTransitionLayoutState { val currentTransition: TransitionState.Transition? get() = transitionState as? TransitionState.Transition + /** The [SceneTransitions] used when animating this state. */ + val transitions: SceneTransitions + /** * Whether we are transitioning. If [from] or [to] is empty, we will also check that they match * the scenes we are animating from and/or to. @@ -46,9 +60,68 @@ sealed interface SceneTransitionLayoutState { fun isTransitioningBetween(scene: SceneKey, other: SceneKey): Boolean } -/** Create a new [SceneTransitionLayoutState] that is currently idle at scene [currentScene]. */ -fun SceneTransitionLayoutState(currentScene: SceneKey): SceneTransitionLayoutState { - return SceneTransitionLayoutStateImpl(currentScene, SceneTransitions.Empty) +/** A [SceneTransitionLayoutState] whose target scene can be imperatively set. */ +sealed interface MutableSceneTransitionLayoutState : SceneTransitionLayoutState { + /** The [SceneTransitions] used when animating this state. */ + override var transitions: SceneTransitions + + /** + * Set the target scene of this state to [targetScene]. + * + * If [targetScene] is the same as the [currentScene][TransitionState.currentScene] of + * [transitionState], then nothing will happen and this will return `null`. Note that this means + * that this will also do nothing if the user is currently swiping from [targetScene] to another + * scene, or if we were already animating to [targetScene]. + * + * If [targetScene] is different than the [currentScene][TransitionState.currentScene] of + * [transitionState], then this will animate to [targetScene]. The associated + * [TransitionState.Transition] will be returned and will be set as the current + * [transitionState] of this [MutableSceneTransitionLayoutState]. + * + * Note that because a non-null [TransitionState.Transition] is returned does not mean that the + * transition will finish and that we will settle to [targetScene]. The returned transition + * might still be interrupted, for instance by another call to [setTargetScene] or by a user + * gesture. + * + * If [this] [CoroutineScope] is cancelled during the transition and that the transition was + * still active, then the [transitionState] of this [MutableSceneTransitionLayoutState] will be + * set to `TransitionState.Idle(targetScene)`. + * + * TODO(b/318794193): Add APIs to await() and cancel() any [TransitionState.Transition]. + */ + fun setTargetScene( + targetScene: SceneKey, + coroutineScope: CoroutineScope, + ): TransitionState.Transition? +} + +/** Return a [MutableSceneTransitionLayoutState] initially idle at [initialScene]. */ +fun MutableSceneTransitionLayoutState( + initialScene: SceneKey, + transitions: SceneTransitions = SceneTransitions.Empty, +): MutableSceneTransitionLayoutState { + return MutableSceneTransitionLayoutStateImpl(initialScene, transitions) +} + +/** + * Sets up a [SceneTransitionLayoutState] and keeps it synced with [currentScene], [onChangeScene] + * and [transitions]. New transitions will automatically be started whenever [currentScene] is + * changed. + * + * @param currentScene the current scene + * @param onChangeScene a mutator that should set [currentScene] to the given scene when called. + * This is called when the user commits a transition to a new scene because of a [UserAction], for + * instance by triggering back navigation or by swiping to a new scene. + * @param transitions the definition of the transitions used to animate a change of scene. + */ +@Composable +fun updateSceneTransitionLayoutState( + currentScene: SceneKey, + onChangeScene: (SceneKey) -> Unit, + transitions: SceneTransitions = SceneTransitions.Empty, +): SceneTransitionLayoutState { + return remember { HoistedSceneTransitionLayoutScene(currentScene, transitions, onChangeScene) } + .apply { update(currentScene, onChangeScene, transitions) } } @Stable @@ -109,13 +182,11 @@ sealed interface TransitionState { } } -internal class SceneTransitionLayoutStateImpl( - initialScene: SceneKey, - internal var transitions: SceneTransitions, -) : SceneTransitionLayoutState { +internal abstract class BaseSceneTransitionLayoutState(initialScene: SceneKey) : + SceneTransitionLayoutState { override var transitionState: TransitionState by mutableStateOf(TransitionState.Idle(initialScene)) - private set + protected set /** * The current [transformationSpec] associated to [transitionState]. Accessing this value makes @@ -123,6 +194,14 @@ internal class SceneTransitionLayoutStateImpl( */ internal var transformationSpec: TransformationSpecImpl = TransformationSpec.Empty + /** + * Called when the [current scene][TransitionState.currentScene] should be changed to [scene]. + * + * When this is called, the source of truth for the current scene should be changed so that + * [transitionState] will animate and settle to [scene]. + */ + internal abstract fun CoroutineScope.onChangeScene(scene: SceneKey) + override fun isTransitioning(from: SceneKey?, to: SceneKey?): Boolean { val transition = currentTransition ?: return false return transition.isTransitioning(from, to) @@ -154,3 +233,62 @@ internal class SceneTransitionLayoutStateImpl( } } } + +/** + * A [SceneTransitionLayout] whose current scene/source of truth is hoisted (its current value comes + * from outside). + */ +internal class HoistedSceneTransitionLayoutScene( + initialScene: SceneKey, + override var transitions: SceneTransitions, + private var changeScene: (SceneKey) -> Unit, +) : BaseSceneTransitionLayoutState(initialScene) { + private val targetSceneChannel = Channel<SceneKey>(Channel.CONFLATED) + + override fun CoroutineScope.onChangeScene(scene: SceneKey) = changeScene(scene) + + @Composable + fun update( + currentScene: SceneKey, + onChangeScene: (SceneKey) -> Unit, + transitions: SceneTransitions, + ) { + SideEffect { + this.changeScene = onChangeScene + this.transitions = transitions + + targetSceneChannel.trySend(currentScene) + } + + LaunchedEffect(targetSceneChannel) { + for (newKey in targetSceneChannel) { + // Inspired by AnimateAsState.kt: let's poll the last value to avoid being one frame + // late. + val newKey = targetSceneChannel.tryReceive().getOrNull() ?: newKey + animateToScene(layoutState = this@HoistedSceneTransitionLayoutScene, newKey) + } + } + } +} + +/** A [MutableSceneTransitionLayoutState] that holds the value for the current scene. */ +internal class MutableSceneTransitionLayoutStateImpl( + initialScene: SceneKey, + override var transitions: SceneTransitions, +) : MutableSceneTransitionLayoutState, BaseSceneTransitionLayoutState(initialScene) { + override fun setTargetScene( + targetScene: SceneKey, + coroutineScope: CoroutineScope + ): TransitionState.Transition? { + return with(this) { + coroutineScope.animateToScene( + layoutState = this@MutableSceneTransitionLayoutStateImpl, + target = targetScene, + ) + } + } + + override fun CoroutineScope.onChangeScene(scene: SceneKey) { + setTargetScene(scene, coroutineScope = this) + } +} diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt index 35a5054cbd2a..c0de87abbfe8 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt @@ -16,7 +16,6 @@ package com.android.compose.animation.scene -import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.tween import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Box @@ -35,17 +34,11 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier -import androidx.compose.ui.geometry.Offset import androidx.compose.ui.layout.intermediateLayout -import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.test.junit4.createComposeRule -import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.dp import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.android.compose.test.subjects.DpOffsetSubject -import com.android.compose.test.subjects.assertThat import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -263,8 +256,11 @@ class ElementTest { rule.setContent { SceneTransitionLayoutForTesting( - currentScene = currentScene, - onChangeScene = { currentScene = it }, + state = + updateSceneTransitionLayoutState( + currentScene = currentScene, + onChangeScene = { currentScene = it } + ), onLayoutImpl = { nullableLayoutImpl = it }, ) { scene(TestScenes.SceneA) { /* Nothing */} @@ -428,8 +424,11 @@ class ElementTest { rule.setContent { SceneTransitionLayoutForTesting( - currentScene = TestScenes.SceneA, - onChangeScene = {}, + state = + updateSceneTransitionLayoutState( + currentScene = TestScenes.SceneA, + onChangeScene = {} + ), onLayoutImpl = { nullableLayoutImpl = it }, ) { scene(TestScenes.SceneA) { Box(Modifier.element(key)) } @@ -478,8 +477,11 @@ class ElementTest { scrollScope = rememberCoroutineScope() SceneTransitionLayoutForTesting( - currentScene = TestScenes.SceneA, - onChangeScene = {}, + state = + updateSceneTransitionLayoutState( + currentScene = TestScenes.SceneA, + onChangeScene = {} + ), onLayoutImpl = { nullableLayoutImpl = it }, ) { scene(TestScenes.SceneA) { @@ -565,86 +567,4 @@ class ElementTest { after { assertThat(fooCompositions).isEqualTo(1) } } } - - @Test - fun sharedElementOffsetIsUpdatedEvenWhenNotPlaced() { - var nullableLayoutImpl: SceneTransitionLayoutImpl? = null - var density: Density? = null - - fun layoutImpl() = nullableLayoutImpl ?: error("nullableLayoutImpl was not set") - - fun density() = density ?: error("density was not set") - - fun Offset.toDpOffset() = with(density()) { DpOffset(x.toDp(), y.toDp()) } - - fun foo() = layoutImpl().elements[TestElements.Foo] ?: error("Foo not in elements map") - - fun Element.lastSharedOffset() = lastSharedState.offset.toDpOffset() - - fun Element.lastOffsetIn(scene: SceneKey) = - (sceneStates[scene] ?: error("$scene not in sceneValues map")) - .lastState - .offset - .toDpOffset() - - rule.testTransition( - from = TestScenes.SceneA, - to = TestScenes.SceneB, - transitionLayout = { currentScene, onChangeScene -> - density = LocalDensity.current - - SceneTransitionLayoutForTesting( - currentScene = currentScene, - onChangeScene = onChangeScene, - onLayoutImpl = { nullableLayoutImpl = it }, - transitions = - transitions { - from(TestScenes.SceneA, to = TestScenes.SceneB) { - spec = tween(durationMillis = 4 * 16, easing = LinearEasing) - } - } - ) { - scene(TestScenes.SceneA) { Box(Modifier.element(TestElements.Foo)) } - scene(TestScenes.SceneB) { - Box(Modifier.offset(x = 40.dp, y = 80.dp).element(TestElements.Foo)) - } - } - } - ) { - val tolerance = DpOffsetSubject.DefaultTolerance - - before { - val expected = DpOffset(0.dp, 0.dp) - assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected) - assertThat(foo().lastOffsetIn(TestScenes.SceneA)).isWithin(tolerance).of(expected) - } - - at(16) { - val expected = DpOffset(10.dp, 20.dp) - assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected) - assertThat(foo().lastOffsetIn(TestScenes.SceneA)).isWithin(tolerance).of(expected) - assertThat(foo().lastOffsetIn(TestScenes.SceneB)).isWithin(tolerance).of(expected) - } - - at(32) { - val expected = DpOffset(20.dp, 40.dp) - assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected) - assertThat(foo().lastOffsetIn(TestScenes.SceneA)).isWithin(tolerance).of(expected) - assertThat(foo().lastOffsetIn(TestScenes.SceneB)).isWithin(tolerance).of(expected) - } - - at(48) { - val expected = DpOffset(30.dp, 60.dp) - assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected) - assertThat(foo().lastOffsetIn(TestScenes.SceneA)).isWithin(tolerance).of(expected) - assertThat(foo().lastOffsetIn(TestScenes.SceneB)).isWithin(tolerance).of(expected) - } - - after { - val expected = DpOffset(40.dp, 80.dp) - assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected) - assertThat(foo().lastOffsetIn(TestScenes.SceneB)).isWithin(tolerance).of(expected) - } - } - } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt index 04b3f8a1dfe7..0f9b0249b93f 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt @@ -32,7 +32,7 @@ class ObservableTransitionStateTest { @Test fun testObservableTransitionState() = runTest { - val state = SceneTransitionLayoutState(TestScenes.SceneA) + lateinit var state: SceneTransitionLayoutState // Collect the current observable state into [observableState]. // TODO(b/290184746): Use collectValues {} once it is extracted into a library that can be @@ -58,12 +58,14 @@ class ObservableTransitionStateTest { from = TestScenes.SceneA, to = TestScenes.SceneB, transitionLayout = { currentScene, onChangeScene -> - SceneTransitionLayout( - currentScene, - onChangeScene, - EmptyTestTransitions, - state = state, - ) { + state = + updateSceneTransitionLayoutState( + currentScene, + onChangeScene, + EmptyTestTransitions + ) + + SceneTransitionLayout(state = state) { scene(TestScenes.SceneA) {} scene(TestScenes.SceneB) {} } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt index d9ce5191f3d9..066a3e45fb3c 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt @@ -18,9 +18,6 @@ package com.android.compose.animation.scene import androidx.compose.foundation.gestures.Orientation import androidx.compose.material3.Text -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollSource @@ -53,10 +50,8 @@ class SceneGestureHandlerTest { private class TestGestureScope( val coroutineScope: MonotonicClockTestScope, ) { - private var internalCurrentScene: SceneKey by mutableStateOf(SceneA) - private val layoutState = - SceneTransitionLayoutStateImpl(internalCurrentScene, EmptyTestTransitions) + MutableSceneTransitionLayoutStateImpl(SceneA, EmptyTestTransitions) val mutableUserActionsA: MutableMap<UserAction, SceneKey> = mutableMapOf(Swipe.Up to SceneB, Swipe.Down to SceneC) @@ -94,7 +89,6 @@ class SceneGestureHandlerTest { private val layoutImpl = SceneTransitionLayoutImpl( state = layoutState, - onChangeScene = { internalCurrentScene = it }, density = Density(1f), edgeDetector = DefaultEdgeDetector, transitionInterceptionThreshold = transitionInterceptionThreshold, diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt index 75dee47a91cd..48825fb88096 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt @@ -18,7 +18,11 @@ package com.android.compose.animation.scene import androidx.compose.ui.test.junit4.createComposeRule import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.compose.test.runMonotonicClockTest import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.CoroutineStart +import kotlinx.coroutines.cancel +import kotlinx.coroutines.launch import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -29,7 +33,7 @@ class SceneTransitionLayoutStateTest { @Test fun isTransitioningTo_idle() { - val state = SceneTransitionLayoutState(TestScenes.SceneA) + val state = MutableSceneTransitionLayoutStateImpl(TestScenes.SceneA, SceneTransitions.Empty) assertThat(state.isTransitioning()).isFalse() assertThat(state.isTransitioning(from = TestScenes.SceneA)).isFalse() @@ -40,7 +44,7 @@ class SceneTransitionLayoutStateTest { @Test fun isTransitioningTo_transition() { - val state = SceneTransitionLayoutStateImpl(TestScenes.SceneA, SceneTransitions.Empty) + val state = MutableSceneTransitionLayoutStateImpl(TestScenes.SceneA, SceneTransitions.Empty) state.startTransition(transition(from = TestScenes.SceneA, to = TestScenes.SceneB)) assertThat(state.isTransitioning()).isTrue() @@ -50,4 +54,56 @@ class SceneTransitionLayoutStateTest { assertThat(state.isTransitioning(to = TestScenes.SceneA)).isFalse() assertThat(state.isTransitioning(from = TestScenes.SceneA, to = TestScenes.SceneB)).isTrue() } + + @Test + fun setTargetScene_idleToSameScene() = runMonotonicClockTest { + val state = MutableSceneTransitionLayoutState(TestScenes.SceneA) + assertThat(state.setTargetScene(TestScenes.SceneA, coroutineScope = this)).isNull() + } + + @Test + fun setTargetScene_idleToDifferentScene() = runMonotonicClockTest { + val state = MutableSceneTransitionLayoutState(TestScenes.SceneA) + val transition = state.setTargetScene(TestScenes.SceneB, coroutineScope = this) + assertThat(transition).isNotNull() + assertThat(state.transitionState).isEqualTo(transition) + + testScheduler.advanceUntilIdle() + assertThat(state.transitionState).isEqualTo(TransitionState.Idle(TestScenes.SceneB)) + } + + @Test + fun setTargetScene_transitionToSameScene() = runMonotonicClockTest { + val state = MutableSceneTransitionLayoutState(TestScenes.SceneA) + assertThat(state.setTargetScene(TestScenes.SceneB, coroutineScope = this)).isNotNull() + assertThat(state.setTargetScene(TestScenes.SceneB, coroutineScope = this)).isNull() + testScheduler.advanceUntilIdle() + assertThat(state.transitionState).isEqualTo(TransitionState.Idle(TestScenes.SceneB)) + } + + @Test + fun setTargetScene_transitionToDifferentScene() = runMonotonicClockTest { + val state = MutableSceneTransitionLayoutState(TestScenes.SceneA) + assertThat(state.setTargetScene(TestScenes.SceneB, coroutineScope = this)).isNotNull() + assertThat(state.setTargetScene(TestScenes.SceneC, coroutineScope = this)).isNotNull() + testScheduler.advanceUntilIdle() + assertThat(state.transitionState).isEqualTo(TransitionState.Idle(TestScenes.SceneC)) + } + + @Test + fun setTargetScene_coroutineScopeCancelled() = runMonotonicClockTest { + val state = MutableSceneTransitionLayoutState(TestScenes.SceneA) + + lateinit var transition: TransitionState.Transition + val job = + launch(start = CoroutineStart.UNDISPATCHED) { + transition = state.setTargetScene(TestScenes.SceneB, coroutineScope = this)!! + } + assertThat(state.transitionState).isEqualTo(transition) + + // Cancelling the scope/job still sets the state to Idle(targetScene). + job.cancel() + testScheduler.advanceUntilIdle() + assertThat(state.transitionState).isEqualTo(TransitionState.Idle(TestScenes.SceneB)) + } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt index 649e4991434e..efaea71f8d2c 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt @@ -63,7 +63,7 @@ class SceneTransitionLayoutTest { } private var currentScene by mutableStateOf(TestScenes.SceneA) - private val layoutState = SceneTransitionLayoutState(currentScene) + private lateinit var layoutState: SceneTransitionLayoutState // We use createAndroidComposeRule() here and not createComposeRule() because we need an // activity for testBack(). @@ -72,10 +72,14 @@ class SceneTransitionLayoutTest { /** The content under test. */ @Composable private fun TestContent() { + layoutState = + updateSceneTransitionLayoutState( + currentScene, + { currentScene = it }, + EmptyTestTransitions + ) + SceneTransitionLayout( - currentScene, - { currentScene = it }, - EmptyTestTransitions, state = layoutState, modifier = Modifier.size(LayoutSize), ) { diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt index 58d853ef5a00..1ec3c8ba2301 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt @@ -20,9 +20,6 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.platform.LocalViewConfiguration @@ -58,18 +55,15 @@ class SwipeToSceneTest { get() = Offset(0f, (LayoutHeight / 2).toPx()) } - private var currentScene by mutableStateOf(TestScenes.SceneA) - private val layoutState = SceneTransitionLayoutState(currentScene) - @get:Rule val rule = createComposeRule() + private fun layoutState(initialScene: SceneKey = TestScenes.SceneA) = + MutableSceneTransitionLayoutState(initialScene, EmptyTestTransitions) + /** The content under test. */ @Composable - private fun TestContent() { + private fun TestContent(layoutState: SceneTransitionLayoutState) { SceneTransitionLayout( - currentScene, - { currentScene = it }, - EmptyTestTransitions, state = layoutState, modifier = Modifier.size(LayoutWidth, LayoutHeight).testTag(TestElements.Foo.debugName), ) { @@ -109,9 +103,11 @@ class SwipeToSceneTest { // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is // detected as a drag event. var touchSlop = 0f + + val layoutState = layoutState() rule.setContent { touchSlop = LocalViewConfiguration.current.touchSlop - TestContent() + TestContent(layoutState) } assertThat(layoutState.transitionState).isInstanceOf(TransitionState.Idle::class.java) @@ -195,9 +191,10 @@ class SwipeToSceneTest { // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is // detected as a drag event. var touchSlop = 0f + val layoutState = layoutState() rule.setContent { touchSlop = LocalViewConfiguration.current.touchSlop - TestContent() + TestContent(layoutState) } assertThat(layoutState.transitionState).isInstanceOf(TransitionState.Idle::class.java) @@ -260,14 +257,14 @@ class SwipeToSceneTest { @Test fun multiPointerSwipe() { // Start at scene C. - currentScene = TestScenes.SceneC + val layoutState = layoutState(TestScenes.SceneC) // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is // detected as a drag event. var touchSlop = 0f rule.setContent { touchSlop = LocalViewConfiguration.current.touchSlop - TestContent() + TestContent(layoutState) } assertThat(layoutState.transitionState).isInstanceOf(TransitionState.Idle::class.java) @@ -299,14 +296,14 @@ class SwipeToSceneTest { @Test fun defaultEdgeSwipe() { // Start at scene C. - currentScene = TestScenes.SceneC + val layoutState = layoutState(TestScenes.SceneC) // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is // detected as a drag event. var touchSlop = 0f rule.setContent { touchSlop = LocalViewConfiguration.current.touchSlop - TestContent() + TestContent(layoutState) } assertThat(layoutState.transitionState).isInstanceOf(TransitionState.Idle::class.java) diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/RunMonotonicClockTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/RunMonotonicClockTest.kt index cb122dc8e25e..fbcd5b27836e 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/RunMonotonicClockTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/RunMonotonicClockTest.kt @@ -13,7 +13,7 @@ import kotlinx.coroutines.withContext * * The [TestCoroutineScheduler] is passed to provide the functionality to wait for idle. */ -@ExperimentalTestApi +@OptIn(ExperimentalTestApi::class) fun runMonotonicClockTest(block: suspend MonotonicClockTestScope.() -> Unit) = runTest { // We need a CoroutineScope (like a TestScope) to create a TestMonotonicFrameClock. withContext(TestMonotonicFrameClock(this)) { diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt index 41bde5298c66..3dfe65a4f736 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt @@ -22,6 +22,7 @@ import android.net.Uri import android.os.UserHandle import android.provider.Settings import androidx.annotation.OpenForTesting +import com.android.systemui.log.LogBuffer import com.android.systemui.log.core.LogLevel import com.android.systemui.log.core.LogcatOnlyMessageBuffer import com.android.systemui.log.core.Logger @@ -120,8 +121,9 @@ open class ClockRegistry( override fun onPluginAttached( manager: PluginLifecycleManager<ClockProviderPlugin> ): Boolean { - manager.isDebug = !keepAllLoaded - + manager.setLogFunc({ tag, msg -> + (clockBuffers?.infraMessageBuffer as LogBuffer?)?.log(tag, LogLevel.DEBUG, msg) + }) if (keepAllLoaded) { // Always load new plugins if requested return true diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt index cbcca55f6a34..f7751753cc18 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt @@ -100,9 +100,12 @@ class KeyguardSimPinViewControllerTest : SysuiTestCase() { mSelectedUserInteractor ) underTest.init() + underTest.onViewAttached() underTest.onResume(0) verify(keyguardUpdateMonitor) .registerCallback(updateMonitorCallbackArgumentCaptor.capture()) + reset(keyguardMessageAreaController) + reset(keyguardUpdateMonitor) } @Test @@ -110,25 +113,24 @@ class KeyguardSimPinViewControllerTest : SysuiTestCase() { underTest.onViewAttached() verify(keyguardMessageAreaController) .setMessage(context.resources.getString(R.string.keyguard_enter_your_pin), false) + verify(keyguardUpdateMonitor) + .registerCallback(any(KeyguardUpdateMonitorCallback::class.java)) } @Test fun onViewDetached() { underTest.onViewDetached() + verify(keyguardUpdateMonitor).removeCallback(any(KeyguardUpdateMonitorCallback::class.java)) } @Test fun onResume() { - reset(keyguardUpdateMonitor) underTest.onResume(KeyguardSecurityView.VIEW_REVEALED) - verify(keyguardUpdateMonitor) - .registerCallback(any(KeyguardUpdateMonitorCallback::class.java)) } @Test fun onPause() { underTest.onPause() - verify(keyguardUpdateMonitor).removeCallback(any(KeyguardUpdateMonitorCallback::class.java)) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt new file mode 100644 index 000000000000..721fc4906aba --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.communal.log + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.internal.logging.UiEventLogger +import com.android.systemui.SysuiTestCase +import com.android.systemui.communal.domain.interactor.CommunalInteractor +import com.android.systemui.communal.domain.interactor.CommunalInteractorFactory +import com.android.systemui.communal.shared.log.CommunalUiEvent +import com.android.systemui.communal.shared.model.CommunalSceneKey +import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.any +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(AndroidJUnit4::class) +class CommunalLoggerStartableTest : SysuiTestCase() { + @Mock private lateinit var uiEventLogger: UiEventLogger + + private lateinit var testScope: TestScope + private lateinit var communalInteractor: CommunalInteractor + private lateinit var underTest: CommunalLoggerStartable + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + val withDeps = CommunalInteractorFactory.create() + testScope = withDeps.testScope + communalInteractor = withDeps.communalInteractor + + underTest = + CommunalLoggerStartable( + testScope.backgroundScope, + communalInteractor, + uiEventLogger, + ) + underTest.start() + } + + @Test + fun transitionStateLogging_enterCommunalHub() = + testScope.runTest { + // Transition state is default (non-communal) + val transitionState = + MutableStateFlow<ObservableCommunalTransitionState>(idle(CommunalSceneKey.DEFAULT)) + communalInteractor.setTransitionState(transitionState) + runCurrent() + + // Verify nothing is logged from the default state + verify(uiEventLogger, never()).log(any()) + + // Start transition to communal + transitionState.value = transition(to = CommunalSceneKey.Communal) + runCurrent() + + // Verify UiEvent logged + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_START) + + // Finish transition to communal + transitionState.value = idle(CommunalSceneKey.Communal) + runCurrent() + + // Verify UiEvent logged + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_FINISH) + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SHOWN) + } + + @Test + fun transitionStateLogging_enterCommunalHub_canceled() = + testScope.runTest { + // Transition state is default (non-communal) + val transitionState = + MutableStateFlow<ObservableCommunalTransitionState>(idle(CommunalSceneKey.DEFAULT)) + communalInteractor.setTransitionState(transitionState) + runCurrent() + + // Verify nothing is logged from the default state + verify(uiEventLogger, never()).log(any()) + + // Start transition to communal + transitionState.value = transition(to = CommunalSceneKey.Communal) + runCurrent() + + // Verify UiEvent logged + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_START) + + // Cancel the transition + transitionState.value = idle(CommunalSceneKey.DEFAULT) + runCurrent() + + // Verify UiEvent logged + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_CANCEL) + + // Verify neither SHOWN nor GONE is logged + verify(uiEventLogger, never()).log(CommunalUiEvent.COMMUNAL_HUB_SHOWN) + verify(uiEventLogger, never()).log(CommunalUiEvent.COMMUNAL_HUB_GONE) + } + + @Test + fun transitionStateLogging_exitCommunalHub() = + testScope.runTest { + // Transition state is communal + val transitionState = + MutableStateFlow<ObservableCommunalTransitionState>(idle(CommunalSceneKey.Communal)) + communalInteractor.setTransitionState(transitionState) + runCurrent() + + // Verify SHOWN is logged when it's the default state + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SHOWN) + + // Start transition from communal + transitionState.value = transition(from = CommunalSceneKey.Communal) + runCurrent() + + // Verify UiEvent logged + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_START) + + // Finish transition to communal + transitionState.value = idle(CommunalSceneKey.DEFAULT) + runCurrent() + + // Verify UiEvent logged + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_FINISH) + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_GONE) + } + + @Test + fun transitionStateLogging_exitCommunalHub_canceled() = + testScope.runTest { + // Transition state is communal + val transitionState = + MutableStateFlow<ObservableCommunalTransitionState>(idle(CommunalSceneKey.Communal)) + communalInteractor.setTransitionState(transitionState) + runCurrent() + + // Clear the initial SHOWN event from the logger + clearInvocations(uiEventLogger) + + // Start transition from communal + transitionState.value = transition(from = CommunalSceneKey.Communal) + runCurrent() + + // Verify UiEvent logged + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_START) + + // Cancel the transition + transitionState.value = idle(CommunalSceneKey.Communal) + runCurrent() + + // Verify UiEvent logged + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_CANCEL) + + // Verify neither SHOWN nor GONE is logged + verify(uiEventLogger, never()).log(CommunalUiEvent.COMMUNAL_HUB_SHOWN) + verify(uiEventLogger, never()).log(CommunalUiEvent.COMMUNAL_HUB_GONE) + } + + private fun transition( + from: CommunalSceneKey = CommunalSceneKey.DEFAULT, + to: CommunalSceneKey = CommunalSceneKey.DEFAULT, + ): ObservableCommunalTransitionState.Transition { + return ObservableCommunalTransitionState.Transition( + fromScene = from, + toScene = to, + progress = emptyFlow(), + isInitiatedByUserInput = true, + isUserInputOngoing = emptyFlow(), + ) + } + + private fun idle(sceneKey: CommunalSceneKey): ObservableCommunalTransitionState.Idle { + return ObservableCommunalTransitionState.Idle(sceneKey) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt index c638e1ea89ee..ff6fd43745df 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt @@ -26,6 +26,7 @@ import android.provider.Settings import android.widget.RemoteViews import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.internal.logging.UiEventLogger import com.android.systemui.SysuiTestCase import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository import com.android.systemui.communal.data.repository.FakeCommunalRepository @@ -33,6 +34,7 @@ import com.android.systemui.communal.data.repository.FakeCommunalTutorialReposit import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository import com.android.systemui.communal.domain.interactor.CommunalInteractorFactory import com.android.systemui.communal.domain.model.CommunalContentModel +import com.android.systemui.communal.shared.log.CommunalUiEvent import com.android.systemui.communal.shared.model.CommunalWidgetContentModel import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel import com.android.systemui.coroutines.collectLastValue @@ -54,6 +56,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @@ -64,6 +67,7 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { @Mock private lateinit var shadeViewController: ShadeViewController @Mock private lateinit var powerManager: PowerManager @Mock private lateinit var appWidgetHost: AppWidgetHost + @Mock private lateinit var uiEventLogger: UiEventLogger private val kosmos = testKosmos() private val testScope = kosmos.testScope @@ -96,6 +100,7 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { Provider { shadeViewController }, powerManager, mediaHost, + uiEventLogger, ) } @@ -203,4 +208,22 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { val providerTwo = ComponentName("pkg.test", "testWidget2") underTest.onAddWidget(componentName = providerTwo, priority = 0) } + + @Test + fun reorderWidget_uiEventLogging_start() { + underTest.onReorderWidgetStart() + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_START) + } + + @Test + fun reorderWidget_uiEventLogging_end() { + underTest.onReorderWidgetEnd() + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_FINISH) + } + + @Test + fun reorderWidget_uiEventLogging_cancel() { + underTest.onReorderWidgetCancel() + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_CANCEL) + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt index 16e0bc00ad35..8e3f66464de6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt @@ -31,6 +31,7 @@ import com.android.systemui.communal.domain.interactor.CommunalInteractorFactory import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.shared.model.CommunalWidgetContentModel import com.android.systemui.communal.ui.viewmodel.CommunalViewModel +import com.android.systemui.communal.ui.viewmodel.CommunalViewModel.Companion.POPUP_AUTO_HIDE_TIMEOUT_MS import com.android.systemui.communal.widgets.WidgetInteractionHandler import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository @@ -41,7 +42,9 @@ import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import javax.inject.Provider +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test @@ -50,6 +53,7 @@ import org.mockito.Mock import org.mockito.Mockito import org.mockito.MockitoAnnotations +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class CommunalViewModelTest : SysuiTestCase() { @@ -84,6 +88,7 @@ class CommunalViewModelTest : SysuiTestCase() { underTest = CommunalViewModel( + testScope, withDeps.communalInteractor, WidgetInteractionHandler(mock()), withDeps.tutorialInteractor, @@ -159,4 +164,44 @@ class CommunalViewModelTest : SysuiTestCase() { assertThat(communalContent?.get(4)) .isInstanceOf(CommunalContentModel.CtaTileInViewMode::class.java) } + + @Test + fun dismissCta_hidesCtaTileAndShowsPopup_thenHidesPopupAfterTimeout() = + testScope.runTest { + tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) + communalRepository.setCtaTileInViewModeVisibility(true) + + val communalContent by collectLastValue(underTest.communalContent) + val isPopupOnDismissCtaShowing by collectLastValue(underTest.isPopupOnDismissCtaShowing) + + assertThat(communalContent?.size).isEqualTo(1) + assertThat(communalContent?.get(0)) + .isInstanceOf(CommunalContentModel.CtaTileInViewMode::class.java) + + underTest.onDismissCtaTile() + + // hide CTA tile and show the popup + assertThat(communalContent).isEmpty() + assertThat(isPopupOnDismissCtaShowing).isEqualTo(true) + + // hide popup after time elapsed + advanceTimeBy(POPUP_AUTO_HIDE_TIMEOUT_MS) + assertThat(isPopupOnDismissCtaShowing).isEqualTo(false) + } + + @Test + fun popup_onDismiss_hidesImmediately() = + testScope.runTest { + tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) + communalRepository.setCtaTileInViewModeVisibility(true) + + val isPopupOnDismissCtaShowing by collectLastValue(underTest.isPopupOnDismissCtaShowing) + + underTest.onDismissCtaTile() + assertThat(isPopupOnDismissCtaShowing).isEqualTo(true) + + // dismiss the popup directly + underTest.onHidePopupAfterDismissCta() + assertThat(isPopupOnDismissCtaShowing).isEqualTo(false) + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java index 3d1efa59a11b..5827671e22b8 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java @@ -293,8 +293,8 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase { } /** - * Verifies that swiping up when the lock pattern is not secure does not consume the scroll - * gesture or expand. + * Verifies that swiping up when the lock pattern is not secure dismissed dream and consumes + * the gesture. */ @Test public void testSwipeUp_keyguardNotSecure_doesNotExpand() { @@ -314,11 +314,44 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase { reset(mScrimController); + // Scroll gesture is consumed. + assertThat(gestureListener.onScroll(event1, event2, 0, distanceY)) + .isTrue(); + // We should not expand since the keyguard is not secure + verify(mScrimController, never()).expand(any()); + // Since we are swiping up, we should wake from dreams. + verify(mCentralSurfaces).awakenDreams(); + } + + /** + * Verifies that swiping down when the lock pattern is not secure does not dismiss the dream. + */ + @Test + public void testSwipeDown_keyguardNotSecure_doesNotExpand() { + when(mLockPatternUtils.isSecure(CURRENT_USER_INFO.id)).thenReturn(false); + mTouchHandler.onSessionStart(mTouchSession); + ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor = + ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); + verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture()); + + final OnGestureListener gestureListener = gestureListenerCaptor.getValue(); + + final float distanceY = SCREEN_HEIGHT_PX * 0.3f; + // Swiping down near the bottom of the screen where the touch initiation region is. + final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, + 0, SCREEN_HEIGHT_PX - distanceY, 0); + final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, + 0, SCREEN_HEIGHT_PX, 0); + + reset(mScrimController); + // Scroll gesture is not consumed. assertThat(gestureListener.onScroll(event1, event2, 0, distanceY)) - .isFalse(); + .isTrue(); // We should not expand since the keyguard is not secure verify(mScrimController, never()).expand(any()); + // Since we are swiping down, we should not dismiss the dream. + verify(mCentralSurfaces, never()).awakenDreams(); } private void verifyScroll(float percent, Direction direction, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt index b483085cf1e5..9bccf4e26e87 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt @@ -82,6 +82,7 @@ class LightRevealScrimInteractorTest : SysuiTestCase() { keyguardTransitionInteractor, fakeLightRevealScrimRepository, testScope.backgroundScope, + mock(), mock() ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelTest.kt index e7037a682cca..9daf1860ebb8 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelTest.kt @@ -94,7 +94,7 @@ class AlternateBouncerToAodTransitionViewModelTest : SysuiTestCase() { testScope, ) - assertThat(values.size).isEqualTo(5) + assertThat(values.size).isEqualTo(6) values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt index e141c2b3107f..f1690dafe75a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt @@ -95,7 +95,7 @@ class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() { testScope, ) - assertThat(values.size).isEqualTo(6) + assertThat(values.size).isEqualTo(7) values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) } } @@ -117,7 +117,7 @@ class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() { testScope, ) - assertThat(values.size).isEqualTo(3) + assertThat(values.size).isEqualTo(4) values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) } } @@ -232,7 +232,7 @@ class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() { testScope, ) - assertThat(values.size).isEqualTo(4) + assertThat(values.size).isEqualTo(5) values.forEach { assertThat(it).isIn(Range.closed(-100f, 0f)) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt index 897ce6d305b6..f763a6790b51 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt @@ -59,9 +59,9 @@ class GoneToDreamingTransitionViewModelTest : SysuiTestCase() { testScope, ) - // Only three values should be present, since the dream overlay runs for a small + // Only five values should be present, since the dream overlay runs for a small // fraction of the overall animation time - assertThat(values.size).isEqualTo(4) + assertThat(values.size).isEqualTo(5) values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) } } @@ -84,7 +84,7 @@ class GoneToDreamingTransitionViewModelTest : SysuiTestCase() { testScope, ) - assertThat(values.size).isEqualTo(4) + assertThat(values.size).isEqualTo(5) values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt index 4843f8ba4249..74025fd6e100 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt @@ -73,9 +73,9 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() { testScope = testScope, ) - // Only three values should be present, since the dream overlay runs for a small + // Only five values should be present, since the dream overlay runs for a small // fraction of the overall animation time - assertThat(values.size).isEqualTo(4) + assertThat(values.size).isEqualTo(5) values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) } } @@ -98,10 +98,10 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() { testScope = testScope, ) - assertThat(values.size).isEqualTo(5) + assertThat(values.size).isEqualTo(6) values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) } // Validate finished value - assertThat(values[4]).isEqualTo(0f) + assertThat(values[5]).isEqualTo(0f) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt index a1b8aab402a7..6fcb0c11edad 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt @@ -74,9 +74,9 @@ class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() { ), testScope = testScope, ) - // Only 3 values should be present, since the dream overlay runs for a small fraction + // Only 5 values should be present, since the dream overlay runs for a small fraction // of the overall animation time - assertThat(values.size).isEqualTo(4) + assertThat(values.size).isEqualTo(5) values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt index 2111ad5d975e..639114c15000 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt @@ -33,6 +33,7 @@ import com.android.systemui.testKosmos import com.google.common.collect.Range import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @@ -54,6 +55,7 @@ class OccludedToLockscreenTransitionViewModelTest : SysuiTestCase() { fun lockscreenFadeIn() = testScope.runTest { val values by collectValues(underTest.lockscreenAlpha) + runCurrent() keyguardTransitionRepository.sendTransitionSteps( listOf( @@ -83,6 +85,7 @@ class OccludedToLockscreenTransitionViewModelTest : SysuiTestCase() { 100 ) val values by collectValues(underTest.lockscreenTranslationY) + runCurrent() keyguardTransitionRepository.sendTransitionSteps( listOf( @@ -95,7 +98,7 @@ class OccludedToLockscreenTransitionViewModelTest : SysuiTestCase() { testScope, ) - assertThat(values.size).isEqualTo(4) + assertThat(values.size).isEqualTo(5) values.forEach { assertThat(it).isIn(Range.closed(-100f, 0f)) } } @@ -107,6 +110,7 @@ class OccludedToLockscreenTransitionViewModelTest : SysuiTestCase() { 100 ) val values by collectValues(underTest.lockscreenTranslationY) + runCurrent() keyguardTransitionRepository.sendTransitionStep(step(0.5f, TransitionState.CANCELED)) @@ -117,6 +121,7 @@ class OccludedToLockscreenTransitionViewModelTest : SysuiTestCase() { fun deviceEntryParentViewFadeIn() = testScope.runTest { val values by collectValues(underTest.deviceEntryParentViewAlpha) + runCurrent() keyguardTransitionRepository.sendTransitionSteps( listOf( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt index 90b83620084c..30b87bbbcf11 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt @@ -33,6 +33,7 @@ import com.android.systemui.testKosmos import com.android.systemui.util.mockito.whenever import com.google.common.collect.Range import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test @@ -65,6 +66,7 @@ class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() { fun bouncerAlpha() = testScope.runTest { val values by collectValues(underTest.bouncerAlpha) + runCurrent() keyguardTransitionRepository.sendTransitionSteps( listOf( @@ -83,6 +85,7 @@ class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() { fun bouncerAlpha_runDimissFromKeyguard() = testScope.runTest { val values by collectValues(underTest.bouncerAlpha) + runCurrent() whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true) @@ -95,7 +98,7 @@ class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() { testScope, ) - assertThat(values.size).isEqualTo(1) + assertThat(values.size).isEqualTo(2) values.forEach { assertThat(it).isEqualTo(0f) } } @@ -103,11 +106,12 @@ class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() { fun lockscreenAlpha() = testScope.runTest { val values by collectValues(underTest.lockscreenAlpha) + runCurrent() keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED)) keyguardTransitionRepository.sendTransitionStep(step(1f)) - assertThat(values.size).isEqualTo(1) + assertThat(values.size).isEqualTo(2) values.forEach { assertThat(it).isEqualTo(0f) } } @@ -115,13 +119,14 @@ class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() { fun lockscreenAlpha_runDimissFromKeyguard() = testScope.runTest { val values by collectValues(underTest.lockscreenAlpha) + runCurrent() sysuiStatusBarStateController.setLeaveOpenOnKeyguardHide(true) keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED)) keyguardTransitionRepository.sendTransitionStep(step(1f)) - assertThat(values.size).isEqualTo(1) + assertThat(values.size).isEqualTo(2) values.forEach { assertThat(it).isEqualTo(1f) } } @@ -129,13 +134,14 @@ class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() { fun lockscreenAlpha_leaveShadeOpen() = testScope.runTest { val values by collectValues(underTest.lockscreenAlpha) + runCurrent() sysuiStatusBarStateController.setLeaveOpenOnKeyguardHide(true) keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED)) keyguardTransitionRepository.sendTransitionStep(step(1f)) - assertThat(values.size).isEqualTo(1) + assertThat(values.size).isEqualTo(2) values.forEach { assertThat(it).isEqualTo(1f) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java index 01fe40f6dff6..d0e05fa60114 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java @@ -42,19 +42,15 @@ import android.content.Intent; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.net.Uri; -import android.os.Handler; -import android.os.Looper; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; import android.util.SparseArray; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.widget.LockPatternUtils; -import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dump.DumpManager; @@ -86,8 +82,7 @@ import java.util.Collection; import java.util.concurrent.Executor; @SmallTest -@RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper +@RunWith(AndroidJUnit4.class) public class NotificationLockscreenUserManagerMainThreadTest extends SysuiTestCase { @Mock private NotificationPresenter mPresenter; @@ -128,6 +123,7 @@ public class NotificationLockscreenUserManagerMainThreadTest extends SysuiTestCa private NotificationEntry mSecondaryUserNotif; private NotificationEntry mWorkProfileNotif; private final FakeFeatureFlagsClassic mFakeFeatureFlags = new FakeFeatureFlagsClassic(); + private Executor mMainExecutor = Runnable::run; // Direct executor private Executor mBackgroundExecutor = Runnable::run; // Direct executor @Before @@ -156,8 +152,6 @@ public class NotificationLockscreenUserManagerMainThreadTest extends SysuiTestCa mSecondaryUser)); when(mUserManager.getProfilesIncludingCommunal(mSecondaryUser.id)).thenReturn( Lists.newArrayList(mSecondaryUser, mCommunalUser)); - mDependency.injectTestDependency(Dependency.MAIN_HANDLER, - Handler.createAsync(Looper.myLooper())); Notification notifWithPrivateVisibility = new Notification(); notifWithPrivateVisibility.visibility = Notification.VISIBILITY_PRIVATE; @@ -378,28 +372,24 @@ public class NotificationLockscreenUserManagerMainThreadTest extends SysuiTestCa // first call explicitly sets user 0 to not public; notifies mLockscreenUserManager.updatePublicMode(); - TestableLooper.get(this).processAllMessages(); assertFalse(mLockscreenUserManager.isLockscreenPublicMode(0)); verify(listener).onNotificationStateChanged(); clearInvocations(listener); // calling again has no changes; does not notify mLockscreenUserManager.updatePublicMode(); - TestableLooper.get(this).processAllMessages(); assertFalse(mLockscreenUserManager.isLockscreenPublicMode(0)); verify(listener, never()).onNotificationStateChanged(); // Calling again with keyguard now showing makes user 0 public; notifies when(mKeyguardStateController.isShowing()).thenReturn(true); mLockscreenUserManager.updatePublicMode(); - TestableLooper.get(this).processAllMessages(); assertTrue(mLockscreenUserManager.isLockscreenPublicMode(0)); verify(listener).onNotificationStateChanged(); clearInvocations(listener); // calling again has no changes; does not notify mLockscreenUserManager.updatePublicMode(); - TestableLooper.get(this).processAllMessages(); assertTrue(mLockscreenUserManager.isLockscreenPublicMode(0)); verify(listener, never()).onNotificationStateChanged(); } @@ -595,8 +585,7 @@ public class NotificationLockscreenUserManagerMainThreadTest extends SysuiTestCa (() -> mOverviewProxyService), NotificationLockscreenUserManagerMainThreadTest.this.mKeyguardManager, mStatusBarStateController, - Handler.createAsync(Looper.myLooper()), - Handler.createAsync(Looper.myLooper()), + mMainExecutor, mBackgroundExecutor, mDeviceProvisionedController, mKeyguardStateController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java index 757f16cac227..bcc0710359cc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java @@ -29,10 +29,11 @@ import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE; import static android.os.UserHandle.USER_ALL; import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS; import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS; -import static com.android.systemui.util.concurrency.MockExecutorHandlerKt.mockExecutorHandler; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -62,13 +63,11 @@ import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.FlagsParameterization; import android.provider.Settings; -import android.testing.TestableLooper; import android.util.SparseArray; import androidx.test.filters.SmallTest; import com.android.internal.widget.LockPatternUtils; -import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dump.DumpManager; @@ -88,6 +87,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.settings.FakeSettings; import com.android.systemui.util.time.FakeSystemClock; + import com.google.android.collect.Lists; import org.junit.After; @@ -109,7 +109,6 @@ import platform.test.runner.parameterized.Parameters; @SmallTest @RunWith(ParameterizedAndroidJunit4.class) -@TestableLooper.RunWithLooper public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Parameters(name = "{0}") @@ -197,8 +196,6 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { mSecondaryUser)); when(mUserManager.getProfilesIncludingCommunal(mSecondaryUser.id)).thenReturn( Lists.newArrayList(mSecondaryUser, mCommunalUser)); - mDependency.injectTestDependency(Dependency.MAIN_HANDLER, - mockExecutorHandler(mMainExecutor)); Notification notifWithPrivateVisibility = new Notification(); notifWithPrivateVisibility.visibility = VISIBILITY_PRIVATE; @@ -949,8 +946,7 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { (() -> mOverviewProxyService), NotificationLockscreenUserManagerTest.this.mKeyguardManager, mStatusBarStateController, - mockExecutorHandler(mMainExecutor), - mockExecutorHandler(mBackgroundExecutor), + mMainExecutor, mBackgroundExecutor, mDeviceProvisionedController, mKeyguardStateController, diff --git a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java index 3e5e8a0d462e..f0ce4604309d 100644 --- a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java +++ b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java @@ -18,6 +18,8 @@ package com.android.systemui.plugins; import android.content.ComponentName; +import java.util.function.BiConsumer; + /** * Provides the ability for consumers to control plugin lifecycle. * @@ -33,11 +35,8 @@ public interface PluginLifecycleManager<T extends Plugin> { /** Returns the currently loaded plugin instance (if plugin is loaded) */ T getPlugin(); - /** Returns true if the lifecycle manager should log debug messages */ - boolean getIsDebug(); - - /** Sets whether or not hte lifecycle manager should log debug messages */ - void setIsDebug(boolean debug); + /** Log tag and messages will be sent to the provided Consumer */ + void setLogFunc(BiConsumer<String, String> logConsumer); /** returns true if the plugin is currently loaded */ default boolean isLoaded() { diff --git a/packages/SystemUI/res/drawable/stat_sys_no_internet_branded_vpn.xml b/packages/SystemUI/res/drawable/stat_sys_no_internet_branded_vpn.xml new file mode 100644 index 000000000000..2161a62ada9c --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_no_internet_branded_vpn.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2023, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT 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="17dp" + android:height="17dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M12.09,9C11.11,7.5 9.43,6.5 7.5,6.5C4.46,6.5 2,8.96 2,12c0,3.04 2.46,5.5 5.5,5.5c1.93,0 3.61,-1 4.59,-2.5H14v3h4V9H12.09zM18,13hv3h-2v-3h-5.16c-0.43,1.44 -1.76,2.5 -3.34,2.5C5.57,15.5 4,13.93 4,12c0,-1.93 1.57,-3.5 3.5,-3.5c1.58,0 2.9,1.06 3.34,2.5H18V13z"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M7.5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M22,10h-2v8h2V10z"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M22,20h-2v2h2V20z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/stat_sys_no_internet_vpn_ic.xml b/packages/SystemUI/res/drawable/stat_sys_no_internet_vpn_ic.xml new file mode 100644 index 000000000000..2161a62ada9c --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_no_internet_vpn_ic.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2023, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT 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="17dp" + android:height="17dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M12.09,9C11.11,7.5 9.43,6.5 7.5,6.5C4.46,6.5 2,8.96 2,12c0,3.04 2.46,5.5 5.5,5.5c1.93,0 3.61,-1 4.59,-2.5H14v3h4V9H12.09zM18,13hv3h-2v-3h-5.16c-0.43,1.44 -1.76,2.5 -3.34,2.5C5.57,15.5 4,13.93 4,12c0,-1.93 1.57,-3.5 3.5,-3.5c1.58,0 2.9,1.06 3.34,2.5H18V13z"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M7.5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M22,10h-2v8h2V10z"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M22,20h-2v2h2V20z"/> +</vector> diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml index 6d7ce0623817..e602d6c8848d 100644 --- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml +++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml @@ -107,4 +107,13 @@ <include layout="@layout/ambient_indication" android:id="@id/ambient_indication_container" /> + + <FrameLayout + android:id="@+id/smartspace_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="bottom" + android:layout_marginBottom="@dimen/ambient_indication_margin_bottom" + android:visibility="gone"> + </FrameLayout> </com.android.systemui.statusbar.phone.KeyguardBottomAreaView> diff --git a/packages/SystemUI/res/layout/keyguard_status_bar.xml b/packages/SystemUI/res/layout/keyguard_status_bar.xml index fc0bf242dc1e..4cb7591a3502 100644 --- a/packages/SystemUI/res/layout/keyguard_status_bar.xml +++ b/packages/SystemUI/res/layout/keyguard_status_bar.xml @@ -23,7 +23,6 @@ android:layout_width="match_parent" android:layout_height="@dimen/status_bar_header_height_keyguard" android:baselineAligned="false" - android:gravity="center_vertical" > <LinearLayout diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 64a1d248b221..17719d11345b 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -1002,4 +1002,7 @@ <item>com.android.switchaccess.SwitchAccessService</item> <item>com.google.android.apps.accessibility.voiceaccess.JustSpeakService</item> </string-array> + + <!-- Whether to use a machine learning model for back gesture falsing. --> + <bool name="config_useBackGestureML">true</bool> </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 3f11faebffb1..f55a11e2f3f0 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -449,11 +449,11 @@ <!-- Content description after successful auth when confirmation required --> <string name="fingerprint_dialog_authenticated_confirmation">Press the unlock icon to continue</string> <!-- Message shown to inform the user a face cannot be recognized and fingerprint should instead be used.[CHAR LIMIT=50] --> - <string name="fingerprint_dialog_use_fingerprint_instead">Can\u2019t recognize face. Use fingerprint instead.</string> + <string name="fingerprint_dialog_use_fingerprint_instead">Face not recognized. Use fingerprint instead.</string> <!-- Message shown to inform the user a face cannot be recognized and fingerprint should instead be used.[CHAR LIMIT=50] --> <string name="keyguard_face_failed_use_fp">@string/fingerprint_dialog_use_fingerprint_instead</string> <!-- Message shown to inform the user a face cannot be recognized. [CHAR LIMIT=25] --> - <string name="keyguard_face_failed">Can\u2019t recognize face</string> + <string name="keyguard_face_failed">Face not recognized</string> <!-- Message shown to suggest using fingerprint sensor to authenticate after another biometric failed. [CHAR LIMIT=25] --> <string name="keyguard_suggest_fingerprint">Use fingerprint instead</string> <!-- Message shown to inform the user that face unlock is not available. [CHAR LIMIT=59] --> @@ -1087,6 +1087,8 @@ <string name="cta_label_to_edit_widget">Add, remove, and reorder your widgets in this space</string> <!-- Label for CTA tile that opens widget picker on click in edit mode [CHAR LIMIT=50] --> <string name="cta_label_to_open_widget_picker">Add more widgets</string> + <!-- Text for the popup to be displayed after dismissing the CTA tile. [CHAR LIMIT=50] --> + <string name="popup_on_dismiss_cta_tile_text">Long press to customize widgets</string> <!-- Description for the button that removes a widget on click. [CHAR LIMIT=50] --> <string name="button_to_remove_widget">Remove</string> <!-- Text for the button that launches the hub mode widget picker. [CHAR LIMIT=50] --> @@ -2282,6 +2284,8 @@ <string name="notification_channel_storage">Storage</string> <!-- Title for the notification channel for hints and suggestions. [CHAR LIMIT=NONE] --> <string name="notification_channel_hints">Hints</string> + <!-- Title for the notification channel for accessibility related (i.e. accessibility floating menu). [CHAR LIMIT=NONE] --> + <string name="notification_channel_accessibility">Accessibility</string> <!-- App label of the instant apps notification [CHAR LIMIT=60] --> <string name="instant_apps">Instant Apps</string> @@ -2552,6 +2556,11 @@ <string name="accessibility_floating_button_docking_tooltip">Move button to the edge to hide it temporarily</string> <!-- Text for the undo action button of the message view of the accessibility floating menu to perform undo operation. [CHAR LIMIT=30]--> <string name="accessibility_floating_button_undo">Undo</string> + <!-- Notification title shown when accessibility floating button is in hidden state. [CHAR LIMIT=NONE] --> + <string name="accessibility_floating_button_hidden_notification_title">Accessibility button hidden</string> + <!-- Notification content text to explain user can tap notification to bring back accessibility floating button. [CHAR LIMIT=NONE] --> + <string name="accessibility_floating_button_hidden_notification_text">Tap to show accessibility button</string> + <!-- Text for the message view with undo action of the accessibility floating menu to show which feature shortcut was removed. [CHAR LIMIT=30]--> <string name="accessibility_floating_button_undo_message_label_text"><xliff:g id="feature name" example="Magnification">%s</xliff:g> shortcut removed</string> <!-- Text for the message view with undo action of the accessibility floating menu to show how many features shortcuts were removed. [CHAR LIMIT=30]--> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 3f026a4cec8a..7d7c050a666c 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -921,7 +921,7 @@ <style name="Theme.ControlsActivity" parent="@android:style/Theme.DeviceDefault.NoActionBar"> <item name="android:windowActivityTransitions">true</item> <item name="android:windowContentTransitions">false</item> - <item name="android:windowIsTranslucent">false</item> + <item name="android:windowIsTranslucent">true</item> <item name="android:windowBackground">@android:color/black</item> <item name="android:windowAnimationStyle">@null</item> <item name="android:statusBarColor">@android:color/black</item> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java index 387f2e1aa430..87cc86f18fdc 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java @@ -38,6 +38,7 @@ import dalvik.system.PathClassLoader; import java.io.File; import java.util.ArrayList; import java.util.List; +import java.util.function.BiConsumer; import java.util.function.Supplier; /** @@ -57,7 +58,7 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager private final PluginFactory<T> mPluginFactory; private final String mTag; - private boolean mIsDebug = false; + private BiConsumer<String, String> mLogConsumer = null; private Context mPluginContext; private T mPlugin; @@ -86,17 +87,13 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager return mTag; } - public boolean getIsDebug() { - return mIsDebug; + public void setLogFunc(BiConsumer logConsumer) { + mLogConsumer = logConsumer; } - public void setIsDebug(boolean debug) { - mIsDebug = debug; - } - - private void logDebug(String message) { - if (mIsDebug) { - Log.i(mTag, message); + private void log(String message) { + if (mLogConsumer != null) { + mLogConsumer.accept(mTag, message); } } @@ -105,19 +102,19 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager boolean loadPlugin = mListener.onPluginAttached(this); if (!loadPlugin) { if (mPlugin != null) { - logDebug("onCreate: auto-unload"); + log("onCreate: auto-unload"); unloadPlugin(); } return; } if (mPlugin == null) { - logDebug("onCreate auto-load"); + log("onCreate auto-load"); loadPlugin(); return; } - logDebug("onCreate: load callbacks"); + log("onCreate: load callbacks"); mPluginFactory.checkVersion(mPlugin); if (!(mPlugin instanceof PluginFragment)) { // Only call onCreate for plugins that aren't fragments, as fragments @@ -129,7 +126,7 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager /** Alerts listener and plugin that the plugin is being shutdown. */ public synchronized void onDestroy() { - logDebug("onDestroy"); + log("onDestroy"); unloadPlugin(); mListener.onPluginDetached(this); } @@ -145,7 +142,7 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager */ public synchronized void loadPlugin() { if (mPlugin != null) { - logDebug("Load request when already loaded"); + log("Load request when already loaded"); return; } @@ -157,7 +154,7 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager return; } - logDebug("Loaded plugin; running callbacks"); + log("Loaded plugin; running callbacks"); mPluginFactory.checkVersion(mPlugin); if (!(mPlugin instanceof PluginFragment)) { // Only call onCreate for plugins that aren't fragments, as fragments @@ -174,11 +171,11 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager */ public synchronized void unloadPlugin() { if (mPlugin == null) { - logDebug("Unload request when already unloaded"); + log("Unload request when already unloaded"); return; } - logDebug("Unloading plugin, running callbacks"); + log("Unloading plugin, running callbacks"); mListener.onPluginUnloaded(mPlugin, this); if (!(mPlugin instanceof PluginFragment)) { // Only call onDestroy for plugins that aren't fragments, as fragments diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl index 823bd2d147d3..7088829bd2a3 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl @@ -88,18 +88,23 @@ oneway interface IOverviewProxy { */ void onNavButtonsDarkIntensityChanged(float darkIntensity) = 22; - /** - * Sent when split keyboard shortcut is triggered to enter stage split. - */ - void enterStageSplitFromRunningApp(boolean leftOrTop) = 25; - - /** - * Sent when the surface for navigation bar is created or changed - */ - void onNavigationBarSurface(in SurfaceControl surface) = 26; - - /** - * Sent when the task bar stash state is toggled. - */ - void onTaskbarToggled() = 27; + /** + * Sent when when navigation bar luma sampling is enabled or disabled. + */ + void onNavigationBarLumaSamplingEnabled(int displayId, boolean enable) = 23; + + /** + * Sent when split keyboard shortcut is triggered to enter stage split. + */ + void enterStageSplitFromRunningApp(boolean leftOrTop) = 25; + + /** + * Sent when the surface for navigation bar is created or changed + */ + void onNavigationBarSurface(in SurfaceControl surface) = 26; + + /** + * Sent when the task bar stash state is toggled. + */ + void onTaskbarToggled() = 27; } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index 74b975cb7232..de7c12db6b11 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -22,6 +22,7 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static com.android.keyguard.KeyguardClockSwitch.LARGE; import static com.android.keyguard.KeyguardClockSwitch.SMALL; import static com.android.systemui.Flags.migrateClocksToBlueprint; +import static com.android.systemui.Flags.smartspaceRelocateToBottom; import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED; import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; @@ -421,6 +422,10 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS return; } + if (smartspaceRelocateToBottom()) { + return; + } + mSmartspaceView = mSmartspaceController.buildAndConnectView(mView); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( MATCH_PARENT, WRAP_CONTENT); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java index c5e70703cd2b..5729119a582a 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java @@ -105,6 +105,13 @@ public class KeyguardSimPinViewController @Override protected void onViewAttached() { super.onViewAttached(); + mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback); + } + + @Override + protected void onViewDetached() { + super.onViewDetached(); + mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback); } @Override @@ -128,14 +135,12 @@ public class KeyguardSimPinViewController @Override public void onResume(int reason) { super.onResume(reason); - mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback); mView.resetState(); } @Override public void onPause() { super.onPause(); - mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback); // dismiss the dialog. if (mSimUnlockProgressDialog != null) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 2a54a4eee657..d372f5a616b1 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -20,6 +20,7 @@ import static androidx.constraintlayout.widget.ConstraintSet.END; import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID; import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION; +import static com.android.systemui.Flags.migrateClocksToBlueprint; import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; import android.animation.Animator; @@ -492,7 +493,12 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV boolean splitShadeEnabled, boolean shouldBeCentered, boolean animate) { - mKeyguardClockSwitchController.setSplitShadeCentered(splitShadeEnabled && shouldBeCentered); + if (migrateClocksToBlueprint()) { + mKeyguardInteractor.setClockShouldBeCentered(mSplitShadeEnabled && shouldBeCentered); + } else { + mKeyguardClockSwitchController.setSplitShadeCentered( + splitShadeEnabled && shouldBeCentered); + } if (mStatusViewCentered == shouldBeCentered) { return; } @@ -548,8 +554,9 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV ClockController clock = mKeyguardClockSwitchController.getClock(); boolean customClockAnimation = clock != null && clock.getLargeClock().getConfig().getHasCustomPositionUpdatedAnimation(); - - if (customClockAnimation) { + // When migrateClocksToBlueprint is on, customized clock animation is conducted in + // KeyguardClockViewBinder + if (customClockAnimation && !migrateClocksToBlueprint()) { // Find the clock, so we can exclude it from this transition. FrameLayout clockContainerView = mView.findViewById(R.id.lockscreen_clock_view_large); diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java index 49e0df6f6afc..568b24dbd4f3 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java @@ -24,6 +24,7 @@ import android.view.MotionEvent; import androidx.annotation.NonNull; import androidx.dynamicanimation.animation.DynamicAnimation; +import com.android.internal.annotations.VisibleForTesting; import com.android.wm.shell.common.bubbles.DismissView; import com.android.wm.shell.common.magnetictarget.MagnetizedObject; @@ -116,6 +117,11 @@ class DragToInteractAnimationController { mMagnetizedObject.setMagnetListener(magnetListener); } + @VisibleForTesting + MagnetizedObject.MagnetListener getMagnetListener() { + return mMagnetizedObject.getMagnetListener(); + } + void maybeConsumeDownMotionEvent(MotionEvent event) { mMagnetizedObject.maybeConsumeMotionEvent(event); } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactory.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactory.java new file mode 100644 index 000000000000..b5eeaa18447e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactory.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.accessibility.floatingmenu; + +import android.app.Notification; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.UserHandle; + +import com.android.systemui.res.R; +import com.android.systemui.util.NotificationChannels; + +class MenuNotificationFactory { + public static final String ACTION_UNDO = + "com.android.systemui.accessibility.floatingmenu.action.UNDO"; + public static final String ACTION_DELETE = + "com.android.systemui.accessibility.floatingmenu.action.DELETE"; + + private final Context mContext; + + MenuNotificationFactory(Context context) { + mContext = context; + } + + public Notification createHiddenNotification() { + final CharSequence title = mContext.getText( + R.string.accessibility_floating_button_hidden_notification_title); + final CharSequence content = mContext.getText( + R.string.accessibility_floating_button_hidden_notification_text); + + return new Notification.Builder(mContext, NotificationChannels.ALERTS) + .setContentTitle(title) + .setContentText(content) + .setSmallIcon(R.drawable.ic_settings_24dp) + .setContentIntent(buildUndoIntent()) + .setDeleteIntent(buildDeleteIntent()) + .setColor(mContext.getResources().getColor( + com.android.internal.R.color.system_notification_accent_color)) + .setLocalOnly(true) + .setCategory(Notification.CATEGORY_SYSTEM) + .build(); + } + + private PendingIntent buildUndoIntent() { + final Intent intent = new Intent(ACTION_UNDO); + + return PendingIntent.getBroadcast(mContext, /* requestCode= */ 0, intent, + PendingIntent.FLAG_IMMUTABLE); + + } + + private PendingIntent buildDeleteIntent() { + final Intent intent = new Intent(ACTION_DELETE); + + return PendingIntent.getBroadcastAsUser(mContext, /* requestCode= */ 0, intent, + PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT + | PendingIntent.FLAG_IMMUTABLE, UserHandle.CURRENT); + + } +} diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java index 62d5feb7d024..6869bbaedcf3 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java @@ -26,16 +26,21 @@ import static com.android.internal.accessibility.common.ShortcutConstants.Access import static com.android.internal.accessibility.util.AccessibilityUtils.getAccessibilityServiceFragmentType; import static com.android.internal.accessibility.util.AccessibilityUtils.setAccessibilityServiceState; import static com.android.systemui.accessibility.floatingmenu.MenuMessageView.Index; +import static com.android.systemui.accessibility.floatingmenu.MenuNotificationFactory.ACTION_DELETE; +import static com.android.systemui.accessibility.floatingmenu.MenuNotificationFactory.ACTION_UNDO; import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat; import android.accessibilityservice.AccessibilityServiceInfo; import android.annotation.IntDef; import android.annotation.StringDef; import android.annotation.SuppressLint; +import android.app.NotificationManager; +import android.content.BroadcastReceiver; import android.content.ComponentCallbacks; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Rect; @@ -58,6 +63,7 @@ import androidx.lifecycle.Observer; import com.android.internal.accessibility.dialog.AccessibilityTarget; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.messages.nano.SystemMessageProto; import com.android.internal.util.Preconditions; import com.android.systemui.Flags; import com.android.systemui.res.R; @@ -91,6 +97,8 @@ class MenuViewLayer extends FrameLayout implements private final MenuViewAppearance mMenuViewAppearance; private final MenuAnimationController mMenuAnimationController; private final AccessibilityManager mAccessibilityManager; + private final NotificationManager mNotificationManager; + private final MenuNotificationFactory mNotificationFactory; private final Handler mHandler = new Handler(Looper.getMainLooper()); private final IAccessibilityFloatingMenu mFloatingMenu; private final SecureSettings mSecureSettings; @@ -103,7 +111,9 @@ class MenuViewLayer extends FrameLayout implements private final Rect mImeInsetsRect = new Rect(); private boolean mIsMigrationTooltipShowing; private boolean mShouldShowDockTooltip; + private boolean mIsNotificationShown; private Optional<MenuEduTooltipView> mEduTooltipView = Optional.empty(); + private BroadcastReceiver mNotificationActionReceiver; @IntDef({ LayerIndex.MENU_VIEW, @@ -184,10 +194,16 @@ class MenuViewLayer extends FrameLayout implements mMenuViewAppearance = new MenuViewAppearance(context, windowManager); mMenuView = new MenuView(context, mMenuViewModel, mMenuViewAppearance); mMenuAnimationController = mMenuView.getMenuAnimationController(); - mMenuAnimationController.setDismissCallback(this::hideMenuAndShowMessage); + if (Flags.floatingMenuDragToHide()) { + mMenuAnimationController.setDismissCallback(this::hideMenuAndShowNotification); + } else { + mMenuAnimationController.setDismissCallback(this::hideMenuAndShowMessage); + } mMenuAnimationController.setSpringAnimationsEndAction(this::onSpringAnimationsEndAction); mDismissView = new DismissView(context); DismissViewUtils.setup(mDismissView); + mNotificationFactory = new MenuNotificationFactory(context); + mNotificationManager = context.getSystemService(NotificationManager.class); mDragToInteractAnimationController = new DragToInteractAnimationController( mDismissView, mMenuView); mDragToInteractAnimationController.setMagnetListener(new MagnetizedObject.MagnetListener() { @@ -204,7 +220,11 @@ class MenuViewLayer extends FrameLayout implements @Override public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) { - hideMenuAndShowMessage(); + if (Flags.floatingMenuDragToHide()) { + hideMenuAndShowNotification(); + } else { + hideMenuAndShowMessage(); + } mDismissView.hide(); mDragToInteractAnimationController.animateDismissMenu(/* scaleUp= */ false); } @@ -218,18 +238,25 @@ class MenuViewLayer extends FrameLayout implements mMessageView = new MenuMessageView(context); mMenuView.setOnTargetFeaturesChangeListener(newTargetFeatures -> { - if (newTargetFeatures.size() < 1) { - return; - } - - // During the undo action period, the pending action will be canceled and undo back - // to the previous state if users did any action related to the accessibility features. - if (mMessageView.getVisibility() == VISIBLE) { + if (Flags.floatingMenuDragToHide()) { + dismissNotification(); undo(); - } + } else { + if (newTargetFeatures.size() < 1) { + return; + } + + // During the undo action period, the pending action will be canceled and undo back + // to the previous state if users did any action related to the accessibility + // features. + if (mMessageView.getVisibility() == VISIBLE) { + undo(); + } - final TextView messageText = (TextView) mMessageView.getChildAt(Index.TEXT_VIEW); - messageText.setText(getMessageText(newTargetFeatures)); + + final TextView messageText = (TextView) mMessageView.getChildAt(Index.TEXT_VIEW); + messageText.setText(getMessageText(newTargetFeatures)); + } }); addView(mMenuView, LayerIndex.MENU_VIEW); @@ -456,6 +483,50 @@ class MenuViewLayer extends FrameLayout implements mMenuAnimationController.startShrinkAnimation(() -> mMenuView.setVisibility(GONE)); } + private void hideMenuAndShowNotification() { + mMenuAnimationController.startShrinkAnimation(() -> mMenuView.setVisibility(GONE)); + showNotification(); + } + + private void showNotification() { + registerReceiverIfNeeded(); + if (!mIsNotificationShown) { + mNotificationManager.notify( + SystemMessageProto.SystemMessage.NOTE_A11Y_FLOATING_MENU_HIDDEN, + mNotificationFactory.createHiddenNotification()); + mIsNotificationShown = true; + } + } + + private void dismissNotification() { + unregisterReceiverIfNeeded(); + if (mIsNotificationShown) { + mNotificationManager.cancel( + SystemMessageProto.SystemMessage.NOTE_A11Y_FLOATING_MENU_HIDDEN); + mIsNotificationShown = false; + } + } + + private void registerReceiverIfNeeded() { + if (mNotificationActionReceiver != null) { + return; + } + mNotificationActionReceiver = new MenuNotificationActionReceiver(); + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(ACTION_UNDO); + intentFilter.addAction(ACTION_DELETE); + getContext().registerReceiver(mNotificationActionReceiver, intentFilter, + Context.RECEIVER_EXPORTED); + } + + private void unregisterReceiverIfNeeded() { + if (mNotificationActionReceiver == null) { + return; + } + getContext().unregisterReceiver(mNotificationActionReceiver); + mNotificationActionReceiver = null; + } + private void undo() { mHandler.removeCallbacksAndMessages(/* token= */ null); mMessageView.setVisibility(GONE); @@ -464,4 +535,23 @@ class MenuViewLayer extends FrameLayout implements mMenuView.setVisibility(VISIBLE); mMenuAnimationController.startGrowAnimation(); } + + @VisibleForTesting + DragToInteractAnimationController getDragToInteractAnimationController() { + return mDragToInteractAnimationController; + } + + private class MenuNotificationActionReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (ACTION_UNDO.equals(action)) { + dismissNotification(); + undo(); + } else if (ACTION_DELETE.equals(action)) { + dismissNotification(); + mDismissMenuAction.run(); + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt new file mode 100644 index 000000000000..889023e8dab6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.communal.log + +import com.android.internal.logging.UiEventLogger +import com.android.systemui.CoreStartable +import com.android.systemui.communal.domain.interactor.CommunalInteractor +import com.android.systemui.communal.shared.log.CommunalUiEvent +import com.android.systemui.communal.shared.model.CommunalSceneKey +import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.util.kotlin.pairwise +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.drop +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach + +/** A [CoreStartable] responsible for logging metrics for the communal hub. */ +@SysUISingleton +class CommunalLoggerStartable +@Inject +constructor( + @Background private val backgroundScope: CoroutineScope, + private val communalInteractor: CommunalInteractor, + private val uiEventLogger: UiEventLogger, +) : CoreStartable { + + override fun start() { + communalInteractor.transitionState + .map { state -> + when { + state.isOnCommunal() -> CommunalUiEvent.COMMUNAL_HUB_SHOWN + state.isNotOnCommunal() -> CommunalUiEvent.COMMUNAL_HUB_GONE + else -> null + } + } + .filterNotNull() + .distinctUntilChanged() + // Drop the default value. + .drop(1) + .onEach { uiEvent -> uiEventLogger.log(uiEvent) } + .launchIn(backgroundScope) + + communalInteractor.transitionState + .pairwise() + .map { (old, new) -> + when { + new.isOnCommunal() && old.isSwipingToCommunal() -> + CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_FINISH + new.isOnCommunal() && old.isSwipingFromCommunal() -> + CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_CANCEL + new.isNotOnCommunal() && old.isSwipingFromCommunal() -> + CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_FINISH + new.isNotOnCommunal() && old.isSwipingToCommunal() -> + CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_CANCEL + new.isSwipingToCommunal() && old.isNotOnCommunal() -> + CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_START + new.isSwipingFromCommunal() && old.isOnCommunal() -> + CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_START + else -> null + } + } + .filterNotNull() + .distinctUntilChanged() + .onEach { uiEvent -> uiEventLogger.log(uiEvent) } + .launchIn(backgroundScope) + } +} + +/** Whether currently in communal scene. */ +private fun ObservableCommunalTransitionState.isOnCommunal(): Boolean { + return this is ObservableCommunalTransitionState.Idle && scene == CommunalSceneKey.Communal +} + +/** Whether currently in a scene other than communal. */ +private fun ObservableCommunalTransitionState.isNotOnCommunal(): Boolean { + return this is ObservableCommunalTransitionState.Idle && scene != CommunalSceneKey.Communal +} + +/** Whether currently transitioning from another scene to communal. */ +private fun ObservableCommunalTransitionState.isSwipingToCommunal(): Boolean { + return this is ObservableCommunalTransitionState.Transition && + toScene == CommunalSceneKey.Communal && + isInitiatedByUserInput +} + +/** Whether currently transitioning from communal to another scene. */ +private fun ObservableCommunalTransitionState.isSwipingFromCommunal(): Boolean { + return this is ObservableCommunalTransitionState.Transition && + fromScene == CommunalSceneKey.Communal && + isInitiatedByUserInput +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalUiEvent.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalUiEvent.kt index e167f3e263fe..b64c1955b43d 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalUiEvent.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalUiEvent.kt @@ -22,8 +22,6 @@ import com.android.internal.logging.UiEventLogger.UiEventEnum /** UI events for the Communal Hub. */ enum class CommunalUiEvent(private val id: Int) : UiEventEnum { @UiEvent(doc = "Communal Hub is fully shown") COMMUNAL_HUB_SHOWN(1566), - @UiEvent(doc = "Communal Hub starts entering") COMMUNAL_HUB_ENTERING(1575), - @UiEvent(doc = "Communal Hub starts exiting") COMMUNAL_HUB_EXITING(1576), @UiEvent(doc = "Communal Hub is fully gone") COMMUNAL_HUB_GONE(1577), @UiEvent(doc = "Communal Hub times out") COMMUNAL_HUB_TIMEOUT(1578), @UiEvent(doc = "The visible content in the Communal Hub is fully loaded and rendered") diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt index 97e530ace9a7..28f48ce1e647 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt @@ -30,6 +30,7 @@ import com.android.systemui.shade.ShadeViewController import javax.inject.Provider import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.flowOf /** The base view model for the communal hub. */ abstract class BaseCommunalViewModel( @@ -96,6 +97,12 @@ abstract class BaseCommunalViewModel( /** Whether in edit mode for the communal hub. */ open val isEditMode = false + /** Whether the popup message triggered by dismissing the CTA tile is showing. */ + open val isPopupOnDismissCtaShowing: Flow<Boolean> = flowOf(false) + + /** Hide the popup message triggered by dismissing the CTA tile. */ + open fun onHidePopupAfterDismissCta() {} + /** Called as the UI requests deleting a widget. */ open fun onDeleteWidget(id: Int) {} @@ -116,4 +123,13 @@ abstract class BaseCommunalViewModel( /** Gets the interaction handler used to handle taps on a remote view */ abstract fun getInteractionHandler(): RemoteViews.InteractionHandler + + /** Called as the user starts dragging a widget to reorder. */ + open fun onReorderWidgetStart() {} + + /** Called as the user finishes dragging a widget to reorder. */ + open fun onReorderWidgetEnd() {} + + /** Called as the user cancels dragging a widget to reorder. */ + open fun onReorderWidgetCancel() {} } diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt index a03e6c1aee97..0cbf3f1312e2 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt @@ -25,8 +25,10 @@ import android.content.ActivityNotFoundException import android.content.ComponentName import android.os.PowerManager import android.widget.RemoteViews +import com.android.internal.logging.UiEventLogger import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.model.CommunalContentModel +import com.android.systemui.communal.shared.log.CommunalUiEvent import com.android.systemui.dagger.SysUISingleton import com.android.systemui.media.controls.ui.MediaHost import com.android.systemui.media.dagger.MediaModule @@ -50,6 +52,7 @@ constructor( shadeViewController: Provider<ShadeViewController>, powerManager: PowerManager, @Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost, + private val uiEventLogger: UiEventLogger, ) : BaseCommunalViewModel(communalInteractor, shadeViewController, powerManager, mediaHost) { private companion object { @@ -135,4 +138,16 @@ constructor( pendingConfiguration?.complete(resultCode) ?: throw IllegalStateException("No widget pending configuration") } + + override fun onReorderWidgetStart() { + uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_START) + } + + override fun onReorderWidgetEnd() { + uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_FINISH) + } + + override fun onReorderWidgetCancel() { + uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_CANCEL) + } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt index 066e897cdfdb..1c696851bb70 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt @@ -23,23 +23,31 @@ import com.android.systemui.communal.domain.interactor.CommunalTutorialInteracto import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.widgets.WidgetInteractionHandler import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.media.controls.ui.MediaHost import com.android.systemui.media.dagger.MediaModule import com.android.systemui.shade.ShadeViewController import javax.inject.Inject import javax.inject.Named import javax.inject.Provider +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.launch /** The default view model used for showing the communal hub. */ @SysUISingleton class CommunalViewModel @Inject constructor( + @Application private val scope: CoroutineScope, private val communalInteractor: CommunalInteractor, private val interactionHandler: WidgetInteractionHandler, tutorialInteractor: CommunalTutorialInteractor, @@ -63,9 +71,45 @@ constructor( } } + private val _isPopupOnDismissCtaShowing: MutableStateFlow<Boolean> = MutableStateFlow(false) + override val isPopupOnDismissCtaShowing: Flow<Boolean> = + _isPopupOnDismissCtaShowing.asStateFlow() + override fun onOpenWidgetEditor() = communalInteractor.showWidgetEditor() - override fun onDismissCtaTile() = communalInteractor.dismissCtaTile() + override fun onDismissCtaTile() { + communalInteractor.dismissCtaTile() + setPopupOnDismissCtaVisibility(true) + schedulePopupHiding() + } override fun getInteractionHandler(): RemoteViews.InteractionHandler = interactionHandler + + override fun onHidePopupAfterDismissCta() { + cancelDelayedPopupHiding() + setPopupOnDismissCtaVisibility(false) + } + + private fun setPopupOnDismissCtaVisibility(isVisible: Boolean) { + _isPopupOnDismissCtaShowing.value = isVisible + } + + private var delayedHidePopupJob: Job? = null + private fun schedulePopupHiding() { + cancelDelayedPopupHiding() + delayedHidePopupJob = + scope.launch { + delay(POPUP_AUTO_HIDE_TIMEOUT_MS) + onHidePopupAfterDismissCta() + } + } + + private fun cancelDelayedPopupHiding() { + delayedHidePopupJob?.cancel() + delayedHidePopupJob = null + } + + companion object { + const val POPUP_AUTO_HIDE_TIMEOUT_MS = 12000L + } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt index bfc6f2b14acd..380ed61a556d 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt @@ -30,6 +30,8 @@ import androidx.activity.result.contract.ActivityResultContracts.StartActivityFo import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle +import com.android.internal.logging.UiEventLogger +import com.android.systemui.communal.shared.log.CommunalUiEvent import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel import com.android.systemui.compose.ComposeFacade.setCommunalEditWidgetActivityContent import javax.inject.Inject @@ -41,6 +43,7 @@ class EditWidgetsActivity constructor( private val communalViewModel: CommunalEditModeViewModel, private var windowManagerService: IWindowManager? = null, + private val uiEventLogger: UiEventLogger, ) : ComponentActivity() { companion object { private const val EXTRA_IS_PENDING_WIDGET_DRAG = "is_pending_widget_drag" @@ -54,6 +57,8 @@ constructor( registerForActivityResult(StartActivityForResult()) { result -> when (result.resultCode) { RESULT_OK -> { + uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_WIDGET_PICKER_SHOWN) + result.data?.let { intent -> val isPendingWidgetDrag = intent.getBooleanExtra(EXTRA_IS_PENDING_WIDGET_DRAG, false) @@ -144,4 +149,16 @@ constructor( communalViewModel.setConfigurationResult(resultCode) } } + + override fun onStart() { + super.onStart() + + uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_EDIT_MODE_SHOWN) + } + + override fun onStop() { + super.onStop() + + uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_EDIT_MODE_GONE) + } } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt index 87a736d926b5..8d82b552fc1e 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt @@ -24,6 +24,7 @@ import com.android.systemui.accessibility.Magnification import com.android.systemui.back.domain.interactor.BackActionInteractor import com.android.systemui.biometrics.BiometricNotificationService import com.android.systemui.clipboardoverlay.ClipboardListener +import com.android.systemui.communal.log.CommunalLoggerStartable import com.android.systemui.controls.dagger.StartControlsStartableModule import com.android.systemui.dagger.qualifiers.PerUser import com.android.systemui.dreams.AssistantAttentionMonitor @@ -318,4 +319,9 @@ abstract class SystemUICoreStartableModule { @IntoMap @ClassKey(KeyguardDismissBinder::class) abstract fun bindKeyguardDismissBinder(impl: KeyguardDismissBinder): CoreStartable + + @Binds + @IntoMap + @ClassKey(CommunalLoggerStartable::class) + abstract fun bindCommunalLoggerStartable(impl: CommunalLoggerStartable): CoreStartable } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java index 4e4b79c5cfa2..7f3b5eba96c6 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java @@ -155,12 +155,12 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler { return true; } - // Don't set expansion if the user doesn't have a pin/password set so that no - // animations are played we're not transitioning to the bouncer. - if (!mLockPatternUtils.isSecure(mUserTracker.getUserId())) { - // Return false so the gesture is not consumed, allowing the dream to wake - // if it wants instead of doing nothing. - return false; + // If scrolling up and keyguard is not locked, dismiss the dream since there's + // no bouncer to show. + if (e1.getY() > e2.getY() + && !mLockPatternUtils.isSecure(mUserTracker.getUserId())) { + mCentralSurfaces.get().awakenDreams(); + return true; } // For consistency, we adopt the expansion definition found in the diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index a34730e2d71f..5cebd96249b6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -1486,6 +1486,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, mOrderUnlockAndWake = context.getResources().getBoolean( com.android.internal.R.bool.config_orderUnlockAndWake); + + mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard"); + mShowKeyguardWakeLock.setReferenceCounted(false); } public void userActivity() { @@ -1493,9 +1496,6 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } private void setupLocked() { - mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard"); - mShowKeyguardWakeLock.setReferenceCounted(false); - IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SHUTDOWN); mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt index cb0f18630324..42f14f1eb317 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt @@ -140,8 +140,9 @@ constructor( override val revealAmount: Flow<Float> = callbackFlow { val updateListener = Animator.AnimatorUpdateListener { - val value = (it as ValueAnimator).animatedValue - trySend(value as Float) + val value = (it as ValueAnimator).animatedValue as Float + trySend(value) + if (value <= 0.0f || value >= 1.0f) { scrimLogger.d(TAG, "revealAmount", value) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt index f7d1543e4650..5b0c562bfdb8 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt @@ -154,6 +154,8 @@ constructor( val dozingToLockscreenTransition: Flow<TransitionStep> = repository.transition(DOZING, LOCKSCREEN) + val transitions = repository.transitions + /** Receive all [TransitionStep] matching a filter of [from]->[to] */ fun transition(from: KeyguardState, to: KeyguardState): Flow<TransitionStep> { return repository.transition(from, to) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt index fbf693625f63..19d00cfea114 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt @@ -22,12 +22,15 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.LightRevealScrimRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.power.domain.interactor.PowerInteractor +import com.android.systemui.power.shared.model.ScreenPowerState import com.android.systemui.statusbar.LightRevealEffect import com.android.systemui.util.kotlin.sample import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch @ExperimentalCoroutinesApi @@ -39,6 +42,7 @@ constructor( private val lightRevealScrimRepository: LightRevealScrimRepository, @Application private val scope: CoroutineScope, private val scrimLogger: ScrimLogger, + powerInteractor: PowerInteractor, ) { init { @@ -73,7 +77,16 @@ constructor( lightRevealScrimRepository.revealEffect ) - val revealAmount = lightRevealScrimRepository.revealAmount + val revealAmount = + lightRevealScrimRepository.revealAmount.filter { + // When the screen is off we do not want to keep producing frames as this is causing + // (invisible) jank. However, we need to still pass through 1f and 0f to ensure that the + // correct end states are respected even if the screen turned off (or was still off) + // when the animation finished + powerInteractor.screenPowerState.value != ScreenPowerState.SCREEN_OFF || + it == 1f || + it == 0f + } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt index cf1d2477c9af..4abda741d495 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt @@ -17,9 +17,13 @@ package com.android.systemui.keyguard.ui import android.view.animation.Interpolator import com.android.app.animation.Interpolators.LINEAR +import com.android.app.tracing.coroutines.launch import com.android.keyguard.logging.KeyguardTransitionAnimationLogger import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionState.CANCELED import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING @@ -31,10 +35,12 @@ import kotlin.math.min import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.mapNotNull /** * Assists in creating sub-flows for a KeyguardTransition. Call [setup] once for a transition, and @@ -45,21 +51,49 @@ class KeyguardTransitionAnimationFlow @Inject constructor( @Application private val scope: CoroutineScope, + private val transitionInteractor: KeyguardTransitionInteractor, private val logger: KeyguardTransitionAnimationLogger, ) { + private val transitionMap = mutableMapOf<Edge, MutableSharedFlow<TransitionStep>>() - /** - * Invoke once per transition between FROM->TO states to get access to - * [SharedFlowBuilder#sharedFlow]. - */ + init { + scope.launch("KeyguardTransitionAnimationFlow") { + transitionInteractor.transitions.collect { + // FROM->TO + transitionMap[Edge(it.from, it.to)]?.emit(it) + // FROM->(ANY) + transitionMap[Edge(it.from, null)]?.emit(it) + // (ANY)->TO + transitionMap[Edge(null, it.to)]?.emit(it) + } + } + } + + private fun getOrCreateFlow(edge: Edge): MutableSharedFlow<TransitionStep> { + return transitionMap.getOrPut(edge) { + MutableSharedFlow<TransitionStep>( + extraBufferCapacity = 10, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) + } + } + + /** Invoke once per transition between FROM->TO states to get access to a shared flow. */ fun setup( duration: Duration, - stepFlow: Flow<TransitionStep>, - ) = SharedFlowBuilder(duration, stepFlow) + from: KeyguardState?, + to: KeyguardState?, + ): FlowBuilder { + if (from == null && to == null) { + throw IllegalArgumentException("from and to are both null") + } + + return FlowBuilder(duration, Edge(from, to)) + } - inner class SharedFlowBuilder( + inner class FlowBuilder( private val transitionDuration: Duration, - private val stepFlow: Flow<TransitionStep>, + private val edge: Edge, ) { /** * Transitions will occur over a [transitionDuration] with [TransitionStep]s being emitted @@ -115,20 +149,21 @@ constructor( }?.let { onStep(interpolator.getInterpolation(it)) } } - return stepFlow + return getOrCreateFlow(edge) .map { step -> - val value = - when (step.transitionState) { - STARTED -> stepToValue(step) - RUNNING -> stepToValue(step) - CANCELED -> onCancel?.invoke() - FINISHED -> onFinish?.invoke() - } - logger.logTransitionStep(name, step, value) - value + StateToValue( + step.transitionState, + when (step.transitionState) { + STARTED -> stepToValue(step) + RUNNING -> stepToValue(step) + CANCELED -> onCancel?.invoke() + FINISHED -> onFinish?.invoke() + } + ) + .also { logger.logTransitionStep(name, step, it.value) } } - .filterNotNull() .distinctUntilChanged() + .mapNotNull { stateToValue -> stateToValue.value } } /** @@ -138,4 +173,14 @@ constructor( return sharedFlow(duration = 1.milliseconds, onStep = { value }, onFinish = { value }) } } + + data class Edge( + val from: KeyguardState?, + val to: KeyguardState?, + ) + + data class StateToValue( + val transitionState: TransitionState, + val value: Float?, + ) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt index 05fe0b25381d..bf763b4e1f99 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt @@ -16,13 +16,20 @@ package com.android.systemui.keyguard.ui.binder +import android.animation.Animator +import android.animation.ValueAnimator +import android.transition.Transition import android.transition.TransitionManager +import android.transition.TransitionSet +import android.transition.TransitionValues +import android.view.ViewGroup import androidx.annotation.VisibleForTesting import androidx.constraintlayout.helper.widget.Layer import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle +import com.android.app.animation.Interpolators import com.android.systemui.Flags.migrateClocksToBlueprint import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor import com.android.systemui.keyguard.ui.view.layout.sections.ClockSection @@ -32,6 +39,8 @@ import com.android.systemui.plugins.clocks.ClockController import com.android.systemui.res.R import kotlinx.coroutines.launch +private const val KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION_MS = 1000L + object KeyguardClockViewBinder { @JvmStatic fun bind( @@ -69,7 +78,13 @@ object KeyguardClockViewBinder { launch { if (!migrateClocksToBlueprint()) return@launch viewModel.clockShouldBeCentered.collect { - applyConstraints(clockSection, keyguardRootView, true) + viewModel.clock?.let { + if (it.largeClock.config.hasCustomPositionUpdatedAnimation) { + playClockCenteringAnimation(clockSection, keyguardRootView, it) + } else { + applyConstraints(clockSection, keyguardRootView, true) + } + } } } } @@ -125,12 +140,84 @@ object KeyguardClockViewBinder { clockSection: ClockSection, rootView: ConstraintLayout, animated: Boolean, + set: TransitionSet? = null, ) { val constraintSet = ConstraintSet().apply { clone(rootView) } clockSection.applyConstraints(constraintSet) if (animated) { - TransitionManager.beginDelayedTransition(rootView) + set?.let { TransitionManager.beginDelayedTransition(rootView, it) } + ?: run { TransitionManager.beginDelayedTransition(rootView) } } constraintSet.applyTo(rootView) } + + private fun playClockCenteringAnimation( + clockSection: ClockSection, + keyguardRootView: ConstraintLayout, + clock: ClockController, + ) { + // Find the clock, so we can exclude it from this transition. + val clockView = clock.largeClock.view + val set = TransitionSet() + val adapter = SplitShadeTransitionAdapter(clock) + adapter.setInterpolator(Interpolators.LINEAR) + adapter.setDuration(KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION_MS) + adapter.addTarget(clockView) + set.addTransition(adapter) + applyConstraints(clockSection, keyguardRootView, true, set) + } + + internal class SplitShadeTransitionAdapter + @VisibleForTesting + constructor(private val clock: ClockController) : Transition() { + private fun captureValues(transitionValues: TransitionValues) { + transitionValues.values[PROP_BOUNDS_LEFT] = transitionValues.view.left + val locationInWindowTmp = IntArray(2) + transitionValues.view.getLocationInWindow(locationInWindowTmp) + transitionValues.values[PROP_X_IN_WINDOW] = locationInWindowTmp[0] + } + + override fun captureEndValues(transitionValues: TransitionValues) { + captureValues(transitionValues) + } + + override fun captureStartValues(transitionValues: TransitionValues) { + captureValues(transitionValues) + } + + override fun createAnimator( + sceneRoot: ViewGroup, + startValues: TransitionValues?, + endValues: TransitionValues? + ): Animator? { + if (startValues == null || endValues == null) { + return null + } + val anim = ValueAnimator.ofFloat(0f, 1f) + val fromLeft = startValues.values[PROP_BOUNDS_LEFT] as Int + val fromWindowX = startValues.values[PROP_X_IN_WINDOW] as Int + val toWindowX = endValues.values[PROP_X_IN_WINDOW] as Int + // Using windowX, to determine direction, instead of left, as in RTL the difference of + // toLeft - fromLeft is always positive, even when moving left. + val direction = if (toWindowX - fromWindowX > 0) 1 else -1 + anim.addUpdateListener { animation: ValueAnimator -> + clock.largeClock.animations.onPositionUpdated( + fromLeft, + direction, + animation.animatedFraction + ) + } + return anim + } + + override fun getTransitionProperties(): Array<String> { + return TRANSITION_PROPERTIES + } + + companion object { + private const val PROP_BOUNDS_LEFT = "splitShadeTransitionAdapter:boundsLeft" + private const val PROP_X_IN_WINDOW = "splitShadeTransitionAdapter:xInWindow" + private val TRANSITION_PROPERTIES = arrayOf(PROP_BOUNDS_LEFT, PROP_X_IN_WINDOW) + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt index 92270ad31664..81ce8f04d302 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt @@ -23,6 +23,7 @@ import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle +import com.android.systemui.Flags.migrateClocksToBlueprint import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel @@ -42,6 +43,7 @@ object KeyguardSmartspaceViewBinder { keyguardRootView.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { launch { + if (!migrateClocksToBlueprint()) return@launch clockViewModel.hasCustomWeatherDataDisplay.collect { hasCustomWeatherDataDisplay -> if (hasCustomWeatherDataDisplay) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt index f2b28d964314..f33aed03856c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt @@ -29,4 +29,8 @@ class KeyguardRootView( ConstraintLayout( context, attrs, - ) + ) { + init { + clipChildren = false + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt index 0f8e67340cc7..756a4cca69d0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt @@ -23,11 +23,13 @@ import androidx.constraintlayout.widget.ConstraintSet.END import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP +import com.android.systemui.Flags.centralizedStatusBarDimensRefactor import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel import com.android.systemui.res.R import com.android.systemui.scene.shared.flag.SceneContainerFlags +import com.android.systemui.shade.LargeScreenHeaderHelper import com.android.systemui.shade.NotificationPanelView import com.android.systemui.statusbar.notification.stack.AmbientState import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController @@ -35,6 +37,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCa import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationStackAppearanceViewModel import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel +import dagger.Lazy import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher @@ -53,6 +56,7 @@ constructor( notificationStackSizeCalculator: NotificationStackSizeCalculator, private val smartspaceViewModel: KeyguardSmartspaceViewModel, @Main mainDispatcher: CoroutineDispatcher, + private val largeScreenHeaderHelperLazy: Lazy<LargeScreenHeaderHelper>, ) : NotificationStackScrollLayoutSection( context, @@ -72,7 +76,13 @@ constructor( } constraintSet.apply { val splitShadeTopMargin = - context.resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height) + if (centralizedStatusBarDimensRefactor()) { + largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight() + } else { + context.resources.getDimensionPixelSize( + R.dimen.large_screen_shade_header_height + ) + } connect(R.id.nssl_placeholder, TOP, PARENT_ID, TOP, splitShadeTopMargin) connect(R.id.nssl_placeholder, START, PARENT_ID, START) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt index a8e3be79fc5a..b4b48a8fb932 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition @@ -38,14 +37,14 @@ import kotlinx.coroutines.flow.flatMapLatest class AlternateBouncerToAodTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { private val transitionAnimation = animationFlow.setup( duration = FromAlternateBouncerTransitionInteractor.TO_AOD_DURATION, - stepFlow = interactor.transition(KeyguardState.ALTERNATE_BOUNCER, KeyguardState.AOD), + from = KeyguardState.ALTERNATE_BOUNCER, + to = KeyguardState.AOD, ) val deviceEntryBackgroundViewAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt index 5d6b0cebf959..3737e6fdf13e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TO_GONE_DURATION -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER import com.android.systemui.keyguard.shared.model.ScrimAlpha @@ -37,14 +36,14 @@ import kotlinx.coroutines.flow.Flow class AlternateBouncerToGoneTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, bouncerToGoneFlows: BouncerToGoneFlows, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { private val transitionAnimation = animationFlow.setup( duration = TO_GONE_DURATION, - stepFlow = interactor.transition(ALTERNATE_BOUNCER, KeyguardState.GONE), + from = ALTERNATE_BOUNCER, + to = KeyguardState.GONE, ) /** Scrim alpha values */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt index 8e729f76e096..2526f0aec796 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import android.graphics.Color import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TRANSITION_DURATION_MS -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager @@ -37,7 +36,6 @@ class AlternateBouncerViewModel @Inject constructor( private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager, - transitionInteractor: KeyguardTransitionInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) { // When we're fully transitioned to the AlternateBouncer, the alpha of the scrim should be: @@ -46,7 +44,8 @@ constructor( animationFlow .setup( duration = TRANSITION_DURATION_MS, - stepFlow = transitionInteractor.anyStateToAlternateBouncerTransition, + from = null, + to = ALTERNATE_BOUNCER, ) .sharedFlow( duration = TRANSITION_DURATION_MS, @@ -60,7 +59,8 @@ constructor( animationFlow .setup( TRANSITION_DURATION_MS, - transitionInteractor.transitionStepsFromState(ALTERNATE_BOUNCER), + from = ALTERNATE_BOUNCER, + to = null, ) .sharedFlow( duration = TRANSITION_DURATION_MS, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt index 2b145216cb80..b92a9a08987a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition @@ -31,14 +30,14 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi class AodToGoneTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { private val transitionAnimation = animationFlow.setup( duration = FromAodTransitionInteractor.TO_GONE_DURATION, - stepFlow = interactor.transition(KeyguardState.AOD, KeyguardState.GONE), + from = KeyguardState.AOD, + to = KeyguardState.GONE, ) override val deviceEntryParentViewAlpha = transitionAnimation.immediatelyTransitionTo(0f) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt index 5e552e1fe00f..266fd02d5bbf 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt @@ -19,7 +19,7 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import javax.inject.Inject @@ -37,7 +37,6 @@ import kotlinx.coroutines.flow.flatMapLatest class AodToLockscreenTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { @@ -45,7 +44,8 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = TO_LOCKSCREEN_DURATION, - stepFlow = interactor.aodToLockscreenTransition, + from = KeyguardState.AOD, + to = KeyguardState.LOCKSCREEN, ) /** Ensure alpha is set to be visible */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt index d283af359b06..105a7ed52311 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition @@ -29,13 +28,13 @@ import javax.inject.Inject class AodToOccludedTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { private val transitionAnimation = animationFlow.setup( duration = FromAodTransitionInteractor.TO_OCCLUDED_DURATION, - stepFlow = interactor.transition(KeyguardState.AOD, KeyguardState.OCCLUDED), + from = KeyguardState.AOD, + to = KeyguardState.OCCLUDED, ) override val deviceEntryParentViewAlpha = transitionAnimation.immediatelyTransitionTo(0f) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt index 41dc15778b0c..924fc5d0333f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt @@ -21,7 +21,6 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.shared.model.ScrimAlpha @@ -41,7 +40,6 @@ import kotlinx.coroutines.flow.map class BouncerToGoneFlows @Inject constructor( - private val interactor: KeyguardTransitionInteractor, private val statusBarStateController: SysuiStatusBarStateController, private val primaryBouncerInteractor: PrimaryBouncerInteractor, private val keyguardDismissActionInteractor: Lazy<KeyguardDismissActionInteractor>, @@ -76,7 +74,8 @@ constructor( val transitionAnimation = animationFlow.setup( duration = duration, - stepFlow = interactor.transition(fromState, GONE) + from = fromState, + to = GONE, ) return shadeInteractor.shadeExpansion.flatMapLatest { shadeExpansion -> diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt index 0b34326bc83d..e4610c15a3d0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import javax.inject.Inject @@ -34,13 +34,13 @@ import kotlinx.coroutines.flow.Flow class DozingToLockscreenTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { private val transitionAnimation = animationFlow.setup( duration = FromDozingTransitionInteractor.TO_LOCKSCREEN_DURATION, - stepFlow = interactor.dozingToLockscreenTransition, + from = KeyguardState.DOZING, + to = KeyguardState.LOCKSCREEN, ) val shortcutsAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt index 8bcf3f8a76d9..67568e12a4a1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromDreamingLockscreenHostedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds @@ -28,14 +28,14 @@ import kotlinx.coroutines.flow.Flow class DreamingHostedToLockscreenTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) { private val transitionAnimation = animationFlow.setup( duration = TO_LOCKSCREEN_DURATION, - stepFlow = interactor.dreamingLockscreenHostedToLockscreenTransition, + from = KeyguardState.DREAMING_LOCKSCREEN_HOSTED, + to = KeyguardState.LOCKSCREEN, ) val shortcutsAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt index 5f620afe2dea..ead2d48240c0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt @@ -22,6 +22,7 @@ import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsIntera import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition @@ -52,7 +53,8 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = TO_LOCKSCREEN_DURATION, - stepFlow = keyguardTransitionInteractor.dreamingToLockscreenTransition, + from = KeyguardState.DREAMING, + to = KeyguardState.LOCKSCREEN, ) val transitionEnded = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt index 3f27eb0c73e3..ba04fd3741a4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt @@ -20,7 +20,7 @@ import com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_AOD_DURATION -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import javax.inject.Inject @@ -36,7 +36,6 @@ import kotlinx.coroutines.flow.flatMapLatest class GoneToAodTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { @@ -44,7 +43,8 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = TO_AOD_DURATION, - stepFlow = interactor.goneToAodTransition, + from = KeyguardState.GONE, + to = KeyguardState.AOD, ) /** y-translation from the top of the screen for AOD */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt index bba790abe807..b52746364a8b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds @@ -32,14 +32,14 @@ import kotlinx.coroutines.flow.Flow class GoneToDreamingLockscreenHostedTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) { private val transitionAnimation = animationFlow.setup( duration = TO_DREAMING_DURATION, - stepFlow = interactor.goneToDreamingLockscreenHostedTransition, + from = KeyguardState.GONE, + to = KeyguardState.DREAMING_LOCKSCREEN_HOSTED, ) /** Lockscreen views alpha - hide immediately */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt index 6762ba6298a5..102242a4a7b0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt @@ -19,7 +19,7 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds @@ -30,14 +30,14 @@ import kotlinx.coroutines.flow.Flow class GoneToDreamingTransitionViewModel @Inject constructor( - private val interactor: KeyguardTransitionInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) { private val transitionAnimation = animationFlow.setup( duration = TO_DREAMING_DURATION, - stepFlow = interactor.goneToDreamingTransition, + from = KeyguardState.GONE, + to = KeyguardState.DREAMING, ) /** Lockscreen views y-translation */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt index adae8abfb9c3..793abb444f31 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds @@ -28,14 +28,14 @@ import kotlinx.coroutines.flow.Flow class GoneToLockscreenTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) { private val transitionAnimation = animationFlow.setup( duration = TO_LOCKSCREEN_DURATION, - stepFlow = interactor.goneToLockscreenTransition + from = KeyguardState.GONE, + to = KeyguardState.LOCKSCREEN ) val shortcutsAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt index 65614f47b120..7bf51a7d3d54 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt @@ -19,7 +19,7 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import javax.inject.Inject @@ -36,7 +36,6 @@ import kotlinx.coroutines.flow.flatMapLatest class LockscreenToAodTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, shadeDependentFlows: ShadeDependentFlows, animationFlow: KeyguardTransitionAnimationFlow, @@ -45,7 +44,8 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = FromLockscreenTransitionInteractor.TO_AOD_DURATION, - stepFlow = interactor.lockscreenToAodTransition, + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.AOD, ) val deviceEntryBackgroundViewAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt index accb20c91f98..4c0cd2f58eb7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DOZING_DURATION -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds @@ -28,14 +28,14 @@ import kotlinx.coroutines.flow.Flow class LockscreenToDozingTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) { private val transitionAnimation = animationFlow.setup( duration = TO_DOZING_DURATION, - stepFlow = interactor.lockscreenToDozingTransition + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.DOZING, ) val shortcutsAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt index c649b12b71e4..19b9cf4733f9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_HOSTED_DURATION -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds @@ -28,14 +28,14 @@ import kotlinx.coroutines.flow.Flow class LockscreenToDreamingHostedTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) { private val transitionAnimation = animationFlow.setup( duration = TO_DREAMING_HOSTED_DURATION, - stepFlow = interactor.lockscreenToDreamingLockscreenHostedTransition + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.DREAMING_LOCKSCREEN_HOSTED, ) val shortcutsAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt index 7f75b547d717..13522a6742ac 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt @@ -19,7 +19,7 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_DURATION -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import javax.inject.Inject @@ -34,14 +34,14 @@ import kotlinx.coroutines.flow.Flow class LockscreenToDreamingTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, shadeDependentFlows: ShadeDependentFlows, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { private val transitionAnimation = animationFlow.setup( duration = TO_DREAMING_DURATION, - stepFlow = interactor.lockscreenToDreamingTransition, + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.DREAMING, ) /** Lockscreen views y-translation */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt index 9e197138d0b2..a26ef0755123 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition @@ -35,14 +34,14 @@ import kotlinx.coroutines.flow.Flow class LockscreenToGoneTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { private val transitionAnimation = animationFlow.setup( duration = FromLockscreenTransitionInteractor.TO_GONE_DURATION, - stepFlow = interactor.transition(KeyguardState.LOCKSCREEN, KeyguardState.GONE), + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.GONE, ) val shortcutsAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt index 9db0b775cd40..dd6652e69792 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt @@ -20,7 +20,7 @@ import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_OCCLUDED_DURATION -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import com.android.systemui.res.R @@ -37,7 +37,6 @@ import kotlinx.coroutines.flow.flatMapLatest class LockscreenToOccludedTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, shadeDependentFlows: ShadeDependentFlows, configurationInteractor: ConfigurationInteractor, animationFlow: KeyguardTransitionAnimationFlow, @@ -46,7 +45,8 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = TO_OCCLUDED_DURATION, - stepFlow = interactor.lockscreenToOccludedTransition, + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.OCCLUDED, ) /** Lockscreen views alpha */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt index 52e3257f8e18..ce47f3c67b21 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition @@ -26,7 +25,6 @@ import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map /** * Breaks down LOCKSCREEN->PRIMARY BOUNCER transition into discrete steps for corresponding views to @@ -37,21 +35,21 @@ import kotlinx.coroutines.flow.map class LockscreenToPrimaryBouncerTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, shadeDependentFlows: ShadeDependentFlows, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { private val transitionAnimation = animationFlow.setup( duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION, - stepFlow = - interactor.transition(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER), + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.PRIMARY_BOUNCER, ) val shortcutsAlpha: Flow<Float> = - interactor.transition(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER).map { - 1 - it.value - } + transitionAnimation.sharedFlow( + duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION, + onStep = { 1f - it } + ) override val deviceEntryParentViewAlpha: Flow<Float> = shadeDependentFlows.transitionFlow( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt index ed5e83c44640..07c114163326 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition @@ -35,14 +34,14 @@ import kotlinx.coroutines.flow.flatMapLatest class OccludedToAodTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { private val transitionAnimation = animationFlow.setup( duration = FromOccludedTransitionInteractor.TO_AOD_DURATION, - stepFlow = interactor.transition(KeyguardState.OCCLUDED, KeyguardState.AOD), + from = KeyguardState.OCCLUDED, + to = KeyguardState.AOD, ) val deviceEntryBackgroundViewAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt index 4c24f83200b2..90195bd343b7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt @@ -21,7 +21,7 @@ import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import com.android.systemui.res.R @@ -41,7 +41,6 @@ import kotlinx.coroutines.flow.flatMapLatest class OccludedToLockscreenTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, configurationInteractor: ConfigurationInteractor, animationFlow: KeyguardTransitionAnimationFlow, @@ -50,7 +49,8 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = TO_LOCKSCREEN_DURATION, - stepFlow = interactor.occludedToLockscreenTransition, + from = KeyguardState.OCCLUDED, + to = KeyguardState.LOCKSCREEN, ) /** Lockscreen views y-translation */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt index 93482ea162bb..74094bea140a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt @@ -17,7 +17,7 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds @@ -27,14 +27,14 @@ import kotlinx.coroutines.flow.Flow class OffToLockscreenTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) { private val transitionAnimation = animationFlow.setup( duration = 250.milliseconds, - stepFlow = interactor.offToLockscreenTransition + from = KeyguardState.OFF, + to = KeyguardState.LOCKSCREEN, ) val shortcutsAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt index b0e2aa2d4765..cd8e2f12cb19 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition @@ -39,14 +38,14 @@ import kotlinx.coroutines.flow.flatMapLatest class PrimaryBouncerToAodTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { private val transitionAnimation = animationFlow.setup( duration = FromPrimaryBouncerTransitionInteractor.TO_AOD_DURATION, - stepFlow = interactor.transition(KeyguardState.PRIMARY_BOUNCER, KeyguardState.AOD), + from = KeyguardState.PRIMARY_BOUNCER, + to = KeyguardState.AOD, ) val deviceEntryBackgroundViewAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt index 9dbe97fd1c20..4f28b4608e49 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt @@ -22,7 +22,6 @@ import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER import com.android.systemui.keyguard.shared.model.ScrimAlpha @@ -44,7 +43,6 @@ import kotlinx.coroutines.flow.flatMapLatest class PrimaryBouncerToGoneTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, private val statusBarStateController: SysuiStatusBarStateController, private val primaryBouncerInteractor: PrimaryBouncerInteractor, keyguardDismissActionInteractor: Lazy<KeyguardDismissActionInteractor>, @@ -55,7 +53,8 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = TO_GONE_DURATION, - stepFlow = interactor.transition(PRIMARY_BOUNCER, GONE) + from = PRIMARY_BOUNCER, + to = GONE, ) private var leaveShadeOpen: Boolean = false diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt index b2eed60e0a9e..284a134f73c7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition @@ -28,7 +27,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.map /** * Breaks down PRIMARY BOUNCER->LOCKSCREEN transition into discrete steps for corresponding views to @@ -39,15 +37,14 @@ import kotlinx.coroutines.flow.map class PrimaryBouncerToLockscreenTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { private val transitionAnimation = animationFlow.setup( duration = FromPrimaryBouncerTransitionInteractor.TO_LOCKSCREEN_DURATION, - stepFlow = - interactor.transition(KeyguardState.PRIMARY_BOUNCER, KeyguardState.LOCKSCREEN), + from = KeyguardState.PRIMARY_BOUNCER, + to = KeyguardState.LOCKSCREEN, ) val deviceEntryBackgroundViewAlpha: Flow<Float> = @@ -60,9 +57,10 @@ constructor( } val shortcutsAlpha: Flow<Float> = - interactor.transition(KeyguardState.PRIMARY_BOUNCER, KeyguardState.LOCKSCREEN).map { - it.value - } + transitionAnimation.sharedFlow( + duration = FromPrimaryBouncerTransitionInteractor.TO_LOCKSCREEN_DURATION, + onStep = { it } + ) override val deviceEntryParentViewAlpha: Flow<Float> = transitionAnimation.immediatelyTransitionTo(1f) diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java index 0320dec0580f..092f1ed7d498 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java @@ -302,9 +302,6 @@ public class NavigationBarControllerImpl implements final NavigationBarView navBarView = getNavigationBarView(displayId); if (navBarView != null) { navBarView.showPinningEnterExitToast(entering); - } else if (displayId == mDisplayTracker.getDefaultDisplayId() - && mTaskbarDelegate.isInitialized()) { - mTaskbarDelegate.showPinningEnterExitToast(entering); } } @@ -314,9 +311,6 @@ public class NavigationBarControllerImpl implements final NavigationBarView navBarView = getNavigationBarView(displayId); if (navBarView != null) { navBarView.showPinningEscapeToast(); - } else if (displayId == mDisplayTracker.getDefaultDisplayId() - && mTaskbarDelegate.isInitialized()) { - mTaskbarDelegate.showPinningEscapeToast(); } } }; diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java index 62c7343d3fe5..01672875fd44 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java @@ -504,6 +504,11 @@ public class TaskbarDelegate implements CommandQueue.Callbacks, } @Override + public void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) { + mOverviewProxyService.onNavigationBarLumaSamplingEnabled(displayId, enable); + } + + @Override public void showPinningEnterExitToast(boolean entering) { updateSysuiFlags(); if (mScreenPinningNotify == null) { 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 e660b97b5c6b..0d641ac9c688 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -758,7 +758,8 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack } private void updateMLModelState() { - boolean newState = mIsGestureHandlingEnabled && DeviceConfig.getBoolean( + boolean newState = mIsGestureHandlingEnabled && mContext.getResources().getBoolean( + R.bool.config_useBackGestureML) && DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_SYSTEMUI, SystemUiDeviceConfigFlags.USE_BACK_GESTURE_ML_MODEL, false); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java index 064423768cd3..a3b92541d593 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java @@ -18,6 +18,8 @@ package com.android.systemui.qs; import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS; +import static com.android.systemui.Flags.centralizedStatusBarDimensRefactor; + import android.content.Context; import android.graphics.Canvas; import android.graphics.Path; @@ -30,6 +32,7 @@ import android.widget.FrameLayout; import com.android.systemui.Dumpable; import com.android.systemui.qs.customize.QSCustomizer; import com.android.systemui.res.R; +import com.android.systemui.shade.LargeScreenHeaderHelper; import com.android.systemui.shade.TouchLogger; import com.android.systemui.util.LargeScreenUtils; @@ -162,8 +165,12 @@ public class QSContainerImpl extends FrameLayout implements Dumpable { QuickStatusBarHeaderController quickStatusBarHeaderController) { int topPadding = QSUtils.getQsHeaderSystemIconsAreaHeight(mContext); if (!LargeScreenUtils.shouldUseLargeScreenShadeHeader(mContext.getResources())) { - topPadding = mContext.getResources() - .getDimensionPixelSize(R.dimen.large_screen_shade_header_height); + topPadding = + centralizedStatusBarDimensRefactor() + ? LargeScreenHeaderHelper.getLargeScreenHeaderHeight(mContext) + : mContext.getResources() + .getDimensionPixelSize( + R.dimen.large_screen_shade_header_height); } mQSPanelContainer.setPaddingRelative( mQSPanelContainer.getPaddingStart(), diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 45917e85d80e..fd53423e7f22 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -1037,6 +1037,19 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } } + public void onNavigationBarLumaSamplingEnabled(int displayId, boolean enable) { + try { + if (mOverviewProxy != null) { + mOverviewProxy.onNavigationBarLumaSamplingEnabled(displayId, enable); + } else { + Log.e(TAG_OPS, "Failed to get overview proxy to enable/disable nav bar luma" + + "sampling"); + } + } catch (RemoteException e) { + Log.e(TAG_OPS, "Failed to call onNavigationBarLumaSamplingEnabled()", e); + } + } + private void updateEnabledState() { final int currentUser = mUserTracker.getUserId(); mIsEnabled = mContext.getPackageManager().resolveServiceAsUser(mQuickStepIntent, diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java index bd43307dba5a..7aa0dadba16c 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java @@ -32,6 +32,7 @@ import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; @@ -143,6 +144,7 @@ public class RecordingService extends Service implements ScreenMediaRecorderList channel.enableVibration(true); mNotificationManager.createNotificationChannel(channel); + int currentUid = Process.myUid(); int currentUserId = mUserContextTracker.getUserContext().getUserId(); UserHandle currentUser = new UserHandle(currentUserId); switch (action) { @@ -166,7 +168,7 @@ public class RecordingService extends Service implements ScreenMediaRecorderList mRecorder = new ScreenMediaRecorder( mUserContextTracker.getUserContext(), mMainHandler, - currentUserId, + currentUid, mAudioSource, captureTarget, this diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java index 3aab3bf62809..a170d0dac100 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java @@ -83,7 +83,7 @@ public class ScreenMediaRecorder extends MediaProjection.Callback { private Surface mInputSurface; private VirtualDisplay mVirtualDisplay; private MediaRecorder mMediaRecorder; - private int mUser; + private int mUid; private ScreenRecordingMuxer mMuxer; private ScreenInternalAudioRecorder mAudio; private ScreenRecordingAudioSource mAudioSource; @@ -94,12 +94,12 @@ public class ScreenMediaRecorder extends MediaProjection.Callback { ScreenMediaRecorderListener mListener; public ScreenMediaRecorder(Context context, Handler handler, - int user, ScreenRecordingAudioSource audioSource, + int uid, ScreenRecordingAudioSource audioSource, MediaProjectionCaptureTarget captureRegion, ScreenMediaRecorderListener listener) { mContext = context; mHandler = handler; - mUser = user; + mUid = uid; mCaptureRegion = captureRegion; mListener = listener; mAudioSource = audioSource; @@ -111,7 +111,7 @@ public class ScreenMediaRecorder extends MediaProjection.Callback { IMediaProjectionManager mediaService = IMediaProjectionManager.Stub.asInterface(b); IMediaProjection proj = null; - proj = mediaService.createProjection(mUser, mContext.getPackageName(), + proj = mediaService.createProjection(mUid, mContext.getPackageName(), MediaProjectionManager.TYPE_SCREEN_CAPTURE, false); IMediaProjection projection = IMediaProjection.Stub.asInterface(proj.asBinder()); if (mCaptureRegion != null) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenHeaderHelper.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenHeaderHelper.kt new file mode 100644 index 000000000000..c74f038ebea4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenHeaderHelper.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.shade + +import android.content.Context +import com.android.internal.policy.SystemBarUtils +import com.android.systemui.res.R +import javax.inject.Inject +import kotlin.math.max + +class LargeScreenHeaderHelper @Inject constructor(private val context: Context) { + + fun getLargeScreenHeaderHeight(): Int = getLargeScreenHeaderHeight(context) + + companion object { + @JvmStatic + fun getLargeScreenHeaderHeight(context: Context): Int { + val defaultHeight = + context.resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height) + val statusBarHeight = SystemBarUtils.getStatusBarHeight(context) + // Height has to be at least as tall as the status bar, as the status bar height takes + // into account display cutouts. + return max(defaultHeight, statusBarHeight) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 6f4a1e7754f5..aeccf0031419 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -27,6 +27,7 @@ import static com.android.keyguard.KeyguardClockSwitch.SMALL; import static com.android.systemui.Flags.keyguardBottomAreaRefactor; import static com.android.systemui.Flags.migrateClocksToBlueprint; import static com.android.systemui.Flags.predictiveBackAnimateShade; +import static com.android.systemui.Flags.smartspaceRelocateToBottom; import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK; import static com.android.systemui.classifier.Classifier.GENERIC; import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; @@ -1214,7 +1215,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump .setMaxLengthSeconds(0.4f).build(); mStatusBarMinHeight = SystemBarUtils.getStatusBarHeight(mView.getContext()); mStatusBarHeaderHeightKeyguard = Utils.getStatusBarHeaderHeightKeyguard(mView.getContext()); - mClockPositionAlgorithm.loadDimens(mResources); + mClockPositionAlgorithm.loadDimens(mView.getContext(), mResources); mIndicationBottomPadding = mResources.getDimensionPixelSize( R.dimen.keyguard_indication_bottom_padding); int statusbarHeight = SystemBarUtils.getStatusBarHeight(mView.getContext()); @@ -1428,7 +1429,12 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump int index = mView.indexOfChild(mKeyguardBottomArea); mView.removeView(mKeyguardBottomArea); KeyguardBottomAreaView oldBottomArea = mKeyguardBottomArea; - setKeyguardBottomArea(mKeyguardBottomAreaViewControllerProvider.get().getView()); + KeyguardBottomAreaViewController keyguardBottomAreaViewController = + mKeyguardBottomAreaViewControllerProvider.get(); + if (smartspaceRelocateToBottom()) { + keyguardBottomAreaViewController.init(); + } + setKeyguardBottomArea(keyguardBottomAreaViewController.getView()); mKeyguardBottomArea.initFrom(oldBottomArea); mView.addView(mKeyguardBottomArea, index); @@ -1751,14 +1757,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } else { layout = mNotificationContainerParent; } - - if (migrateClocksToBlueprint()) { - mKeyguardInteractor.setClockShouldBeCentered(mSplitShadeEnabled && shouldBeCentered); - } else { - mKeyguardStatusViewController.updateAlignment( - layout, mSplitShadeEnabled, shouldBeCentered, animate); - mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(shouldBeCentered)); - } + mKeyguardStatusViewController.updateAlignment( + layout, mSplitShadeEnabled, shouldBeCentered, animate); + mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(shouldBeCentered)); } private boolean shouldKeyguardStatusViewBeCentered() { diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt index 9c8a286b2918..84cad1d16d73 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt @@ -27,6 +27,7 @@ import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP import androidx.lifecycle.lifecycleScope +import com.android.systemui.Flags.centralizedStatusBarDimensRefactor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.flags.FeatureFlags @@ -47,10 +48,11 @@ import com.android.systemui.statusbar.policy.SplitShadeStateController import com.android.systemui.util.LargeScreenUtils import com.android.systemui.util.ViewController import com.android.systemui.util.concurrency.DelayableExecutor -import kotlinx.coroutines.launch +import dagger.Lazy import java.util.function.Consumer import javax.inject.Inject import kotlin.reflect.KMutableProperty0 +import kotlinx.coroutines.launch @VisibleForTesting internal const val INSET_DEBOUNCE_MILLIS = 500L @@ -67,7 +69,8 @@ class NotificationsQSContainerController @Inject constructor( private val featureFlags: FeatureFlags, private val notificationStackScrollLayoutController: NotificationStackScrollLayoutController, - private val splitShadeStateController: SplitShadeStateController + private val splitShadeStateController: SplitShadeStateController, + private val largeScreenHeaderHelperLazy: Lazy<LargeScreenHeaderHelper>, ) : ViewController<NotificationsQuickSettingsContainer>(view), QSContainerController { private var splitShadeEnabled = false @@ -186,7 +189,11 @@ class NotificationsQSContainerController @Inject constructor( } private fun calculateLargeShadeHeaderHeight(): Int { - return resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height) + return if (centralizedStatusBarDimensRefactor()) { + largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight() + } else { + resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height) + } } private fun calculateShadeHeaderHeight(): Int { diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java index 1dff99d53078..f3e9c7503626 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java @@ -20,6 +20,7 @@ package com.android.systemui.shade; import static android.view.WindowInsets.Type.ime; import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE; +import static com.android.systemui.Flags.centralizedStatusBarDimensRefactor; import static com.android.systemui.classifier.Classifier.QS_COLLAPSE; import static com.android.systemui.shade.NotificationPanelViewController.COUNTER_PANEL_OPEN_QS; import static com.android.systemui.shade.NotificationPanelViewController.FLING_COLLAPSE; @@ -126,6 +127,7 @@ public class QuickSettingsController implements Dumpable { private final Lazy<NotificationPanelViewController> mPanelViewControllerLazy; private final NotificationPanelView mPanelView; + private final Lazy<LargeScreenHeaderHelper> mLargeScreenHeaderHelperLazy; private final KeyguardStatusBarView mKeyguardStatusBar; private final FrameLayout mQsFrame; @@ -344,10 +346,12 @@ public class QuickSettingsController implements Dumpable { ActiveNotificationsInteractor activeNotificationsInteractor, JavaAdapter javaAdapter, CastController castController, - SplitShadeStateController splitShadeStateController + SplitShadeStateController splitShadeStateController, + Lazy<LargeScreenHeaderHelper> largeScreenHeaderHelperLazy ) { mPanelViewControllerLazy = panelViewControllerLazy; mPanelView = panelView; + mLargeScreenHeaderHelperLazy = largeScreenHeaderHelperLazy; mQsFrame = mPanelView.findViewById(R.id.qs_frame); mKeyguardStatusBar = mPanelView.findViewById(R.id.keyguard_header); mResources = mPanelView.getResources(); @@ -449,7 +453,10 @@ public class QuickSettingsController implements Dumpable { mUseLargeScreenShadeHeader = LargeScreenUtils.shouldUseLargeScreenShadeHeader(mPanelView.getResources()); mLargeScreenShadeHeaderHeight = - mResources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height); + centralizedStatusBarDimensRefactor() + ? mLargeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight() + : mResources.getDimensionPixelSize( + R.dimen.large_screen_shade_header_height); int topMargin = mUseLargeScreenShadeHeader ? mLargeScreenShadeHeaderHeight : mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_top); mShadeHeaderController.setLargeScreenActive(mUseLargeScreenShadeHeader); diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt index 2460a3314be5..a66bacd237be 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt @@ -38,6 +38,7 @@ import androidx.core.view.doOnLayout import com.android.app.animation.Interpolators import com.android.settingslib.Utils import com.android.systemui.Dumpable +import com.android.systemui.Flags.centralizedStatusBarDimensRefactor import com.android.systemui.animation.ShadeInterpolation import com.android.systemui.battery.BatteryMeterView import com.android.systemui.battery.BatteryMeterViewController @@ -432,6 +433,9 @@ constructor( changes += combinedShadeHeadersConstraintManager.emptyCutoutConstraints() } + if (centralizedStatusBarDimensRefactor()) { + view.setPadding(view.paddingLeft, sbInsets.top, view.paddingRight, view.paddingBottom) + } view.updateAllConstraints(changes) updateBatteryMode() } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt index 39b7930ed386..6e8507422fbe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt @@ -253,6 +253,8 @@ constructor( initialHeight: Int? = null ) : View(context, attrs) { + private val logString = this::class.simpleName!! + "@" + hashCode() + /** Listener that is called if the scrim's opaqueness changes */ var isScrimOpaqueChangedListener: Consumer<Boolean>? = null @@ -267,13 +269,13 @@ constructor( if (field != value) { field = value if (value <= 0.0f || value >= 1.0f) { - scrimLogger?.d(TAG, "revealAmount", "$value on ${logString()}") + scrimLogger?.d(TAG, "revealAmount", "$value on $logString") } revealEffect.setRevealAmountOnScrim(value, this) updateScrimOpaque() Trace.traceCounter( Trace.TRACE_TAG_APP, - "light_reveal_amount", + "light_reveal_amount $logString", (field * 100).toInt() ) invalidate() @@ -290,7 +292,7 @@ constructor( field = value revealEffect.setRevealAmountOnScrim(revealAmount, this) - scrimLogger?.d(TAG, "revealEffect", "$value on ${logString()}") + scrimLogger?.d(TAG, "revealEffect", "$value on $logString") invalidate() } } @@ -350,7 +352,7 @@ constructor( if (field != value) { field = value isScrimOpaqueChangedListener?.accept(field) - scrimLogger?.d(TAG, "isScrimOpaque", "$value on ${logString()}") + scrimLogger?.d(TAG, "isScrimOpaque", "$value on $logString") } } @@ -368,13 +370,13 @@ constructor( override fun setAlpha(alpha: Float) { super.setAlpha(alpha) - scrimLogger?.d(TAG, "alpha", "$alpha on ${logString()}") + scrimLogger?.d(TAG, "alpha", "$alpha on $logString") updateScrimOpaque() } override fun setVisibility(visibility: Int) { super.setVisibility(visibility) - scrimLogger?.d(TAG, "visibility", "$visibility on ${logString()}") + scrimLogger?.d(TAG, "visibility", "$visibility on $logString") updateScrimOpaque() } @@ -467,8 +469,4 @@ constructor( PorterDuff.Mode.MULTIPLY ) } - - private fun logString(): String { - return this::class.simpleName!! + "@" + hashCode() - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index 9b8dd0b75a24..24ac70e63e46 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -15,17 +15,17 @@ */ package com.android.systemui.statusbar; +import static android.app.Flags.keyguardPrivateNotifications; import static android.app.StatusBarManager.ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED; import static android.app.StatusBarManager.EXTRA_KM_PRIVATE_NOTIFS_ALLOWED; import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED; import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS; import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS; +import static android.os.Flags.allowPrivateProfile; import static android.os.UserHandle.USER_ALL; import static android.os.UserHandle.USER_NULL; import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS; import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS; -import static android.app.Flags.keyguardPrivateNotifications; -import static android.os.Flags.allowPrivateProfile; import static com.android.systemui.DejankUtils.whitelistIpcs; @@ -42,9 +42,8 @@ import android.content.IntentFilter; import android.content.IntentSender; import android.content.pm.UserInfo; import android.database.ContentObserver; +import android.database.ExecutorContentObserver; import android.net.Uri; -import android.os.Handler; -import android.os.HandlerExecutor; import android.os.Looper; import android.os.UserHandle; import android.os.UserManager; @@ -79,17 +78,17 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.ListenerSet; import com.android.systemui.util.settings.SecureSettings; +import dagger.Lazy; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.concurrent.Executor; import java.util.Objects; +import java.util.concurrent.Executor; import javax.inject.Inject; -import dagger.Lazy; - /** * Handles keeping track of the current user, profiles, and various things related to hiding * contents, redacting notifications, and the lockscreen. @@ -228,7 +227,7 @@ public class NotificationLockscreenUserManagerImpl implements updateCurrentProfilesCache(); if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL); - mBackgroundHandler.post(() -> { + mBackgroundExecutor.execute(() -> { initValuesForUser(userId); }); } @@ -289,8 +288,7 @@ public class NotificationLockscreenUserManagerImpl implements }; protected final Context mContext; - private final Handler mMainHandler; - private final Handler mBackgroundHandler; + private final Executor mMainExecutor; private final Executor mBackgroundExecutor; /** The current user and its profiles (possibly including a communal profile). */ protected final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>(); @@ -313,8 +311,7 @@ public class NotificationLockscreenUserManagerImpl implements Lazy<OverviewProxyService> overviewProxyServiceLazy, KeyguardManager keyguardManager, StatusBarStateController statusBarStateController, - @Main Handler mainHandler, - @Background Handler backgroundHandler, + @Main Executor mainExecutor, @Background Executor backgroundExecutor, DeviceProvisionedController deviceProvisionedController, KeyguardStateController keyguardStateController, @@ -323,8 +320,7 @@ public class NotificationLockscreenUserManagerImpl implements LockPatternUtils lockPatternUtils, FeatureFlagsClassic featureFlags) { mContext = context; - mMainHandler = mainHandler; - mBackgroundHandler = backgroundHandler; + mMainExecutor = mainExecutor; mBackgroundExecutor = backgroundExecutor; mDevicePolicyManager = devicePolicyManager; mUserManager = userManager; @@ -362,10 +358,10 @@ public class NotificationLockscreenUserManagerImpl implements } private void init() { - mLockscreenSettingsObserver = new ContentObserver( + mLockscreenSettingsObserver = new ExecutorContentObserver( mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD) - ? mBackgroundHandler - : mMainHandler) { + ? mBackgroundExecutor + : mMainExecutor) { @Override public void onChange(boolean selfChange, Collection<Uri> uris, int flags) { @@ -412,7 +408,7 @@ public class NotificationLockscreenUserManagerImpl implements } }; - mSettingsObserver = new ContentObserver(mMainHandler) { + mSettingsObserver = new ExecutorContentObserver(mMainExecutor) { @Override public void onChange(boolean selfChange) { updateLockscreenNotificationSetting(); @@ -468,14 +464,14 @@ public class NotificationLockscreenUserManagerImpl implements mContext.registerReceiver(mBaseBroadcastReceiver, internalFilter, PERMISSION_SELF, null, Context.RECEIVER_EXPORTED_UNAUDITED); - mUserTracker.addCallback(mUserChangedCallback, new HandlerExecutor(mMainHandler)); + mUserTracker.addCallback(mUserChangedCallback, mMainExecutor); mCurrentUserId = mUserTracker.getUserId(); // in case we reg'd receiver too late updateCurrentProfilesCache(); if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { // Set up - mBackgroundHandler.post(() -> { + mBackgroundExecutor.execute(() -> { @SuppressLint("MissingPermission") List<UserInfo> users = mUserManager.getUsers(); for (int i = users.size() - 1; i >= 0; i--) { initValuesForUser(users.get(i).id); @@ -796,7 +792,7 @@ public class NotificationLockscreenUserManagerImpl implements } } } - mMainHandler.post(() -> { + mMainExecutor.execute(() -> { for (UserChangedListener listener : mListeners) { listener.onCurrentProfilesChanged(mCurrentProfiles); } @@ -895,7 +891,7 @@ public class NotificationLockscreenUserManagerImpl implements private void notifyNotificationStateChanged() { if (!Looper.getMainLooper().isCurrentThread()) { - mMainHandler.post(() -> { + mMainExecutor.execute(() -> { for (NotificationStateChangedListener listener : mNotifStateChangedListeners) { listener.onNotificationStateChanged(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt index 625fdc1c12f8..4b8fb1e5a0ff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt @@ -18,12 +18,15 @@ package com.android.systemui.statusbar.notification.stack.domain.interactor import android.content.Context +import com.android.systemui.Flags.centralizedStatusBarDimensRefactor import com.android.systemui.common.ui.data.repository.ConfigurationRepository import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.res.R +import com.android.systemui.shade.LargeScreenHeaderHelper import com.android.systemui.statusbar.policy.SplitShadeStateController +import dagger.Lazy import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow @@ -45,6 +48,7 @@ constructor( private val splitShadeStateController: SplitShadeStateController, keyguardInteractor: KeyguardInteractor, deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, + largeScreenHeaderHelperLazy: Lazy<LargeScreenHeaderHelper>, ) { private val _topPosition = MutableStateFlow(0f) @@ -72,7 +76,11 @@ constructor( getDimensionPixelSize(R.dimen.notification_panel_margin_bottom), marginTop = getDimensionPixelSize(R.dimen.notification_panel_margin_top), marginTopLargeScreen = - getDimensionPixelSize(R.dimen.large_screen_shade_header_height), + if (centralizedStatusBarDimensRefactor()) { + largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight() + } else { + getDimensionPixelSize(R.dimen.large_screen_shade_header_height) + }, keyguardSplitShadeTopMargin = getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin), ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt index 0bad47ecb2ae..8a45ec15b627 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt @@ -18,12 +18,23 @@ package com.android.systemui.statusbar.phone import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags +import com.android.systemui.Flags.smartspaceRelocateToBottom +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import com.android.systemui.res.R +import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController import com.android.systemui.util.ViewController import javax.inject.Inject class KeyguardBottomAreaViewController - @Inject constructor(view: KeyguardBottomAreaView, featureFlags: FeatureFlagsClassic) : - ViewController<KeyguardBottomAreaView> (view) { + @Inject constructor( + view: KeyguardBottomAreaView, + private val smartspaceController: LockscreenSmartspaceController, + featureFlags: FeatureFlagsClassic +) : ViewController<KeyguardBottomAreaView> (view) { + + private var smartspaceView: View? = null init { view.setIsLockscreenLandscapeEnabled( @@ -31,6 +42,14 @@ class KeyguardBottomAreaViewController } override fun onViewAttached() { + if (!smartspaceRelocateToBottom() || !smartspaceController.isEnabled()) { + return + } + + val ambientIndicationArea = mView.findViewById<View>(R.id.ambient_indication_container) + ambientIndicationArea?.visibility = View.GONE + + addSmartspaceView() } override fun onViewDetached() { @@ -40,4 +59,24 @@ class KeyguardBottomAreaViewController // TODO: remove this method. return mView } + + private fun addSmartspaceView() { + if (!smartspaceRelocateToBottom()) { + return + } + + val smartspaceContainer = mView.findViewById<View>(R.id.smartspace_container) + smartspaceContainer!!.visibility = View.VISIBLE + + smartspaceView = smartspaceController.buildAndConnectView(smartspaceContainer as ViewGroup) + val lp = LinearLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) + (smartspaceContainer as ViewGroup).addView(smartspaceView, 0, lp) + val startPadding = context.resources.getDimensionPixelSize( + R.dimen.below_clock_padding_start) + val endPadding = context.resources.getDimensionPixelSize( + R.dimen.below_clock_padding_end) + smartspaceView?.setPaddingRelative(startPadding, 0, endPadding, 0) +// mKeyguardUnlockAnimationController.lockscreenSmartspace = smartspaceView + } } 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 0a03af7d9387..ca3e3c629619 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -16,10 +16,12 @@ package com.android.systemui.statusbar.phone; +import static com.android.systemui.Flags.centralizedStatusBarDimensRefactor; import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInScale; import static com.android.systemui.statusbar.notification.NotificationUtils.interpolate; +import android.content.Context; import android.content.res.Resources; import android.util.MathUtils; @@ -30,6 +32,7 @@ import com.android.systemui.log.LogBuffer; import com.android.systemui.log.core.Logger; import com.android.systemui.log.dagger.KeyguardClockLog; import com.android.systemui.res.R; +import com.android.systemui.shade.LargeScreenHeaderHelper; import com.android.systemui.shade.ShadeViewController; import com.android.systemui.statusbar.policy.KeyguardUserSwitcherListView; @@ -160,14 +163,14 @@ public class KeyguardClockPositionAlgorithm { mLogger = new Logger(logBuffer, TAG); } - /** - * Refreshes the dimension values. - */ - public void loadDimens(Resources res) { - mStatusViewBottomMargin = res.getDimensionPixelSize( - R.dimen.keyguard_status_view_bottom_margin); + /** Refreshes the dimension values. */ + public void loadDimens(Context context, Resources res) { + mStatusViewBottomMargin = + res.getDimensionPixelSize(R.dimen.keyguard_status_view_bottom_margin); mSplitShadeTopNotificationsMargin = - res.getDimensionPixelSize(R.dimen.large_screen_shade_header_height); + centralizedStatusBarDimensRefactor() + ? LargeScreenHeaderHelper.getLargeScreenHeaderHeight(context) + : res.getDimensionPixelSize(R.dimen.large_screen_shade_header_height); mSplitShadeTargetTopMargin = res.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java index 7cc08887ae52..769145923886 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.phone; +import static com.android.systemui.Flags.centralizedStatusBarDimensRefactor; import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection; import static com.android.systemui.util.Utils.getStatusBarHeaderHeightKeyguard; @@ -130,6 +131,9 @@ public class KeyguardStatusBarView extends RelativeLayout { mUserSwitcherContainer = findViewById(R.id.user_switcher_container); mIsPrivacyDotEnabled = mContext.getResources().getBoolean(R.bool.config_enablePrivacyDot); loadDimens(); + if (!centralizedStatusBarDimensRefactor()) { + setGravity(Gravity.CENTER_VERTICAL); + } } /** @@ -307,7 +311,8 @@ public class KeyguardStatusBarView extends RelativeLayout { final int minRight = (!isLayoutRtl() && mIsPrivacyDotEnabled) ? Math.max(mMinDotWidth, mPadding.right) : mPadding.right; - setPadding(minLeft, waterfallTop, minRight, 0); + int top = centralizedStatusBarDimensRefactor() ? waterfallTop + mPadding.top : waterfallTop; + setPadding(minLeft, top, minRight, 0); } private boolean updateLayoutParamsNoCutout() { 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 30a445f7ce4a..703b3c65029a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java @@ -118,15 +118,25 @@ public class StatusBarSignalPolicy implements SignalCallback, private void updateVpn() { boolean vpnVisible = mSecurityController.isVpnEnabled(); - int vpnIconId = currentVpnIconId(mSecurityController.isVpnBranded()); + int vpnIconId = currentVpnIconId( + mSecurityController.isVpnBranded(), + mSecurityController.isVpnValidated()); mIconController.setIcon(mSlotVpn, vpnIconId, mContext.getResources().getString(R.string.accessibility_vpn_on)); mIconController.setIconVisibility(mSlotVpn, vpnVisible); } - private int currentVpnIconId(boolean isBranded) { - return isBranded ? R.drawable.stat_sys_branded_vpn : R.drawable.stat_sys_vpn_ic; + private int currentVpnIconId(boolean isBranded, boolean isValidated) { + if (isBranded) { + return isValidated + ? R.drawable.stat_sys_branded_vpn + : R.drawable.stat_sys_no_internet_branded_vpn; + } else { + return isValidated + ? R.drawable.stat_sys_vpn_ic + : R.drawable.stat_sys_no_internet_vpn_ic; + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt index 8fc8b2f31366..de46a5ed99d6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt @@ -19,7 +19,7 @@ package com.android.systemui.statusbar.pipeline.satellite.data.prod import android.os.OutcomeReceiver import android.telephony.satellite.NtnSignalStrengthCallback import android.telephony.satellite.SatelliteManager -import android.telephony.satellite.SatelliteStateCallback +import android.telephony.satellite.SatelliteModemStateCallback import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -180,7 +180,7 @@ constructor( // By using the SupportedSatelliteManager here, we expect registration never to fail private fun connectionStateFlow(sm: SupportedSatelliteManager): Flow<SatelliteConnectionState> = conflatedCallbackFlow { - val cb = SatelliteStateCallback { state -> + val cb = SatelliteModemStateCallback { state -> trySend(SatelliteConnectionState.fromModemState(state)) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java index 3be14bc867a1..10bf0680b567 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java @@ -48,6 +48,8 @@ public interface SecurityController extends CallbackController<SecurityControlle boolean isNetworkLoggingEnabled(); boolean isVpnEnabled(); boolean isVpnRestricted(); + /** Whether the VPN network is validated. */ + boolean isVpnValidated(); /** Whether the VPN app should use branded VPN iconography. */ boolean isVpnBranded(); String getPrimaryVpnName(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java index 5d69f367d77e..9f4a90658b2e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java @@ -15,6 +15,9 @@ */ package com.android.systemui.statusbar.policy; +import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; +import static android.net.NetworkCapabilities.TRANSPORT_VPN; + import android.annotation.Nullable; import android.app.admin.DeviceAdminInfo; import android.app.admin.DevicePolicyManager; @@ -32,7 +35,9 @@ import android.content.pm.UserInfo; import android.graphics.drawable.Drawable; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; +import android.net.LinkProperties; import android.net.Network; +import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.VpnManager; import android.os.Handler; @@ -76,7 +81,10 @@ public class SecurityControllerImpl implements SecurityController { private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final NetworkRequest REQUEST = - new NetworkRequest.Builder().clearCapabilities().build(); + new NetworkRequest.Builder() + .clearCapabilities() + .addTransportType(TRANSPORT_VPN) + .build(); private static final int NO_NETWORK = -1; private static final String VPN_BRANDED_META_DATA = "com.android.systemui.IS_BRANDED"; @@ -99,6 +107,8 @@ public class SecurityControllerImpl implements SecurityController { private SparseArray<VpnConfig> mCurrentVpns = new SparseArray<>(); private int mCurrentUserId; private int mVpnUserId; + @GuardedBy("mNetworkProperties") + private final SparseArray<NetworkProperties> mNetworkProperties = new SparseArray<>(); // Key: userId, Value: whether the user has CACerts installed // Needs to be cached here since the query has to be asynchronous @@ -162,6 +172,21 @@ public class SecurityControllerImpl implements SecurityController { pw.print(mCurrentVpns.valueAt(i).user); } pw.println("}"); + pw.print(" mNetworkProperties={"); + synchronized (mNetworkProperties) { + for (int i = 0; i < mNetworkProperties.size(); ++i) { + if (i > 0) { + pw.print(", "); + } + pw.print(mNetworkProperties.keyAt(i)); + pw.print("={"); + pw.print(mNetworkProperties.valueAt(i).interfaceName); + pw.print(", "); + pw.print(mNetworkProperties.valueAt(i).validated); + pw.print("}"); + } + } + pw.println("}"); } @Override @@ -304,6 +329,26 @@ public class SecurityControllerImpl implements SecurityController { } @Override + public boolean isVpnValidated() { + // Prioritize reporting the network status of the parent user. + final VpnConfig primaryVpnConfig = mCurrentVpns.get(mVpnUserId); + if (primaryVpnConfig != null) { + return getVpnValidationStatus(primaryVpnConfig); + } + // Identify any Unvalidated status in each active VPN network within other profiles. + for (int profileId : mUserManager.getEnabledProfileIds(mVpnUserId)) { + final VpnConfig vpnConfig = mCurrentVpns.get(profileId); + if (vpnConfig == null) { + continue; + } + if (!getVpnValidationStatus(vpnConfig)) { + return false; + } + } + return true; + } + + @Override public boolean hasCACertInCurrentUser() { Boolean hasCACerts = mHasCACerts.get(mCurrentUserId); return hasCACerts != null && hasCACerts.booleanValue(); @@ -493,11 +538,74 @@ public class SecurityControllerImpl implements SecurityController { @Override public void onLost(Network network) { if (DEBUG) Log.d(TAG, "onLost " + network.getNetId()); + synchronized (mNetworkProperties) { + mNetworkProperties.delete(network.getNetId()); + } updateState(); fireCallbacks(); }; + + + @Override + public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) { + if (DEBUG) Log.d(TAG, "onCapabilitiesChanged " + network.getNetId()); + final NetworkProperties properties; + synchronized (mNetworkProperties) { + properties = mNetworkProperties.get(network.getNetId()); + } + // When a new network appears, the system first notifies the application about + // its capabilities through onCapabilitiesChanged. This initial notification + // will be skipped because the interface information is included in the + // subsequent onLinkPropertiesChanged call. After validating the network, the + // system might send another onCapabilitiesChanged notification if the network + // becomes validated. + if (properties == null) { + return; + } + final boolean validated = nc.hasCapability(NET_CAPABILITY_VALIDATED); + if (properties.validated != validated) { + properties.validated = validated; + fireCallbacks(); + } + } + + @Override + public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) { + if (DEBUG) Log.d(TAG, "onLinkPropertiesChanged " + network.getNetId()); + final String interfaceName = linkProperties.getInterfaceName(); + if (interfaceName == null) { + Log.w(TAG, "onLinkPropertiesChanged event with null interface"); + return; + } + synchronized (mNetworkProperties) { + final NetworkProperties properties = mNetworkProperties.get(network.getNetId()); + if (properties == null) { + mNetworkProperties.put( + network.getNetId(), + new NetworkProperties(interfaceName, false)); + } else { + properties.interfaceName = interfaceName; + } + } + } }; + /** + * Retrieve the validation status of the VPN network associated with the given VpnConfig. + */ + private boolean getVpnValidationStatus(@NonNull VpnConfig vpnConfig) { + synchronized (mNetworkProperties) { + // Find the network has the same interface as the VpnConfig + for (int i = 0; i < mNetworkProperties.size(); ++i) { + if (mNetworkProperties.valueAt(i).interfaceName.equals(vpnConfig.interfaze)) { + return mNetworkProperties.valueAt(i).validated; + } + } + } + // If no matching network is found, consider it validated. + return true; + } + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (KeyChain.ACTION_TRUST_STORE_CHANGED.equals(intent.getAction())) { @@ -508,4 +616,17 @@ public class SecurityControllerImpl implements SecurityController { } } }; + + /** + * A data class to hold specific Network properties received through the NetworkCallback. + */ + private static class NetworkProperties { + public String interfaceName; + public boolean validated; + + NetworkProperties(@NonNull String interfaceName, boolean validated) { + this.interfaceName = interfaceName; + this.validated = validated; + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java index 20fef927e8d1..e57c0e7f1044 100644 --- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java @@ -210,7 +210,13 @@ public class ImageWallpaper extends WallpaperService { if (DEBUG) { Log.i(TAG, "onSurfaceDestroyed"); } - mSurfaceHolder = null; + mLongExecutor.execute(this::onSurfaceDestroyedSynchronized); + } + + private void onSurfaceDestroyedSynchronized() { + synchronized (mLock) { + mSurfaceHolder = null; + } } @Override @@ -241,7 +247,7 @@ public class ImageWallpaper extends WallpaperService { private void drawFrameInternal() { if (mSurfaceHolder == null) { - Log.e(TAG, "attempt to draw a frame without a valid surface"); + Log.i(TAG, "attempt to draw a frame without a valid surface"); return; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java index 837a13073eea..2afb3a15b4be 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java @@ -43,7 +43,6 @@ import android.view.WindowManagerGlobal; import android.view.accessibility.IRemoteMagnificationAnimationCallback; import android.view.animation.AccelerateInterpolator; -import androidx.test.filters.FlakyTest; import androidx.test.filters.LargeTest; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; @@ -68,7 +67,6 @@ import java.util.concurrent.atomic.AtomicReference; @LargeTest @RunWith(AndroidTestingRunner.class) -@FlakyTest(bugId = 308501761) public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { @Rule diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java new file mode 100644 index 000000000000..9dd337e43b6a --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.accessibility.floatingmenu; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.Notification; +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidTestingRunner.class) +@SmallTest +public class MenuNotificationFactoryTest extends SysuiTestCase { + private MenuNotificationFactory mMenuNotificationFactory; + + @Before + public void setUp() { + mMenuNotificationFactory = new MenuNotificationFactory(mContext); + } + + @Test + public void createHiddenNotification_hasUndoAndDeleteAction() { + Notification notification = mMenuNotificationFactory.createHiddenNotification(); + + assertThat(notification.contentIntent.getIntent().getAction()).isEqualTo( + MenuNotificationFactory.ACTION_UNDO); + assertThat(notification.deleteIntent.getIntent().getAction()).isEqualTo( + MenuNotificationFactory.ACTION_DELETE); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java index be6f3ff8d3f9..68879a54cfe4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java @@ -21,15 +21,30 @@ import static android.view.View.VISIBLE; import static android.view.WindowInsets.Type.displayCutout; import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.systemBars; + +import static com.android.systemui.accessibility.floatingmenu.MenuNotificationFactory.ACTION_DELETE; +import static com.android.systemui.accessibility.floatingmenu.MenuNotificationFactory.ACTION_UNDO; import static com.android.systemui.accessibility.floatingmenu.MenuViewLayer.LayerIndex; + 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.doReturn; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.accessibilityservice.AccessibilityServiceInfo; +import android.app.Notification; +import android.app.NotificationManager; +import android.content.BroadcastReceiver; import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; @@ -40,6 +55,8 @@ import android.os.Build; import android.os.UserHandle; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.provider.Settings; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -53,16 +70,21 @@ import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.SpringAnimation; import androidx.test.filters.SmallTest; +import com.android.internal.messages.nano.SystemMessageProto; import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; +import com.android.systemui.SysuiTestableContext; import com.android.systemui.util.settings.SecureSettings; +import com.android.wm.shell.common.magnetictarget.MagnetizedObject; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; +import org.mockito.Spy; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -98,6 +120,12 @@ public class MenuViewLayerTest extends SysuiTestCase { @Rule public MockitoRule mockito = MockitoJUnit.rule(); + @Rule + public final CheckFlagsRule mCheckFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); + + @Spy + private SysuiTestableContext mSpyContext = getContext(); @Mock private IAccessibilityFloatingMenu mFloatingMenu; @@ -110,8 +138,12 @@ public class MenuViewLayerTest extends SysuiTestCase { @Mock private AccessibilityManager mStubAccessibilityManager; + private final NotificationManager mMockNotificationManager = mock(NotificationManager.class); + @Before public void setUp() throws Exception { + mSpyContext.addMockSystemService(Context.NOTIFICATION_SERVICE, mMockNotificationManager); + final Rect mDisplayBounds = new Rect(); mDisplayBounds.set(/* left= */ 0, /* top= */ 0, DISPLAY_WINDOW_WIDTH, DISPLAY_WINDOW_HEIGHT); @@ -119,31 +151,31 @@ public class MenuViewLayerTest extends SysuiTestCase { new WindowMetrics(mDisplayBounds, fakeDisplayInsets(), /* density = */ 0.0f)); doReturn(mWindowMetrics).when(mStubWindowManager).getCurrentWindowMetrics(); - mMenuViewLayer = new MenuViewLayer(mContext, mStubWindowManager, mStubAccessibilityManager, - mFloatingMenu, mSecureSettings); + mMenuViewLayer = new MenuViewLayer(mSpyContext, mStubWindowManager, + mStubAccessibilityManager, mFloatingMenu, mSecureSettings); mMenuView = (MenuView) mMenuViewLayer.getChildAt(LayerIndex.MENU_VIEW); mMenuAnimationController = mMenuView.getMenuAnimationController(); mLastAccessibilityButtonTargets = - Settings.Secure.getStringForUser(mContext.getContentResolver(), + Settings.Secure.getStringForUser(mSpyContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, UserHandle.USER_CURRENT); mLastEnabledAccessibilityServices = - Settings.Secure.getStringForUser(mContext.getContentResolver(), + Settings.Secure.getStringForUser(mSpyContext.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, UserHandle.USER_CURRENT); mMenuViewLayer.onAttachedToWindow(); - Settings.Secure.putStringForUser(mContext.getContentResolver(), + Settings.Secure.putStringForUser(mSpyContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, "", UserHandle.USER_CURRENT); - Settings.Secure.putStringForUser(mContext.getContentResolver(), + Settings.Secure.putStringForUser(mSpyContext.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "", UserHandle.USER_CURRENT); } @After public void tearDown() throws Exception { - Settings.Secure.putStringForUser(mContext.getContentResolver(), + Settings.Secure.putStringForUser(mSpyContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, mLastAccessibilityButtonTargets, UserHandle.USER_CURRENT); - Settings.Secure.putStringForUser(mContext.getContentResolver(), + Settings.Secure.putStringForUser(mSpyContext.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, mLastEnabledAccessibilityServices, UserHandle.USER_CURRENT); @@ -188,7 +220,7 @@ public class MenuViewLayerTest extends SysuiTestCase { setupEnabledAccessibilityServiceList(); mMenuViewLayer.mDismissMenuAction.run(); - final String value = Settings.Secure.getString(mContext.getContentResolver(), + final String value = Settings.Secure.getString(mSpyContext.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); assertThat(value).isEqualTo(""); @@ -203,7 +235,7 @@ public class MenuViewLayerTest extends SysuiTestCase { AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY)).thenReturn(stubShortcutTargets); mMenuViewLayer.mDismissMenuAction.run(); - final String value = Settings.Secure.getString(mContext.getContentResolver(), + final String value = Settings.Secure.getString(mSpyContext.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); assertThat(value).isEqualTo(TEST_SELECT_TO_SPEAK_COMPONENT_NAME.flattenToString()); @@ -278,9 +310,60 @@ public class MenuViewLayerTest extends SysuiTestCase { assertThat(mMenuView.getTranslationX()).isEqualTo(beforePosition.x); assertThat(mMenuView.getTranslationY()).isEqualTo(beforePosition.y); } + @Test + @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_HIDE) + public void onReleasedInTarget_hideMenuAndShowNotificationWithExpectedActions() { + dragMenuThenReleasedInTarget(); + + verify(mMockNotificationManager).notify( + eq(SystemMessageProto.SystemMessage.NOTE_A11Y_FLOATING_MENU_HIDDEN), + any(Notification.class)); + ArgumentCaptor<IntentFilter> intentFilterCaptor = ArgumentCaptor.forClass( + IntentFilter.class); + verify(mSpyContext).registerReceiver( + any(BroadcastReceiver.class), + intentFilterCaptor.capture(), + anyInt()); + assertThat(intentFilterCaptor.getValue().matchAction(ACTION_UNDO)).isTrue(); + assertThat(intentFilterCaptor.getValue().matchAction(ACTION_DELETE)).isTrue(); + } + + @Test + @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_HIDE) + public void receiveActionUndo_dismissNotificationAndMenuVisible() { + ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = ArgumentCaptor.forClass( + BroadcastReceiver.class); + dragMenuThenReleasedInTarget(); + + verify(mSpyContext).registerReceiver(broadcastReceiverCaptor.capture(), + any(IntentFilter.class), anyInt()); + broadcastReceiverCaptor.getValue().onReceive(mSpyContext, new Intent(ACTION_UNDO)); + + verify(mSpyContext).unregisterReceiver(broadcastReceiverCaptor.getValue()); + verify(mMockNotificationManager).cancel( + SystemMessageProto.SystemMessage.NOTE_A11Y_FLOATING_MENU_HIDDEN); + assertThat(mMenuView.getVisibility()).isEqualTo(VISIBLE); + } + + @Test + @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_HIDE) + public void receiveActionDelete_dismissNotificationAndHideMenu() { + ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = ArgumentCaptor.forClass( + BroadcastReceiver.class); + dragMenuThenReleasedInTarget(); + + verify(mSpyContext).registerReceiver(broadcastReceiverCaptor.capture(), + any(IntentFilter.class), anyInt()); + broadcastReceiverCaptor.getValue().onReceive(mSpyContext, new Intent(ACTION_DELETE)); + + verify(mSpyContext).unregisterReceiver(broadcastReceiverCaptor.getValue()); + verify(mMockNotificationManager).cancel( + SystemMessageProto.SystemMessage.NOTE_A11Y_FLOATING_MENU_HIDDEN); + verify(mFloatingMenu).hide(); + } private void setupEnabledAccessibilityServiceList() { - Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.putString(mSpyContext.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, TEST_SELECT_TO_SPEAK_COMPONENT_NAME.flattenToString()); @@ -344,6 +427,12 @@ public class MenuViewLayerTest extends SysuiTestCase { springAnimation.skipToEnd(); springAnimation.doAnimationFrame(500); }); + } + private void dragMenuThenReleasedInTarget() { + MagnetizedObject.MagnetListener magnetListener = + mMenuViewLayer.getDragToInteractAnimationController().getMagnetListener(); + magnetListener.onReleasedInTarget( + new MagnetizedObject.MagneticTarget(mock(View.class), 200)); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt index edd781dec3a0..2d9d5ed2b5e1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt @@ -20,14 +20,16 @@ import androidx.test.filters.SmallTest import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +import com.android.systemui.coroutines.collectValues +import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep -import com.android.systemui.util.mockito.mock +import com.android.systemui.kosmos.testScope +import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlin.time.Duration.Companion.milliseconds -import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test @@ -37,23 +39,21 @@ import org.junit.runners.JUnit4 @SmallTest @RunWith(JUnit4::class) class KeyguardTransitionAnimationFlowTest : SysuiTestCase() { - private lateinit var underTest: KeyguardTransitionAnimationFlow.SharedFlowBuilder - private lateinit var repository: FakeKeyguardTransitionRepository - private lateinit var testScope: TestScope + val kosmos = testKosmos() + val testScope = kosmos.testScope + val animationFlow = kosmos.keyguardTransitionAnimationFlow + val repository = kosmos.fakeKeyguardTransitionRepository + + private lateinit var underTest: KeyguardTransitionAnimationFlow.FlowBuilder @Before fun setUp() { - testScope = TestScope() - repository = FakeKeyguardTransitionRepository() underTest = - KeyguardTransitionAnimationFlow( - testScope.backgroundScope, - mock(), - ) - .setup( - duration = 1000.milliseconds, - stepFlow = repository.transitions, - ) + animationFlow.setup( + duration = 1000.milliseconds, + from = KeyguardState.GONE, + to = KeyguardState.DREAMING, + ) } @Test(expected = IllegalArgumentException::class) @@ -83,6 +83,8 @@ class KeyguardTransitionAnimationFlowTest : SysuiTestCase() { onFinish = { 10f }, ) var animationValues = collectLastValue(flow) + runCurrent() + repository.sendTransitionStep(step(1f, TransitionState.FINISHED), validateStep = false) assertThat(animationValues()).isEqualTo(10f) } @@ -97,6 +99,8 @@ class KeyguardTransitionAnimationFlowTest : SysuiTestCase() { onCancel = { 100f }, ) var animationValues = collectLastValue(flow) + runCurrent() + repository.sendTransitionStep(step(0.5f, TransitionState.CANCELED)) assertThat(animationValues()).isEqualTo(100f) } @@ -111,6 +115,8 @@ class KeyguardTransitionAnimationFlowTest : SysuiTestCase() { onStep = { it }, ) var animationValues = collectLastValue(flow) + runCurrent() + repository.sendTransitionStep(step(0f, TransitionState.STARTED)) assertThat(animationValues()).isEqualTo(0f) @@ -137,6 +143,8 @@ class KeyguardTransitionAnimationFlowTest : SysuiTestCase() { onStep = { it }, ) var animationValues = collectLastValue(flow) + runCurrent() + repository.sendTransitionStep(step(0f, TransitionState.STARTED)) assertFloat(animationValues(), EMPHASIZED_ACCELERATE.getInterpolation(0f)) repository.sendTransitionStep(step(0.5f, TransitionState.RUNNING)) @@ -157,17 +165,56 @@ class KeyguardTransitionAnimationFlowTest : SysuiTestCase() { duration = 1000.milliseconds, onStep = { it * 2 }, ) - var animationValues = collectLastValue(flow) + val animationValues by collectLastValue(flow) + runCurrent() + repository.sendTransitionStep(step(0f, TransitionState.STARTED)) - assertFloat(animationValues(), 0f) + assertFloat(animationValues, 0f) repository.sendTransitionStep(step(0.3f, TransitionState.RUNNING)) - assertFloat(animationValues(), 0.6f) + assertFloat(animationValues, 0.6f) repository.sendTransitionStep(step(0.6f, TransitionState.RUNNING)) - assertFloat(animationValues(), 1.2f) + assertFloat(animationValues, 1.2f) repository.sendTransitionStep(step(0.8f, TransitionState.RUNNING)) - assertFloat(animationValues(), 1.6f) + assertFloat(animationValues, 1.6f) repository.sendTransitionStep(step(1f, TransitionState.RUNNING)) - assertFloat(animationValues(), 2f) + assertFloat(animationValues, 2f) + } + + @Test + fun sameFloatValueWithTheSameTransitionStateDoesNotEmitTwice() = + testScope.runTest { + val flow = + underTest.sharedFlow( + duration = 1000.milliseconds, + onStep = { it }, + ) + val values by collectValues(flow) + runCurrent() + + repository.sendTransitionStep(step(0.3f, TransitionState.RUNNING)) + repository.sendTransitionStep(step(0.3f, TransitionState.RUNNING)) + + assertThat(values.size).isEqualTo(1) + assertThat(values[0]).isEqualTo(0.3f) + } + + @Test + fun sameFloatValueWithADifferentTransitionStateDoesEmitTwice() = + testScope.runTest { + val flow = + underTest.sharedFlow( + duration = 1000.milliseconds, + onStep = { it }, + ) + val values by collectValues(flow) + runCurrent() + + repository.sendTransitionStep(step(0.3f, TransitionState.STARTED)) + repository.sendTransitionStep(step(0.3f, TransitionState.RUNNING)) + + assertThat(values.size).isEqualTo(2) + assertThat(values[0]).isEqualTo(0.3f) + assertThat(values[0]).isEqualTo(0.3f) } private fun assertFloat(actual: Float?, expected: Float) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt index d959872fa80c..87391cce9136 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt @@ -31,6 +31,7 @@ import com.android.systemui.testKosmos import com.google.common.collect.Range import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @@ -50,6 +51,7 @@ class AlternateBouncerViewModelTest : SysuiTestCase() { fun transitionToAlternateBouncer_scrimAlphaUpdate() = testScope.runTest { val scrimAlphas by collectValues(underTest.scrimAlpha) + runCurrent() transitionRepository.sendTransitionSteps( listOf( @@ -69,17 +71,17 @@ class AlternateBouncerViewModelTest : SysuiTestCase() { fun transitionFromAlternateBouncer_scrimAlphaUpdate() = testScope.runTest { val scrimAlphas by collectValues(underTest.scrimAlpha) + runCurrent() transitionRepository.sendTransitionSteps( listOf( - stepToAlternateBouncer(0f, TransitionState.STARTED), - stepToAlternateBouncer(.4f), - stepToAlternateBouncer(.6f), - stepToAlternateBouncer(1f), + stepFromAlternateBouncer(0f, TransitionState.STARTED), + stepFromAlternateBouncer(.4f), + stepFromAlternateBouncer(.6f), + stepFromAlternateBouncer(1f), ), testScope, ) - assertThat(scrimAlphas.size).isEqualTo(4) scrimAlphas.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt index af8d8a8978b5..795e68ddf335 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt @@ -30,6 +30,7 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @@ -63,6 +64,7 @@ class AodToLockscreenTransitionViewModelTest : SysuiTestCase() { fingerprintPropertyRepository.supportsUdfps() val deviceEntryBackgroundViewAlpha by collectLastValue(underTest.deviceEntryBackgroundViewAlpha) + runCurrent() // fade in repository.sendTransitionStep(step(0f, TransitionState.STARTED)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt index daafe12514ba..75994da6c934 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt @@ -39,6 +39,7 @@ import com.google.common.collect.Range import com.google.common.truth.Truth.assertThat import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test @@ -78,6 +79,8 @@ class BouncerToGoneFlowsTest : SysuiTestCase() { fun scrimAlpha_runDimissFromKeyguard_shadeExpanded() = testScope.runTest { val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER)) + runCurrent() + shadeRepository.setLockscreenShadeExpansion(1f) whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true) @@ -101,6 +104,8 @@ class BouncerToGoneFlowsTest : SysuiTestCase() { fun scrimAlpha_runDimissFromKeyguard_shadeNotExpanded() = testScope.runTest { val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER)) + runCurrent() + shadeRepository.setLockscreenShadeExpansion(0f) whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true) @@ -123,6 +128,7 @@ class BouncerToGoneFlowsTest : SysuiTestCase() { fun scrimBehindAlpha_leaveShadeOpen() = testScope.runTest { val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER)) + runCurrent() sysuiStatusBarStateController.setLeaveOpenOnKeyguardHide(true) @@ -146,6 +152,8 @@ class BouncerToGoneFlowsTest : SysuiTestCase() { fun scrimBehindAlpha_doNotLeaveShadeOpen() = testScope.runTest { val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER)) + runCurrent() + keyguardTransitionRepository.sendTransitionSteps( listOf( step(0f, TransitionState.STARTED), diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt index dd542d482745..471029b17873 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt @@ -20,18 +20,15 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectValues -import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory +import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep -import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow -import com.android.systemui.util.mockito.mock +import com.android.systemui.kosmos.testScope +import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest -import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -39,29 +36,10 @@ import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) class DozingToLockscreenTransitionViewModelTest : SysuiTestCase() { - private lateinit var testScope: TestScope - private lateinit var underTest: DozingToLockscreenTransitionViewModel - private lateinit var repository: FakeKeyguardTransitionRepository - - @Before - fun setUp() { - testScope = TestScope() - repository = FakeKeyguardTransitionRepository() - underTest = - DozingToLockscreenTransitionViewModel( - interactor = - KeyguardTransitionInteractorFactory.create( - scope = testScope.backgroundScope, - repository = repository, - ) - .keyguardTransitionInteractor, - animationFlow = - KeyguardTransitionAnimationFlow( - scope = testScope.backgroundScope, - logger = mock() - ), - ) - } + val kosmos = testKosmos() + val testScope = kosmos.testScope + val repository = kosmos.fakeKeyguardTransitionRepository + val underTest = kosmos.dozingToLockscreenTransitionViewModel @Test fun deviceEntryParentViewShows() = diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt index a105008f3f37..1c9c942eafc6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt @@ -31,6 +31,7 @@ import com.android.systemui.testKosmos import com.google.common.collect.Range import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @@ -52,6 +53,7 @@ class GoneToAodTransitionViewModelTest : SysuiTestCase() { val pixels = -100f val enterFromTopTranslationY by collectLastValue(underTest.enterFromTopTranslationY(pixels.toInt())) + runCurrent() // The animation should only start > .4f way through repository.sendTransitionStep(step(0f, TransitionState.STARTED)) @@ -72,6 +74,7 @@ class GoneToAodTransitionViewModelTest : SysuiTestCase() { fun enterFromTopAnimationAlpha() = testScope.runTest { val enterFromTopAnimationAlpha by collectLastValue(underTest.enterFromTopAnimationAlpha) + runCurrent() // The animation should only start > .4f way through repository.sendTransitionStep(step(0f, TransitionState.STARTED)) @@ -92,6 +95,7 @@ class GoneToAodTransitionViewModelTest : SysuiTestCase() { testScope.runTest { val deviceEntryBackgroundViewAlpha by collectLastValue(underTest.deviceEntryBackgroundViewAlpha) + runCurrent() // immediately 0f repository.sendTransitionStep(step(0f, TransitionState.STARTED)) @@ -113,6 +117,7 @@ class GoneToAodTransitionViewModelTest : SysuiTestCase() { fingerprintPropertyRepository.supportsUdfps() biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha) + runCurrent() // animation doesn't start until the end repository.sendTransitionStep(step(0f, TransitionState.STARTED)) @@ -137,6 +142,7 @@ class GoneToAodTransitionViewModelTest : SysuiTestCase() { fingerprintPropertyRepository.supportsRearFps() biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha) + runCurrent() // animation doesn't start until the end repository.sendTransitionStep(step(0f, TransitionState.STARTED)) @@ -161,6 +167,7 @@ class GoneToAodTransitionViewModelTest : SysuiTestCase() { fingerprintPropertyRepository.supportsUdfps() biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha) + runCurrent() // animation doesn't start until the end repository.sendTransitionStep(step(0f, TransitionState.STARTED)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt index 5e6231734d32..1912987cc447 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt @@ -30,6 +30,7 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @@ -51,6 +52,7 @@ class OccludedToAodTransitionViewModelTest : SysuiTestCase() { testScope.runTest { val deviceEntryBackgroundViewAlpha by collectLastValue(underTest.deviceEntryBackgroundViewAlpha) + runCurrent() // immediately 0f keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED)) @@ -72,6 +74,7 @@ class OccludedToAodTransitionViewModelTest : SysuiTestCase() { fingerprintPropertyRepository.supportsUdfps() biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha) + runCurrent() // immediately 1f keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED)) @@ -96,6 +99,7 @@ class OccludedToAodTransitionViewModelTest : SysuiTestCase() { fingerprintPropertyRepository.supportsRearFps() biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha) + runCurrent() // no updates keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED)) @@ -120,6 +124,7 @@ class OccludedToAodTransitionViewModelTest : SysuiTestCase() { fingerprintPropertyRepository.supportsUdfps() biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha) + runCurrent() // no updates keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt index 9729022ca890..c55c27c3b516 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt @@ -30,6 +30,7 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @@ -54,6 +55,7 @@ class PrimaryBouncerToAodTransitionViewModelTest : SysuiTestCase() { fingerprintPropertyRepository.supportsUdfps() val deviceEntryBackgroundViewAlpha by collectLastValue(underTest.deviceEntryBackgroundViewAlpha) + runCurrent() // immediately 0f keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED)) @@ -75,6 +77,7 @@ class PrimaryBouncerToAodTransitionViewModelTest : SysuiTestCase() { fingerprintPropertyRepository.supportsUdfps() biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha) + runCurrent() keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED)) @@ -92,6 +95,7 @@ class PrimaryBouncerToAodTransitionViewModelTest : SysuiTestCase() { fingerprintPropertyRepository.supportsRearFps() biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha) + runCurrent() // animation doesn't start until the end keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED)) @@ -116,6 +120,7 @@ class PrimaryBouncerToAodTransitionViewModelTest : SysuiTestCase() { fingerprintPropertyRepository.supportsUdfps() biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha) + runCurrent() keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED)) assertThat(deviceEntryParentViewAlpha).isNull() diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt index 2c6436e07b91..0796af065790 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt @@ -30,6 +30,7 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @@ -71,6 +72,7 @@ class PrimaryBouncerToLockscreenTransitionViewModelTest : SysuiTestCase() { testScope.runTest { fingerprintPropertyRepository.supportsUdfps() val bgViewAlpha by collectLastValue(underTest.deviceEntryBackgroundViewAlpha) + runCurrent() // immediately 1f keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index a20658197a8d..b2394820b2af 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -346,6 +346,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { @Mock protected ActiveNotificationsInteractor mActiveNotificationsInteractor; @Mock private KeyguardClockPositionAlgorithm mKeyguardClockPositionAlgorithm; @Mock private NaturalScrollingSettingObserver mNaturalScrollingSettingObserver; + @Mock private LargeScreenHeaderHelper mLargeScreenHeaderHelper; protected final int mMaxUdfpsBurnInOffsetY = 5; protected FakeFeatureFlagsClassic mFeatureFlags = new FakeFeatureFlagsClassic(); @@ -426,7 +427,8 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mContext, new ResourcesSplitShadeStateController(), mKeyguardInteractor, - deviceEntryUdfpsInteractor + deviceEntryUdfpsInteractor, + () -> mLargeScreenHeaderHelper ), mShadeRepository ) @@ -812,7 +814,8 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mActiveNotificationsInteractor, mJavaAdapter, mCastController, - new ResourcesSplitShadeStateController() + new ResourcesSplitShadeStateController(), + () -> mLargeScreenHeaderHelper ); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java index 9d8b21464585..437d00ac8723 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java @@ -143,6 +143,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { @Mock private SelectedUserInteractor mSelectedUserInteractor; @Mock private UserTracker mUserTracker; @Mock private SceneContainerFlags mSceneContainerFlags; + @Mock private LargeScreenHeaderHelper mLargeScreenHeaderHelper; @Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters; @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListener; @@ -261,7 +262,8 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { mContext, new ResourcesSplitShadeStateController(), keyguardInteractor, - deviceEntryUdfpsInteractor), + deviceEntryUdfpsInteractor, + () -> mLargeScreenHeaderHelper), shadeRepository ) ); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt index 88a47eb81bde..ea3caa380cf2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt @@ -42,6 +42,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.capture +import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat @@ -52,7 +53,6 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Captor -import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.RETURNS_DEEP_STUBS import org.mockito.Mockito.any @@ -74,15 +74,16 @@ import org.mockito.MockitoAnnotations @SmallTest class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() { - @Mock lateinit var view: NotificationsQuickSettingsContainer - @Mock lateinit var navigationModeController: NavigationModeController - @Mock lateinit var overviewProxyService: OverviewProxyService - @Mock lateinit var shadeHeaderController: ShadeHeaderController - @Mock lateinit var shadeInteractor: ShadeInteractor - @Mock lateinit var fragmentService: FragmentService - @Mock lateinit var fragmentHostManager: FragmentHostManager - @Mock - lateinit var notificationStackScrollLayoutController: NotificationStackScrollLayoutController + private val view = mock<NotificationsQuickSettingsContainer>() + private val navigationModeController = mock<NavigationModeController>() + private val overviewProxyService = mock<OverviewProxyService>() + private val shadeHeaderController = mock<ShadeHeaderController>() + private val shadeInteractor = mock<ShadeInteractor>() + private val fragmentService = mock<FragmentService>() + private val fragmentHostManager = mock<FragmentHostManager>() + private val notificationStackScrollLayoutController = + mock<NotificationStackScrollLayoutController>() + private val largeScreenHeaderHelper = mock<LargeScreenHeaderHelper>() @Captor lateinit var navigationModeCaptor: ArgumentCaptor<ModeChangedListener> @Captor lateinit var taskbarVisibilityCaptor: ArgumentCaptor<OverviewProxyListener> @@ -123,7 +124,8 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() { delayableExecutor, featureFlags, notificationStackScrollLayoutController, - ResourcesSplitShadeStateController() + ResourcesSplitShadeStateController(), + largeScreenHeaderHelperLazy = { largeScreenHeaderHelper } ) overrideResource(R.dimen.split_shade_notifications_scrim_margin_bottom, SCRIM_MARGIN) @@ -480,7 +482,8 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() { delayableExecutor, featureFlags, notificationStackScrollLayoutController, - ResourcesSplitShadeStateController() + ResourcesSplitShadeStateController(), + largeScreenHeaderHelperLazy = { largeScreenHeaderHelper } ) controller.updateConstraints() diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt index 1f37ca09dc26..c1bc303f26ea 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt @@ -43,6 +43,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.capture +import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat @@ -53,7 +54,6 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Captor -import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.RETURNS_DEEP_STUBS import org.mockito.Mockito.any @@ -71,15 +71,16 @@ import org.mockito.MockitoAnnotations @SmallTest class NotificationsQSContainerControllerTest : SysuiTestCase() { - @Mock lateinit var view: NotificationsQuickSettingsContainer - @Mock lateinit var navigationModeController: NavigationModeController - @Mock lateinit var overviewProxyService: OverviewProxyService - @Mock lateinit var shadeHeaderController: ShadeHeaderController - @Mock lateinit var shadeInteractor: ShadeInteractor - @Mock lateinit var fragmentService: FragmentService - @Mock lateinit var fragmentHostManager: FragmentHostManager - @Mock - lateinit var notificationStackScrollLayoutController: NotificationStackScrollLayoutController + private val view = mock<NotificationsQuickSettingsContainer>() + private val navigationModeController = mock<NavigationModeController>() + private val overviewProxyService = mock<OverviewProxyService>() + private val shadeHeaderController = mock<ShadeHeaderController>() + private val shadeInteractor = mock<ShadeInteractor>() + private val fragmentService = mock<FragmentService>() + private val fragmentHostManager = mock<FragmentHostManager>() + private val notificationStackScrollLayoutController = + mock<NotificationStackScrollLayoutController>() + private val largeScreenHeaderHelper = mock<LargeScreenHeaderHelper>() @Captor lateinit var navigationModeCaptor: ArgumentCaptor<ModeChangedListener> @Captor lateinit var taskbarVisibilityCaptor: ArgumentCaptor<OverviewProxyListener> @@ -122,7 +123,8 @@ class NotificationsQSContainerControllerTest : SysuiTestCase() { delayableExecutor, featureFlags, notificationStackScrollLayoutController, - ResourcesSplitShadeStateController() + ResourcesSplitShadeStateController(), + largeScreenHeaderHelperLazy = { largeScreenHeaderHelper } ) overrideResource(R.dimen.split_shade_notifications_scrim_margin_bottom, SCRIM_MARGIN) @@ -463,7 +465,8 @@ class NotificationsQSContainerControllerTest : SysuiTestCase() { delayableExecutor, featureFlags, notificationStackScrollLayoutController, - ResourcesSplitShadeStateController() + ResourcesSplitShadeStateController(), + largeScreenHeaderHelperLazy = { largeScreenHeaderHelper } ) controller.updateConstraints() diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java index 727a6c3d1adc..39051eba3ad9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java @@ -176,6 +176,7 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { @Mock protected CastController mCastController; @Mock protected UserSwitcherInteractor mUserSwitcherInteractor; @Mock protected SelectedUserInteractor mSelectedUserInteractor; + @Mock protected LargeScreenHeaderHelper mLargeScreenHeaderHelper; protected FakeDisableFlagsRepository mDisableFlagsRepository = new FakeDisableFlagsRepository(); @@ -299,7 +300,8 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { mContext, splitShadeStateController, keyguardInteractor, - deviceEntryUdfpsInteractor), + deviceEntryUdfpsInteractor, + () -> mLargeScreenHeaderHelper), mShadeRepository ) ); @@ -384,7 +386,8 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { mActiveNotificationsInteractor, new JavaAdapter(mTestScope.getBackgroundScope()), mCastController, - splitShadeStateController + splitShadeStateController, + () -> mLargeScreenHeaderHelper ); mQsController.init(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt index ee27c5c9ba0d..64fd80d72d3f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt @@ -35,6 +35,7 @@ import com.android.systemui.plugins.PluginListener import com.android.systemui.plugins.PluginManager import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.eq +import java.util.function.BiConsumer import junit.framework.Assert.assertEquals import junit.framework.Assert.fail import kotlinx.coroutines.CoroutineDispatcher @@ -100,10 +101,7 @@ class ClockRegistryTest : SysuiTestCase() { override fun toString() = "Manager[$tag]" override fun getPackage(): String = mComponentName.getPackageName() override fun getComponentName(): ComponentName = mComponentName - - private var isDebug: Boolean = false - override fun getIsDebug(): Boolean = isDebug - override fun setIsDebug(value: Boolean) { isDebug = value } + override fun setLogFunc(func: BiConsumer<String, String>) { } override fun loadPlugin() { if (!mIsLoaded) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java index bc50c25a77a0..3defee9e6eb5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java @@ -112,7 +112,7 @@ public class PluginInstanceTest extends SysuiTestCase { mPluginInstance = mPluginInstanceFactory.create( mContext, mAppInfo, TEST_PLUGIN_COMPONENT_NAME, TestPlugin.class, mPluginListener); - mPluginInstance.setIsDebug(true); + mPluginInstance.setLogFunc((tag, msg) -> Log.d((String) tag, (String) msg)); mPluginContext = new WeakReference<>(mPluginInstance.getPluginContext()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt index f25ce0aa5278..aed616349eb3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt @@ -27,7 +27,7 @@ import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepositor import com.android.systemui.classifier.FalsingCollectorFake import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor -import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor; +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.FakeCommandQueue import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository @@ -44,6 +44,7 @@ import com.android.systemui.power.data.repository.FakePowerRepository import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags +import com.android.systemui.shade.LargeScreenHeaderHelper import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.domain.interactor.ShadeInteractorImpl @@ -65,7 +66,6 @@ import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.anyFloat import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.eq -import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.mock import org.mockito.Mockito.verify @@ -83,9 +83,10 @@ class StatusBarStateControllerImplTest : SysuiTestCase() { private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor private lateinit var fromPrimaryBouncerTransitionInteractor: FromPrimaryBouncerTransitionInteractor - @Mock lateinit var interactionJankMonitor: InteractionJankMonitor - @Mock lateinit var mockDarkAnimator: ObjectAnimator - @Mock lateinit var deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor + private val interactionJankMonitor = mock<InteractionJankMonitor>() + private val mockDarkAnimator = mock<ObjectAnimator>() + private val deviceEntryUdfpsInteractor = mock<DeviceEntryUdfpsInteractor>() + private val largeScreenHeaderHelper = mock<LargeScreenHeaderHelper>() private lateinit var controller: StatusBarStateControllerImpl private lateinit var uiEventLogger: UiEventLoggerFake @@ -189,6 +190,7 @@ class StatusBarStateControllerImplTest : SysuiTestCase() { ResourcesSplitShadeStateController(), keyguardInteractor, deviceEntryUdfpsInteractor, + largeScreenHeaderHelperLazy = { largeScreenHeaderHelper } ), shadeRepository, ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java index 3556703a2fa8..4dc4798caa8c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java @@ -84,7 +84,7 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { LogBuffer logBuffer = FakeLogBuffer.Factory.Companion.create(); mClockPositionAlgorithm = new KeyguardClockPositionAlgorithm(logBuffer); when(mResources.getDimensionPixelSize(anyInt())).thenReturn(0); - mClockPositionAlgorithm.loadDimens(mResources); + mClockPositionAlgorithm.loadDimens(mContext, mResources); mClockPosition = new KeyguardClockPositionAlgorithm.Result(); } @@ -297,7 +297,7 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { .thenReturn(100); when(mResources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height)) .thenReturn(70); - mClockPositionAlgorithm.loadDimens(mResources); + mClockPositionAlgorithm.loadDimens(mContext, mResources); givenLockScreen(); mIsSplitShade = true; // WHEN the position algorithm is run @@ -589,7 +589,7 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { private void setSplitShadeTopMargin(int value) { when(mResources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin)) .thenReturn(value); - mClockPositionAlgorithm.loadDimens(mResources); + mClockPositionAlgorithm.loadDimens(mContext, mResources); } private void givenHighestBurnInOffset() { @@ -603,7 +603,7 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { private void givenMaxBurnInOffset(int offset) { when(mResources.getDimensionPixelSize(R.dimen.burn_in_prevention_offset_y_clock)) .thenReturn(offset); - mClockPositionAlgorithm.loadDimens(mResources); + mClockPositionAlgorithm.loadDimens(mContext, mResources); } private void givenAOD() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt index a906a8953e02..02e6fd5a9d6e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt @@ -31,7 +31,7 @@ import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_OFF import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN import android.telephony.satellite.SatelliteManager.SatelliteException -import android.telephony.satellite.SatelliteStateCallback +import android.telephony.satellite.SatelliteModemStateCallback import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -106,7 +106,7 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() { val latest by collectLastValue(underTest.connectionState) runCurrent() val callback = - withArgCaptor<SatelliteStateCallback> { + withArgCaptor<SatelliteModemStateCallback> { verify(satelliteManager).registerForSatelliteModemStateChanged(any(), capture()) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java index 1dab84eb6e6a..cb6ce68aaf80 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.policy; import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED; +import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -214,7 +215,8 @@ public class SecurityControllerTest extends SysuiTestCase { public void testNetworkRequest() { verify(mConnectivityManager, times(1)).registerNetworkCallback(argThat( (NetworkRequest request) -> - request.equals(new NetworkRequest.Builder().clearCapabilities().build()) + request.equals(new NetworkRequest.Builder() + .clearCapabilities().addTransportType(TRANSPORT_VPN).build()) ), any(NetworkCallback.class)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt index 6714c94b017c..fb5375a1ab83 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt @@ -32,6 +32,7 @@ import androidx.test.filters.SmallTest import com.android.internal.logging.UiEventLogger import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback +import com.android.systemui.Flags as AConfigFlags import com.android.systemui.GuestResetOrExitSessionReceiver import com.android.systemui.GuestResumeSessionReceiver import com.android.systemui.SysuiTestCase @@ -121,6 +122,7 @@ class UserSwitcherInteractorTest : SysuiTestCase() { ) utils.featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, false) + mSetFlagsRule.enableFlags(AConfigFlags.FLAG_SWITCH_USER_ON_BG) spyContext = spy(context) keyguardReply = KeyguardInteractorFactory.create(featureFlags = utils.featureFlags) keyguardRepository = keyguardReply.repository @@ -172,6 +174,7 @@ class UserSwitcherInteractorTest : SysuiTestCase() { userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true)) underTest.onRecordSelected(UserRecord(info = userInfos[1]), dialogShower) + runCurrent() verify(uiEventLogger, times(1)) .log(MultiUserActionsEvent.SWITCH_TO_USER_FROM_USER_SWITCHER) @@ -191,6 +194,7 @@ class UserSwitcherInteractorTest : SysuiTestCase() { userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true)) underTest.onRecordSelected(UserRecord(info = userInfos.last())) + runCurrent() verify(uiEventLogger, times(1)) .log(MultiUserActionsEvent.SWITCH_TO_GUEST_FROM_USER_SWITCHER) @@ -218,6 +222,7 @@ class UserSwitcherInteractorTest : SysuiTestCase() { userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true)) underTest.onRecordSelected(UserRecord(info = userInfos.last())) + runCurrent() verify(uiEventLogger, times(1)) .log(MultiUserActionsEvent.SWITCH_TO_RESTRICTED_USER_FROM_USER_SWITCHER) diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 814ea19a7dcb..b7529a82dd3d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -126,6 +126,7 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlags; import com.android.systemui.scene.shared.logger.SceneLogger; import com.android.systemui.settings.FakeDisplayTracker; import com.android.systemui.settings.UserTracker; +import com.android.systemui.shade.LargeScreenHeaderHelper; import com.android.systemui.shade.NotificationShadeWindowControllerImpl; import com.android.systemui.shade.NotificationShadeWindowView; import com.android.systemui.shade.ShadeController; @@ -349,6 +350,8 @@ public class BubblesTest extends SysuiTestCase { private Display mDefaultDisplay; @Mock private SceneContainerFlags mSceneContainerFlags; + @Mock + private LargeScreenHeaderHelper mLargeScreenHeaderHelper; private final SceneTestUtils mUtils = new SceneTestUtils(this); private final TestScope mTestScope = mUtils.getTestScope(); @@ -490,7 +493,8 @@ public class BubblesTest extends SysuiTestCase { mContext, splitShadeStateController, keyguardInteractor, - deviceEntryUdfpsInteractor), + deviceEntryUdfpsInteractor, + () -> mLargeScreenHeaderHelper), shadeRepository ) ); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt index eb287ee522c0..95ff889177b8 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt @@ -35,7 +35,8 @@ object CommunalInteractorFactory { @JvmStatic fun create( testScope: TestScope = TestScope(), - communalRepository: FakeCommunalRepository = FakeCommunalRepository(), + communalRepository: FakeCommunalRepository = + FakeCommunalRepository(testScope.backgroundScope), widgetRepository: FakeCommunalWidgetRepository = FakeCommunalWidgetRepository(testScope.backgroundScope), mediaRepository: FakeCommunalMediaRepository = FakeCommunalMediaRepository(), @@ -51,6 +52,7 @@ object CommunalInteractorFactory { communalRepository = communalRepository, ) return WithDependencies( + testScope, communalRepository, widgetRepository, mediaRepository, @@ -74,6 +76,7 @@ object CommunalInteractorFactory { } data class WithDependencies( + val testScope: TestScope, val communalRepository: FakeCommunalRepository, val widgetRepository: FakeCommunalWidgetRepository, val mediaRepository: FakeCommunalMediaRepository, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt index a94ca291298d..0c1dbfebfb34 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt @@ -147,7 +147,6 @@ class FakeKeyguardTransitionRepository @Inject constructor() : KeyguardTransitio ) } } - _transitions.emit(step) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowKosmos.kt index 8d6529a114b8..dad1887cbd85 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowKosmos.kt @@ -19,6 +19,7 @@ package com.android.systemui.keyguard.ui import com.android.keyguard.logging.keyguardTransitionAnimationLogger +import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.kosmos.applicationCoroutineScope @@ -27,6 +28,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.keyguardTransitionAnimationFlow by Fixture { KeyguardTransitionAnimationFlow( scope = applicationCoroutineScope, + transitionInteractor = keyguardTransitionInteractor, logger = keyguardTransitionAnimationLogger, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelKosmos.kt index d9c6e4f1f605..3ed9392bab2a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelKosmos.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -27,7 +26,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.alternateBouncerToAodTransitionViewModel by Fixture { AlternateBouncerToAodTransitionViewModel( - interactor = keyguardTransitionInteractor, deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor, animationFlow = keyguardTransitionAnimationFlow, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelKosmos.kt index e4821b04fcef..c909dd6ffdd5 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelKosmos.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -26,7 +25,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.alternateBouncerToGoneTransitionViewModel by Fixture { AlternateBouncerToGoneTransitionViewModel( - interactor = keyguardTransitionInteractor, bouncerToGoneFlows = bouncerToGoneFlows, animationFlow = keyguardTransitionAnimationFlow, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt index 9f0466dda51e..b4f1218617a2 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -28,7 +27,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.alternateBouncerViewModel by Fixture { AlternateBouncerViewModel( statusBarKeyguardViewManager = statusBarKeyguardViewManager, - transitionInteractor = keyguardTransitionInteractor, animationFlow = keyguardTransitionAnimationFlow, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelKosmos.kt index 44e542660971..b6f278c1b466 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelKosmos.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -26,7 +25,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.aodToGoneTransitionViewModel by Fixture { AodToGoneTransitionViewModel( - interactor = keyguardTransitionInteractor, animationFlow = keyguardTransitionAnimationFlow, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt index b5a5f039200f..733340c67e55 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -27,7 +26,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.aodToLockscreenTransitionViewModel by Fixture { AodToLockscreenTransitionViewModel( - interactor = keyguardTransitionInteractor, deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor, animationFlow = keyguardTransitionAnimationFlow, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelKosmos.kt index 27ad0f0f01e3..8d066fc05996 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelKosmos.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -26,7 +25,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.aodToOccludedTransitionViewModel by Fixture { AodToOccludedTransitionViewModel( - interactor = keyguardTransitionInteractor, animationFlow = keyguardTransitionAnimationFlow, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt index 6ffcc9a03b05..c71c1c3ea5f0 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt @@ -20,7 +20,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor import com.android.systemui.flags.featureFlagsClassic -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -31,7 +30,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.bouncerToGoneFlows by Fixture { BouncerToGoneFlows( - interactor = keyguardTransitionInteractor, statusBarStateController = sysuiStatusBarStateController, primaryBouncerInteractor = primaryBouncerInteractor, keyguardDismissActionInteractor = mock(), diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelKosmos.kt new file mode 100644 index 000000000000..400a0d87f041 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelKosmos.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.keyguard.ui.viewmodel + +import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.Kosmos.Fixture +import kotlinx.coroutines.ExperimentalCoroutinesApi + +val Kosmos.dozingToLockscreenTransitionViewModel by Fixture { + DozingToLockscreenTransitionViewModel( + animationFlow = keyguardTransitionAnimationFlow, + ) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt index 00ece1482236..19e4241c62fa 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -27,7 +26,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi var Kosmos.goneToAodTransitionViewModel by Fixture { GoneToAodTransitionViewModel( - interactor = keyguardTransitionInteractor, deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor, animationFlow = keyguardTransitionAnimationFlow, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelKosmos.kt index 073b34bcf277..b267a962a1ff 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelKosmos.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -26,7 +25,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.goneToDreamingTransitionViewModel by Fixture { GoneToDreamingTransitionViewModel( - interactor = keyguardTransitionInteractor, animationFlow = keyguardTransitionAnimationFlow, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt index 7865f71ead83..07b4cd4f3251 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -27,7 +26,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.lockscreenToAodTransitionViewModel by Fixture { LockscreenToAodTransitionViewModel( - interactor = keyguardTransitionInteractor, deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor, shadeDependentFlows = shadeDependentFlows, animationFlow = keyguardTransitionAnimationFlow, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelKosmos.kt index b9f4b71d24d6..56d5ff6e30eb 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelKosmos.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -26,7 +25,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.lockscreenToDreamingTransitionViewModel by Fixture { LockscreenToDreamingTransitionViewModel( - interactor = keyguardTransitionInteractor, shadeDependentFlows = shadeDependentFlows, animationFlow = keyguardTransitionAnimationFlow, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelKosmos.kt index 475aa2de3f9b..1b2337fedf6a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelKosmos.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -26,7 +25,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.lockscreenToGoneTransitionViewModel by Fixture { LockscreenToGoneTransitionViewModel( - interactor = keyguardTransitionInteractor, animationFlow = keyguardTransitionAnimationFlow, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelKosmos.kt index 8541a4fe7096..9953d39e9a49 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelKosmos.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.common.ui.domain.interactor.configurationInteractor -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -27,7 +26,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.lockscreenToOccludedTransitionViewModel by Fixture { LockscreenToOccludedTransitionViewModel( - interactor = keyguardTransitionInteractor, shadeDependentFlows = shadeDependentFlows, configurationInteractor = configurationInteractor, animationFlow = keyguardTransitionAnimationFlow, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelKosmos.kt index 65c47fc9c2c7..f094f22c9f50 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelKosmos.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -26,7 +25,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.lockscreenToPrimaryBouncerTransitionViewModel by Fixture { LockscreenToPrimaryBouncerTransitionViewModel( - interactor = keyguardTransitionInteractor, shadeDependentFlows = shadeDependentFlows, animationFlow = keyguardTransitionAnimationFlow, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelKosmos.kt index ddde5498d544..b7867b6cabde 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelKosmos.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -27,7 +26,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.occludedToAodTransitionViewModel by Fixture { OccludedToAodTransitionViewModel( - interactor = keyguardTransitionInteractor, deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor, animationFlow = keyguardTransitionAnimationFlow, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelKosmos.kt index 93ecb7968ee2..e6651a44236f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelKosmos.kt @@ -20,7 +20,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.common.ui.domain.interactor.configurationInteractor import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -28,7 +27,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi var Kosmos.occludedToLockscreenTransitionViewModel by Fixture { OccludedToLockscreenTransitionViewModel( - interactor = keyguardTransitionInteractor, deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor, configurationInteractor = configurationInteractor, animationFlow = keyguardTransitionAnimationFlow, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelKosmos.kt index a7f29d637281..8d887309f576 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelKosmos.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -27,7 +26,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.primaryBouncerToAodTransitionViewModel by Fixture { PrimaryBouncerToAodTransitionViewModel( - interactor = keyguardTransitionInteractor, deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor, animationFlow = keyguardTransitionAnimationFlow, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelKosmos.kt index ace6ae3e3eec..ab28d0d670ef 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelKosmos.kt @@ -20,7 +20,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor import com.android.systemui.flags.featureFlagsClassic -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -30,7 +29,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.primaryBouncerToGoneTransitionViewModel by Fixture { PrimaryBouncerToGoneTransitionViewModel( - interactor = keyguardTransitionInteractor, statusBarStateController = sysuiStatusBarStateController, primaryBouncerInteractor = primaryBouncerInteractor, keyguardDismissActionInteractor = mock(), diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelKosmos.kt index 3bbabf713b91..85662512a5ee 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelKosmos.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -27,7 +26,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.primaryBouncerToLockscreenTransitionViewModel by Fixture { PrimaryBouncerToLockscreenTransitionViewModel( - interactor = keyguardTransitionInteractor, deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor, animationFlow = keyguardTransitionAnimationFlow, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/LargeScreenHeaderHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/LargeScreenHeaderHelperKosmos.kt new file mode 100644 index 000000000000..d3e757469a99 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/LargeScreenHeaderHelperKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.shade + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.Kosmos.Fixture +import com.android.systemui.util.mockito.mock + +var Kosmos.largeScreenHeaderHelper by Fixture { mockLargeScreenHeaderHelper } +val Kosmos.mockLargeScreenHeaderHelper by Fixture { mock<LargeScreenHeaderHelper>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt index 13d577bde711..8909d751227a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt @@ -21,6 +21,7 @@ import com.android.systemui.common.ui.data.repository.configurationRepository import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.kosmos.Kosmos +import com.android.systemui.shade.largeScreenHeaderHelper import com.android.systemui.statusbar.policy.splitShadeStateController val Kosmos.sharedNotificationContainerInteractor by @@ -31,5 +32,6 @@ val Kosmos.sharedNotificationContainerInteractor by splitShadeStateController = splitShadeStateController, keyguardInteractor = keyguardInteractor, deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor, + largeScreenHeaderHelperLazy = { largeScreenHeaderHelper } ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSecurityController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSecurityController.kt index 021e7dff9120..ac90a45450d0 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSecurityController.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSecurityController.kt @@ -77,6 +77,8 @@ class FakeSecurityController( override fun isVpnBranded(): Boolean = fakeState.isVpnBranded + override fun isVpnValidated(): Boolean = fakeState.isVpnValidated + override fun getPrimaryVpnName(): String? = fakeState.primaryVpnName override fun getWorkProfileVpnName(): String? = fakeState.workProfileVpnName @@ -110,6 +112,7 @@ class FakeSecurityController( var isVpnEnabled: Boolean = false, var isVpnRestricted: Boolean = false, var isVpnBranded: Boolean = false, + var isVpnValidated: Boolean = false, var primaryVpnName: String? = null, var workProfileVpnName: String? = null, var hasCACertInCurrentUser: Boolean = false, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeSecurityController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeSecurityController.java index 76199e3168da..791165d97795 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeSecurityController.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeSecurityController.java @@ -109,6 +109,11 @@ public class FakeSecurityController extends BaseLeakChecker<SecurityControllerCa } @Override + public boolean isVpnValidated() { + return false; + } + + @Override public String getPrimaryVpnName() { return null; } diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java index 1fc74f704f62..67a7d121ea17 100644 --- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java +++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java @@ -16,6 +16,8 @@ package com.android.vpndialogs; +import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; + import android.content.DialogInterface; import android.net.VpnManager; import android.os.Bundle; @@ -87,6 +89,7 @@ public class ManageDialog extends AlertActivity implements mAlertParams.mNegativeButtonListener = this; mAlertParams.mView = view; setupAlert(); + getWindow().addPrivateFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); if (mHandler == null) { mHandler = new Handler(this); diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto index b403a7fe8f12..7f542d130cf7 100644 --- a/proto/src/system_messages.proto +++ b/proto/src/system_messages.proto @@ -408,5 +408,9 @@ message SystemMessage { // Notify the user about external display events related to screenshot. // Package: com.android.systemui NOTE_GLOBAL_SCREENSHOT_EXTERNAL_DISPLAY = 1008; + + // Notify the user that accessibility floating menu is hidden. + // Package: com.android.systemui + NOTE_A11Y_FLOATING_MENU_HIDDEN = 1009; } } diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java index 513c09587026..d96787480cfa 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java @@ -142,6 +142,11 @@ public class RavenwoodRule implements TestRule { Assume.assumeFalse(IS_UNDER_RAVENWOOD); } + // Stopgap for http://g/ravenwood/EPAD-N5ntxM + if (description.getMethodName().endsWith("$noRavenwood")) { + Assume.assumeFalse(IS_UNDER_RAVENWOOD); + } + RavenwoodRuleImpl.init(RavenwoodRule.this); try { base.evaluate(); diff --git a/ravenwood/minimum-test/test/com/android/ravenwood/RavenwoodMinimumTest.java b/ravenwood/minimum-test/test/com/android/ravenwood/RavenwoodMinimumTest.java index 085c18622885..7abfecf0e424 100644 --- a/ravenwood/minimum-test/test/com/android/ravenwood/RavenwoodMinimumTest.java +++ b/ravenwood/minimum-test/test/com/android/ravenwood/RavenwoodMinimumTest.java @@ -42,4 +42,9 @@ public class RavenwoodMinimumTest { public void testIgnored() { throw new RuntimeException("Shouldn't be executed under ravenwood"); } + + @Test + public void testIgnored$noRavenwood() { + throw new RuntimeException("Shouldn't be executed under ravenwood"); + } } diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt index 491ed22ce2c8..ab2546bab246 100644 --- a/ravenwood/ravenwood-annotation-allowed-classes.txt +++ b/ravenwood/ravenwood-annotation-allowed-classes.txt @@ -9,8 +9,10 @@ com.android.internal.os.Clock com.android.internal.os.LongArrayMultiStateCounter com.android.internal.os.LongArrayMultiStateCounter$LongArrayContainer com.android.internal.os.MonotonicClock +com.android.internal.os.PowerProfile com.android.internal.os.PowerStats com.android.internal.os.PowerStats$Descriptor +com.android.internal.power.ModemPowerProfile android.util.AtomicFile android.util.DataUnit @@ -32,11 +34,16 @@ android.util.SparseSetArray android.util.TimeUtils android.util.Xml +android.os.AggregateBatteryConsumer android.os.BatteryConsumer +android.os.BatteryStats android.os.BatteryStats$HistoryItem android.os.BatteryStats$HistoryStepDetails android.os.BatteryStats$HistoryTag +android.os.BatteryStats$LongCounter android.os.BatteryStats$ProcessStateChange +android.os.BatteryUsageStats +android.os.BatteryUsageStatsQuery android.os.Binder android.os.Binder$IdentitySupplier android.os.Broadcaster @@ -54,11 +61,14 @@ android.os.MessageQueue android.os.PackageTagsList android.os.Parcel android.os.Parcelable +android.os.PowerComponents android.os.Process android.os.ServiceSpecificException android.os.SystemClock android.os.ThreadLocalWorkSource android.os.TimestampedValue +android.os.UidBatteryConsumer +android.os.UidBatteryConsumer$Builder android.os.UserHandle android.os.WorkSource @@ -117,6 +127,7 @@ android.graphics.RectF android.content.ContentProvider com.android.server.LocalServices +com.android.server.power.stats.BatteryStatsImpl com.android.internal.util.BitUtils com.android.internal.util.BitwiseInputStream diff --git a/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java b/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java index 553ba124402c..7fc1738f3172 100644 --- a/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java +++ b/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java @@ -16,6 +16,7 @@ package com.android.server.autofill; +import static com.android.server.autofill.Session.REQUEST_ID_KEY; import static com.android.server.autofill.Session.SESSION_ID_KEY; import android.annotation.NonNull; @@ -107,15 +108,16 @@ final class SecondaryProviderHandler implements RemoteFillService.FillServiceCal mLastFlag = flag; if (mRemoteFillService != null && mRemoteFillService.isCredentialAutofillService()) { Slog.v(TAG, "About to call CredAutofill service as secondary provider"); - addSessionIdToClientState(pendingFillRequest, pendingInlineSuggestionsRequest, id); + addSessionIdAndRequestIdToClientState(pendingFillRequest, + pendingInlineSuggestionsRequest, id); mRemoteFillService.onFillCredentialRequest(pendingFillRequest, client); } else { mRemoteFillService.onFillRequest(pendingFillRequest); } } - private FillRequest addSessionIdToClientState(FillRequest pendingFillRequest, - InlineSuggestionsRequest pendingInlineSuggestionsRequest, int id) { + private FillRequest addSessionIdAndRequestIdToClientState(FillRequest pendingFillRequest, + InlineSuggestionsRequest pendingInlineSuggestionsRequest, int sessionId) { if (pendingFillRequest.getClientState() == null) { pendingFillRequest = new FillRequest(pendingFillRequest.getId(), pendingFillRequest.getFillContexts(), @@ -125,7 +127,8 @@ final class SecondaryProviderHandler implements RemoteFillService.FillServiceCal pendingInlineSuggestionsRequest, pendingFillRequest.getDelayedFillIntentSender()); } - pendingFillRequest.getClientState().putInt(SESSION_ID_KEY, id); + pendingFillRequest.getClientState().putInt(SESSION_ID_KEY, sessionId); + pendingFillRequest.getClientState().putInt(REQUEST_ID_KEY, pendingFillRequest.getId()); return pendingFillRequest; } diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 007be05ff929..6a81425c1443 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -234,7 +234,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState new ComponentName("com.android.credentialmanager", "com.android.credentialmanager.autofill.CredentialAutofillService"); - static final String SESSION_ID_KEY = "session_id"; + static final String SESSION_ID_KEY = "autofill_session_id"; + static final String REQUEST_ID_KEY = "autofill_request_id"; final Object mLock; @@ -729,7 +730,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mPendingFillRequest.getFlags(), id, mClient); } else if (mRemoteFillService != null) { if (mIsPrimaryCredential) { - mPendingFillRequest = addSessionIdToClientState(mPendingFillRequest, + mPendingFillRequest = addSessionIdAndRequestIdToClientState(mPendingFillRequest, mPendingInlineSuggestionsRequest, id); mRemoteFillService.onFillCredentialRequest(mPendingFillRequest, mClient); } else { @@ -877,8 +878,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - private FillRequest addSessionIdToClientState(FillRequest pendingFillRequest, - InlineSuggestionsRequest pendingInlineSuggestionsRequest, int id) { + private FillRequest addSessionIdAndRequestIdToClientState(FillRequest pendingFillRequest, + InlineSuggestionsRequest pendingInlineSuggestionsRequest, int sessionId) { if (pendingFillRequest.getClientState() == null) { pendingFillRequest = new FillRequest(pendingFillRequest.getId(), pendingFillRequest.getFillContexts(), @@ -888,7 +889,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState pendingInlineSuggestionsRequest, pendingFillRequest.getDelayedFillIntentSender()); } - pendingFillRequest.getClientState().putInt(SESSION_ID_KEY, id); + pendingFillRequest.getClientState().putInt(SESSION_ID_KEY, sessionId); + pendingFillRequest.getClientState().putInt(REQUEST_ID_KEY, pendingFillRequest.getId()); return pendingFillRequest; } diff --git a/services/backup/flags.aconfig b/services/backup/flags.aconfig index d695d36db0ea..549fa36597b7 100644 --- a/services/backup/flags.aconfig +++ b/services/backup/flags.aconfig @@ -7,4 +7,12 @@ flag { "restore for apps that have been launched." bug: "308401499" is_fixed_read_only: true +} + +flag { + name: "enable_max_size_writes_to_pipes" + namespace: "onboarding" + description: "Enables the write buffer to pipes to be of maximum size." + bug: "265976737" + is_fixed_read_only: true }
\ No newline at end of file diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java index 6aed9aa15860..cca166b0939c 100644 --- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java +++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java @@ -40,8 +40,8 @@ import android.util.Slog; import com.android.server.EventLogTags; import com.android.server.backup.BackupAgentTimeoutParameters; -import com.android.server.backup.BackupAndRestoreFeatureFlags; import com.android.server.backup.BackupRestoreTask; +import com.android.server.backup.Flags; import com.android.server.backup.FullBackupJob; import com.android.server.backup.OperationStorage; import com.android.server.backup.OperationStorage.OpState; @@ -390,8 +390,11 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba // Set up to send data to the transport final int N = mPackages.size(); - final int chunkSizeInBytes = - BackupAndRestoreFeatureFlags.getFullBackupWriteToTransportBufferSizeBytes(); + int chunkSizeInBytes = 8 * 1024; // 8KB + if (Flags.enableMaxSizeWritesToPipes()) { + // Linux pipe capacity (buffer size) is 16 pages where each page is 4KB + chunkSizeInBytes = 64 * 1024; // 64KB + } final byte[] buffer = new byte[chunkSizeInBytes]; for (int i = 0; i < N; i++) { mBackupRunner = null; diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java index ff72476d4bf1..2c9eb51972af 100644 --- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java +++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java @@ -29,7 +29,6 @@ import android.app.ApplicationThreadConstants; import android.app.IBackupAgent; import android.app.backup.BackupAgent; import android.app.backup.BackupAnnotations; -import android.app.backup.BackupManager; import android.app.backup.FullBackup; import android.app.backup.IBackupManagerMonitor; import android.app.backup.IFullBackupRestoreObserver; @@ -51,6 +50,7 @@ import com.android.server.LocalServices; import com.android.server.backup.BackupAgentTimeoutParameters; import com.android.server.backup.BackupRestoreTask; import com.android.server.backup.FileMetadata; +import com.android.server.backup.Flags; import com.android.server.backup.KeyValueAdbRestoreEngine; import com.android.server.backup.OperationStorage; import com.android.server.backup.OperationStorage.OpType; @@ -157,13 +157,19 @@ public class FullRestoreEngine extends RestoreEngine { mMonitor = monitor; mOnlyPackage = onlyPackage; mAllowApks = allowApks; - mBuffer = new byte[32 * 1024]; mAgentTimeoutParameters = Objects.requireNonNull( backupManagerService.getAgentTimeoutParameters(), "Timeout parameters cannot be null"); mIsAdbRestore = isAdbRestore; mUserId = backupManagerService.getUserId(); mBackupEligibilityRules = backupEligibilityRules; + + if (Flags.enableMaxSizeWritesToPipes()) { + // Linux pipe capacity (buffer size) is 16 pages where each page is 4KB + mBuffer = new byte[64 * 1024]; // 64KB + } else { + mBuffer = new byte[32 * 1024]; + } } @VisibleForTesting diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java index 316a16d9934c..2fbc3cd24d65 100644 --- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java +++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java @@ -968,7 +968,12 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { throws Exception { Set<String> excludedKeysForPackage = getExcludedKeysForPackage(packageName); - byte[] buffer = new byte[8192]; // will grow when needed + int bufferSize = 8192; // 8KB + if (Flags.enableMaxSizeWritesToPipes()) { + // Linux pipe capacity (buffer size) is 16 pages where each page is 4KB + bufferSize = 64 * 1024; // 64KB + } + byte[] buffer = new byte[bufferSize]; // will grow when needed while (in.readNextHeader()) { final String key = in.getKey(); final int size = in.getDataSize(); @@ -1116,7 +1121,11 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { ParcelFileDescriptor tReadEnd = mTransportPipes[0]; ParcelFileDescriptor tWriteEnd = mTransportPipes[1]; - int bufferSize = 32 * 1024; + int bufferSize = 32 * 1024; // 32KB + if (Flags.enableMaxSizeWritesToPipes()) { + // Linux pipe capacity (buffer size) is 16 pages where each page is 4KB + bufferSize = 64 * 1024; // 64KB + } byte[] buffer = new byte[bufferSize]; FileOutputStream engineOut = new FileOutputStream(eWriteEnd.getFileDescriptor()); FileInputStream transportIn = new FileInputStream(tReadEnd.getFileDescriptor()); diff --git a/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java b/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java index 1c0cd87cec6f..843354e719bb 100644 --- a/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java +++ b/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java @@ -21,7 +21,7 @@ import static com.android.server.backup.BackupManagerService.TAG; import android.os.ParcelFileDescriptor; import android.util.Slog; -import com.android.server.backup.BackupAndRestoreFeatureFlags; +import com.android.server.backup.Flags; import java.io.DataInputStream; import java.io.EOFException; @@ -46,8 +46,11 @@ public class FullBackupUtils { // We do not take close() responsibility for the pipe FD FileInputStream raw = new FileInputStream(inPipe.getFileDescriptor()); DataInputStream in = new DataInputStream(raw); - final int chunkSizeInBytes = - BackupAndRestoreFeatureFlags.getFullBackupUtilsRouteBufferSizeBytes(); + int chunkSizeInBytes = 32 * 1024; // 32KB + if (Flags.enableMaxSizeWritesToPipes()) { + // Linux pipe capacity (buffer size) is 16 pages where each page is 4KB + chunkSizeInBytes = 64 * 1024; // 64KB + } byte[] buffer = new byte[chunkSizeInBytes]; int chunkTotal; while ((chunkTotal = in.readInt()) > 0) { diff --git a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java index 0accb9fadd04..5a8533a2daee 100644 --- a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java +++ b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java @@ -40,6 +40,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.server.LocalServices; import com.android.server.backup.FileMetadata; +import com.android.server.backup.Flags; import com.android.server.backup.restore.RestoreDeleteObserver; import com.android.server.backup.restore.RestorePolicy; @@ -93,7 +94,12 @@ public class RestoreUtils { try (Session session = installer.openSession(sessionId)) { try (OutputStream apkStream = session.openWrite(info.packageName, 0, info.size)) { - byte[] buffer = new byte[32 * 1024]; + int bufferSize = 32 * 1024; // 32KB + if (Flags.enableMaxSizeWritesToPipes()) { + // Linux pipe capacity (buffer size) is 16 pages where each page is 4KB + bufferSize = 64 * 1024; // 64KB + } + byte[] buffer = new byte[bufferSize]; long size = info.size; while (size > 0) { long toRead = (buffer.length < size) ? buffer.length : size; diff --git a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java index 720687ef20cc..0e66fbc020a1 100644 --- a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java +++ b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java @@ -23,12 +23,12 @@ import android.content.Context; import android.os.Build; import android.util.Slog; -import com.google.security.cryptauth.lib.securegcm.BadHandleException; -import com.google.security.cryptauth.lib.securegcm.CryptoException; -import com.google.security.cryptauth.lib.securegcm.D2DConnectionContextV1; -import com.google.security.cryptauth.lib.securegcm.D2DHandshakeContext; -import com.google.security.cryptauth.lib.securegcm.D2DHandshakeContext.Role; -import com.google.security.cryptauth.lib.securegcm.HandshakeException; +import com.google.security.cryptauth.lib.securegcm.ukey2.BadHandleException; +import com.google.security.cryptauth.lib.securegcm.ukey2.CryptoException; +import com.google.security.cryptauth.lib.securegcm.ukey2.D2DConnectionContextV1; +import com.google.security.cryptauth.lib.securegcm.ukey2.D2DHandshakeContext; +import com.google.security.cryptauth.lib.securegcm.ukey2.D2DHandshakeContext.Role; +import com.google.security.cryptauth.lib.securegcm.ukey2.HandshakeException; import libcore.io.IoUtils; import libcore.io.Streams; diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java index 1f89e57b90d7..d1274d49a14d 100644 --- a/services/companion/java/com/android/server/companion/virtual/InputController.java +++ b/services/companion/java/com/android/server/companion/virtual/InputController.java @@ -245,8 +245,8 @@ class InputController { mInputManagerInternal.setPointerIconVisible(visible, displayId); } - void setPointerAcceleration(float pointerAcceleration, int displayId) { - mInputManagerInternal.setPointerAcceleration(pointerAcceleration, displayId); + void setMousePointerAccelerationEnabled(boolean enabled, int displayId) { + mInputManagerInternal.setMousePointerAccelerationEnabled(enabled, displayId); } void setDisplayEligibilityForPointerCapture(boolean isEligible, int displayId) { diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java index 58aa2c303345..44c3a8d7537f 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -1110,7 +1110,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub final long token = Binder.clearCallingIdentity(); try { mInputController.setShowPointerIcon(showPointer, displayId); - mInputController.setPointerAcceleration(1f, displayId); + mInputController.setMousePointerAccelerationEnabled(false, displayId); mInputController.setDisplayEligibilityForPointerCapture(/* isEligible= */ false, displayId); // WM throws a SecurityException if the display is untrusted. diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 19a9239752cd..7a4ac6ac4500 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -1356,8 +1356,8 @@ class StorageManagerService extends IStorageManager.Stub final int flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE; for (UserInfo user : users) { - prepareUserStorageInternal(fromVolumeUuid, user.id, user.serialNumber, flags); - prepareUserStorageInternal(toVolumeUuid, user.id, user.serialNumber, flags); + prepareUserStorageInternal(fromVolumeUuid, user.id, flags); + prepareUserStorageInternal(toVolumeUuid, user.id, flags); } } @@ -3231,7 +3231,7 @@ class StorageManagerService extends IStorageManager.Stub @android.annotation.EnforcePermission(android.Manifest.permission.STORAGE_INTERNAL) @Override - public void createUserStorageKeys(int userId, int serialNumber, boolean ephemeral) { + public void createUserStorageKeys(int userId, boolean ephemeral) { super.createUserStorageKeys_enforcePermission(); @@ -3276,8 +3276,7 @@ class StorageManagerService extends IStorageManager.Stub /* Only for use by LockSettingsService */ @android.annotation.EnforcePermission(android.Manifest.permission.STORAGE_INTERNAL) @Override - public void unlockCeStorage(@UserIdInt int userId, int serialNumber, byte[] secret) - throws RemoteException { + public void unlockCeStorage(@UserIdInt int userId, byte[] secret) throws RemoteException { super.unlockCeStorage_enforcePermission(); if (StorageManager.isFileEncrypted()) { @@ -3348,25 +3347,25 @@ class StorageManagerService extends IStorageManager.Stub continue; } - prepareUserStorageInternal(vol.fsUuid, user.id, user.serialNumber, flags); + prepareUserStorageInternal(vol.fsUuid, user.id, flags); } } @android.annotation.EnforcePermission(android.Manifest.permission.STORAGE_INTERNAL) @Override - public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) { + public void prepareUserStorage(String volumeUuid, int userId, int flags) { super.prepareUserStorage_enforcePermission(); try { - prepareUserStorageInternal(volumeUuid, userId, serialNumber, flags); + prepareUserStorageInternal(volumeUuid, userId, flags); } catch (Exception e) { throw new RuntimeException(e); } } - private void prepareUserStorageInternal(String volumeUuid, int userId, int serialNumber, - int flags) throws Exception { + private void prepareUserStorageInternal(String volumeUuid, int userId, int flags) + throws Exception { try { mVold.prepareUserStorage(volumeUuid, userId, flags); // After preparing user storage, we should check if we should mount data mirror again, diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 9eb35fde50fb..eb6fdd72f2c3 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -101,6 +101,7 @@ import com.android.internal.telephony.IOnSubscriptionsChangedListener; import com.android.internal.telephony.IPhoneStateListener; import com.android.internal.telephony.ITelephonyRegistry; import com.android.internal.telephony.TelephonyPermissions; +import com.android.internal.telephony.flags.Flags; import com.android.internal.telephony.util.TelephonyUtils; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; @@ -2679,6 +2680,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (!checkNotifyPermission("notifyEmergencyNumberList()")) { return; } + if (Flags.enforceTelephonyFeatureMappingForPublicApis()) { + if (!mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_TELEPHONY_CALLING)) { + // TelephonyManager.getEmergencyNumberList() throws an exception if + // FEATURE_TELEPHONY_CALLING is not defined. + return; + } + } synchronized (mRecords) { if (validatePhoneId(phoneId)) { diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 85abf87b4b2e..130a7333959d 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -3152,6 +3152,7 @@ public class AccountManagerService new AccountAuthenticatorResponse(this), authTokenType, true); + mCanStartAccountManagerActivity = true; Bundle bundle = new Bundle(); bundle.putParcelable(AccountManager.KEY_INTENT, intent); onResult(bundle); @@ -4933,6 +4934,7 @@ public class AccountManagerService IAccountAuthenticator mAuthenticator = null; private final boolean mStripAuthTokenFromResult; + protected boolean mCanStartAccountManagerActivity = false; protected final UserAccounts mAccounts; public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType, @@ -5068,9 +5070,13 @@ public class AccountManagerService private boolean isExportedSystemActivity(ActivityInfo activityInfo) { String className = activityInfo.name; - return "android".equals(activityInfo.packageName) && - (GrantCredentialsPermissionActivity.class.getName().equals(className) - || CantAddAccountActivity.class.getName().equals(className)); + if (!"android".equals(activityInfo.packageName)) { + return false; + + } + return (mCanStartAccountManagerActivity + && GrantCredentialsPermissionActivity.class.getName().equals(className)) + || CantAddAccountActivity.class.getName().equals(className); } private void close() { diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 8ad60e6a0782..72e62c37106d 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -243,7 +243,7 @@ final class ActivityManagerConstants extends ContentObserver { /** * The default value to {@link #KEY_ENABLE_NEW_OOMADJ}. */ - private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = Flags.oomadjusterCorrectnessRewrite(); + private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = false; /** * Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED} diff --git a/services/core/java/com/android/server/am/AnrHelper.java b/services/core/java/com/android/server/am/AnrHelper.java index e0a224629174..9fc0bf920969 100644 --- a/services/core/java/com/android/server/am/AnrHelper.java +++ b/services/core/java/com/android/server/am/AnrHelper.java @@ -63,6 +63,11 @@ class AnrHelper { private static final long CONSECUTIVE_ANR_TIME_MS = TimeUnit.MINUTES.toMillis(2); /** + * Time to wait before taking dumps for other processes to reduce load at boot time. + */ + private static final long SELF_ONLY_AFTER_BOOT_MS = TimeUnit.MINUTES.toMillis(10); + + /** * The keep alive time for the threads in the helper threadpool executor */ private static final int DEFAULT_THREAD_KEEP_ALIVE_SECOND = 10; @@ -231,7 +236,8 @@ class AnrHelper { // If there are many ANR at the same time, the latency may be larger. // If the latency is too large, the stack trace might not be meaningful. final long reportLatency = startTime - r.mTimestamp; - final boolean onlyDumpSelf = reportLatency > EXPIRED_REPORT_TIME_MS; + final boolean onlyDumpSelf = reportLatency > EXPIRED_REPORT_TIME_MS + || startTime < SELF_ONLY_AFTER_BOOT_MS; r.appNotResponding(onlyDumpSelf); final long endTime = SystemClock.uptimeMillis(); Slog.d(TAG, "Completed ANR of " + r.mApp.processName + " in " diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 08b129eeb8e5..2771572edb01 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -39,7 +39,7 @@ import android.app.PendingIntent; import android.app.RemoteServiceException.CannotPostForegroundServiceNotificationException; import android.app.compat.CompatChanges; import android.compat.annotation.ChangeId; -import android.compat.annotation.Disabled; +import android.compat.annotation.EnabledAfter; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -49,6 +49,7 @@ import android.content.pm.ServiceInfo; import android.net.Uri; import android.os.Binder; import android.os.Build; +import android.os.Build.VERSION_CODES; import android.os.IBinder; import android.os.PowerExemptionManager; import android.os.SystemClock; @@ -94,16 +95,14 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN * (See also android.app.ForegroundServiceTypePolicy) */ @ChangeId - // @EnabledAfter(targetSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE) - @Disabled + @EnabledAfter(targetSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE) static final long USE_NEW_WIU_LOGIC_FOR_START = 311208629L; /** * Compat ID to enable the new FGS start logic, for capability calculation. */ @ChangeId - // Always enabled - @Disabled + @EnabledAfter(targetSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE) static final long USE_NEW_WIU_LOGIC_FOR_CAPABILITIES = 313677553L; /** @@ -111,8 +110,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN * the background. */ @ChangeId - // @EnabledAfter(targetSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE) - @Disabled + @EnabledAfter(targetSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE) static final long USE_NEW_BFSL_LOGIC = 311208749L; final ActivityManagerService ams; diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java index 9f31f375dafe..5f12ce1e4163 100644 --- a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java +++ b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java @@ -73,7 +73,8 @@ public class AmbientContextManagerService extends private static final Set<Integer> DEFAULT_EVENT_SET = Sets.newHashSet( AmbientContextEvent.EVENT_COUGH, AmbientContextEvent.EVENT_SNORE, - AmbientContextEvent.EVENT_BACK_DOUBLE_TAP); + AmbientContextEvent.EVENT_BACK_DOUBLE_TAP, + AmbientContextEvent.EVENT_HEART_RATE); /** Default value in absence of {@link DeviceConfig} override. */ private static final boolean DEFAULT_SERVICE_ENABLED = true; diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index dada72eb51d9..f80228afa52d 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -1812,6 +1812,7 @@ public class AudioDeviceBroker { "msg: MSG_L_SET_BT_ACTIVE_DEVICE " + "received with null profile proxy: " + btInfo)).printLog(TAG)); + sendMsg(MSG_CHECK_MUTE_MUSIC, SENDMSG_REPLACE, 0 /*delay*/); return; } @AudioSystem.AudioFormatNativeEnumForBtCodec final int codec = diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index e05824a5f1ab..bf20ae3b516d 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -1992,7 +1992,7 @@ public class AudioDeviceInventory { // TODO: return; } else { AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( - "A2DP source device addr=" + Utils.anonymizeBluetoothAddress(address) + "A2DP sink device addr=" + Utils.anonymizeBluetoothAddress(address) + " now available").printLog(TAG)); } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 91d533c73b5d..4cbee2b89bb2 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -8835,6 +8835,8 @@ public class AudioService extends IAudioService.Stub synchronized (VolumeStreamState.class) { oldIndex = getIndex(device); index = getValidIndex(index, hasModifyAudioSettings); + // for STREAM_SYSTEM_ENFORCED, do not sync aliased streams on the enforced index + int aliasIndex = index; if ((mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) && mCameraSoundForced) { index = mIndexMax; } @@ -8853,7 +8855,8 @@ public class AudioService extends IAudioService.Stub if (streamType != mStreamType && mStreamVolumeAlias[streamType] == mStreamType && (changed || !aliasStreamState.hasIndexForDevice(device))) { - final int scaledIndex = rescaleIndex(index, mStreamType, streamType); + final int scaledIndex = + rescaleIndex(aliasIndex, mStreamType, streamType); aliasStreamState.setIndex(scaledIndex, device, caller, hasModifyAudioSettings); if (isCurrentDevice) { @@ -9375,6 +9378,14 @@ public class AudioService extends IAudioService.Stub if (mIsSingleVolume && (streamState.mStreamType != AudioSystem.STREAM_MUSIC)) { return; } + + // Persisting STREAM_SYSTEM_ENFORCED index is not needed as its alias (STREAM_RING) + // is persisted. This can also be problematic when the enforcement is active as it will + // override current SYSTEM_RING persisted value given they share the same settings name + // (due to aliasing). + if (streamState.mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) { + return; + } if (streamState.hasValidSettingsName()) { mSettings.putSystemIntForUser(mContentResolver, streamState.getSettingNameForDevice(device), diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index a818c30c2e28..f51043dc1cdc 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -634,16 +634,17 @@ public class BtHelper { return; } List<BluetoothDevice> activeDevices = adapter.getActiveDevices(profile); - if (activeDevices.isEmpty() || activeDevices.get(0) == null) { - return; + BluetoothProfileConnectionInfo bpci = new BluetoothProfileConnectionInfo(profile); + for (BluetoothDevice device : activeDevices) { + if (device == null) { + continue; + } + AudioDeviceBroker.BtDeviceChangedData data = new AudioDeviceBroker.BtDeviceChangedData( + device, null, bpci, "mBluetoothProfileServiceListener"); + AudioDeviceBroker.BtDeviceInfo info = mDeviceBroker.createBtDeviceInfo( + data, device, BluetoothProfile.STATE_CONNECTED); + mDeviceBroker.postBluetoothActiveDevice(info, 0 /* delay */); } - AudioDeviceBroker.BtDeviceChangedData data = new AudioDeviceBroker.BtDeviceChangedData( - activeDevices.get(0), null, new BluetoothProfileConnectionInfo(profile), - "mBluetoothProfileServiceListener"); - AudioDeviceBroker.BtDeviceInfo info = - mDeviceBroker.createBtDeviceInfo(data, activeDevices.get(0), - BluetoothProfile.STATE_CONNECTED); - mDeviceBroker.postBluetoothActiveDevice(info, 0 /* delay */); } // @GuardedBy("mDeviceBroker.mSetModeLock") @@ -678,8 +679,11 @@ public class BtHelper { if (adapter != null) { List<BluetoothDevice> activeDevices = adapter.getActiveDevices(BluetoothProfile.HEADSET); - if (activeDevices.size() > 0 && activeDevices.get(0) != null) { - onSetBtScoActiveDevice(activeDevices.get(0)); + for (BluetoothDevice device : activeDevices) { + if (device == null) { + continue; + } + onSetBtScoActiveDevice(device); } } else { Log.e(TAG, "onHeadsetProfileConnected: Null BluetoothAdapter"); diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java index 89b638be3500..89e08c165373 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java @@ -16,6 +16,8 @@ package com.android.server.biometrics.sensors; +import static com.android.server.biometrics.sensors.BiometricSchedulerOperation.STATE_STARTED; + import android.annotation.IntDef; import android.annotation.MainThread; import android.annotation.NonNull; @@ -28,6 +30,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserHandle; import android.util.Slog; import android.util.proto.ProtoOutputStream; @@ -35,6 +38,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.modules.expresslog.Counter; import com.android.server.biometrics.BiometricSchedulerProto; import com.android.server.biometrics.BiometricsProto; +import com.android.server.biometrics.Flags; import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher; import java.io.PrintWriter; @@ -48,6 +52,7 @@ import java.util.Deque; import java.util.List; import java.util.Locale; import java.util.function.Consumer; +import java.util.function.Supplier; /** * A scheduler for biometric HAL operations. Maintains a queue of {@link BaseClientMonitor} @@ -56,11 +61,16 @@ import java.util.function.Consumer; * * We currently assume (and require) that each biometric sensor have its own instance of a * {@link BiometricScheduler}. + * + * @param <T> Hal instance for starting the user. + * @param <U> Session associated with the current user id. + * + * TODO: (b/304604965) Update thread annotation when FLAGS_DE_HIDL is removed. */ @MainThread -public class BiometricScheduler { +public class BiometricScheduler<T, U> { - private static final String BASE_TAG = "BiometricScheduler"; + private static final String TAG = "BiometricScheduler"; // Number of recent operations to keep in our logs for dumpsys protected static final int LOG_NUM_RECENT_OPERATIONS = 50; @@ -89,30 +99,6 @@ public class BiometricScheduler { @Retention(RetentionPolicy.SOURCE) public @interface SensorType {} - public static @SensorType int sensorTypeFromFingerprintProperties( - @NonNull FingerprintSensorPropertiesInternal props) { - if (props.isAnyUdfpsType()) { - return SENSOR_TYPE_UDFPS; - } - - return SENSOR_TYPE_FP_OTHER; - } - - public static String sensorTypeToString(@SensorType int sensorType) { - switch (sensorType) { - case SENSOR_TYPE_UNKNOWN: - return "Unknown"; - case SENSOR_TYPE_FACE: - return "Face"; - case SENSOR_TYPE_UDFPS: - return "Udfps"; - case SENSOR_TYPE_FP_OTHER: - return "OtherFp"; - default: - return "UnknownUnknown"; - } - } - private static final class CrashState { static final int NUM_ENTRIES = 10; final String timestamp; @@ -145,8 +131,8 @@ public class BiometricScheduler { } } - @NonNull protected final String mBiometricTag; - private final @SensorType int mSensorType; + @SensorType + private final int mSensorType; @Nullable private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher; @NonNull private final IBiometricService mBiometricService; @NonNull protected final Handler mHandler; @@ -157,6 +143,43 @@ public class BiometricScheduler { private int mTotalOperationsHandled; private final int mRecentOperationsLimit; @NonNull private final List<Integer> mRecentOperations; + @Nullable private StopUserClient<U> mStopUserClient; + @NonNull private Supplier<Integer> mCurrentUserRetriever; + @Nullable private UserSwitchProvider<T, U> mUserSwitchProvider; + + private class UserSwitchClientCallback implements ClientMonitorCallback { + @NonNull private final BaseClientMonitor mOwner; + + UserSwitchClientCallback(@NonNull BaseClientMonitor owner) { + mOwner = owner; + } + + @Override + public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { + mHandler.post(() -> { + Slog.d(TAG, "[Client finished] " + clientMonitor + ", success: " + success); + + // Set mStopUserClient to null when StopUserClient fails. Otherwise it's possible + // for that the queue will wait indefinitely until the field is cleared. + if (clientMonitor instanceof StopUserClient<?>) { + if (!success) { + Slog.w(TAG, "StopUserClient failed(), is the HAL stuck? " + + "Clearing mStopUserClient"); + } + mStopUserClient = null; + } + if (mCurrentOperation != null && mCurrentOperation.isFor(mOwner)) { + mCurrentOperation = null; + } else { + // can happen if the hal dies and is usually okay + // do not unset the current operation that may be newer + Slog.w(TAG, "operation is already null or different (reset?): " + + mCurrentOperation); + } + startNextOperationIfIdle(); + }); + } + } // Internal callback, notified when an operation is complete. Notifies the requester // that the operation is complete, before performing internal scheduler work (such as @@ -164,26 +187,26 @@ public class BiometricScheduler { private final ClientMonitorCallback mInternalCallback = new ClientMonitorCallback() { @Override public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) { - Slog.d(getTag(), "[Started] " + clientMonitor); + Slog.d(TAG, "[Started] " + clientMonitor); } @Override public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { mHandler.post(() -> { if (mCurrentOperation == null) { - Slog.e(getTag(), "[Finishing] " + clientMonitor + Slog.e(TAG, "[Finishing] " + clientMonitor + " but current operation is null, success: " + success + ", possible lifecycle bug in clientMonitor implementation?"); return; } if (!mCurrentOperation.isFor(clientMonitor)) { - Slog.e(getTag(), "[Ignoring Finish] " + clientMonitor + " does not match" + Slog.e(TAG, "[Ignoring Finish] " + clientMonitor + " does not match" + " current: " + mCurrentOperation); return; } - Slog.d(getTag(), "[Finishing] " + clientMonitor + ", success: " + success); + Slog.d(TAG, "[Finishing] " + clientMonitor + ", success: " + success); if (mGestureAvailabilityDispatcher != null) { mGestureAvailabilityDispatcher.markSensorActive( @@ -202,13 +225,11 @@ public class BiometricScheduler { }; @VisibleForTesting - public BiometricScheduler(@NonNull String tag, - @NonNull Handler handler, + public BiometricScheduler(@NonNull Handler handler, @SensorType int sensorType, @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher, @NonNull IBiometricService biometricService, int recentOperationsLimit) { - mBiometricTag = tag; mHandler = handler; mSensorType = sensorType; mGestureAvailabilityDispatcher = gestureAvailabilityDispatcher; @@ -219,49 +240,140 @@ public class BiometricScheduler { mRecentOperations = new ArrayList<>(); } + @VisibleForTesting + public BiometricScheduler(@NonNull Handler handler, + @SensorType int sensorType, + @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher, + @NonNull IBiometricService biometricService, + int recentOperationsLimit, + @NonNull Supplier<Integer> currentUserRetriever, + @Nullable UserSwitchProvider<T, U> userSwitchProvider) { + mHandler = handler; + mSensorType = sensorType; + mGestureAvailabilityDispatcher = gestureAvailabilityDispatcher; + mPendingOperations = new ArrayDeque<>(); + mBiometricService = biometricService; + mCrashStates = new ArrayDeque<>(); + mRecentOperationsLimit = recentOperationsLimit; + mRecentOperations = new ArrayList<>(); + mCurrentUserRetriever = currentUserRetriever; + mUserSwitchProvider = userSwitchProvider; + } + + public BiometricScheduler(@NonNull Handler handler, + @SensorType int sensorType, + @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher, + @NonNull Supplier<Integer> currentUserRetriever, + @NonNull UserSwitchProvider<T, U> userSwitchProvider) { + this(handler, sensorType, gestureAvailabilityDispatcher, + IBiometricService.Stub.asInterface(ServiceManager.getService( + Context.BIOMETRIC_SERVICE)), LOG_NUM_RECENT_OPERATIONS, + currentUserRetriever, userSwitchProvider); + } + /** * Creates a new scheduler. * - * @param tag for the specific instance of the scheduler. Should be unique. * @param sensorType the sensorType that this scheduler is handling. * @param gestureAvailabilityDispatcher may be null if the sensor does not support gestures * (such as fingerprint swipe). */ - public BiometricScheduler(@NonNull String tag, - @SensorType int sensorType, + public BiometricScheduler(@SensorType int sensorType, @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { - this(tag, new Handler(Looper.getMainLooper()), sensorType, gestureAvailabilityDispatcher, + this(new Handler(Looper.getMainLooper()), sensorType, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface( ServiceManager.getService(Context.BIOMETRIC_SERVICE)), LOG_NUM_RECENT_OPERATIONS); } + /** + * Returns sensor type for a fingerprint sensor. + */ + @SensorType + public static int sensorTypeFromFingerprintProperties( + @NonNull FingerprintSensorPropertiesInternal props) { + if (props.isAnyUdfpsType()) { + return SENSOR_TYPE_UDFPS; + } + + return SENSOR_TYPE_FP_OTHER; + } + @VisibleForTesting public ClientMonitorCallback getInternalCallback() { return mInternalCallback; } - protected String getTag() { - return BASE_TAG + "/" + mBiometricTag; + protected void startNextOperationIfIdle() { + if (Flags.deHidl()) { + startNextOperation(); + } else { + startNextOperationIfIdleLegacy(); + } + } + + protected void startNextOperation() { + if (mCurrentOperation != null) { + Slog.v(TAG, "Not idle, current operation: " + mCurrentOperation); + return; + } + if (mPendingOperations.isEmpty()) { + Slog.d(TAG, "No operations, returning to idle"); + return; + } + + final int currentUserId = mCurrentUserRetriever.get(); + final int nextUserId = mPendingOperations.getFirst().getTargetUserId(); + + if (nextUserId == currentUserId || mPendingOperations.getFirst().isStartUserOperation()) { + startNextOperationIfIdleLegacy(); + } else if (currentUserId == UserHandle.USER_NULL && mUserSwitchProvider != null) { + final BaseClientMonitor startClient = + mUserSwitchProvider.getStartUserClient(nextUserId); + final UserSwitchClientCallback finishedCallback = + new UserSwitchClientCallback(startClient); + + Slog.d(TAG, "[Starting User] " + startClient); + mCurrentOperation = new BiometricSchedulerOperation( + startClient, finishedCallback, STATE_STARTED); + startClient.start(finishedCallback); + } else if (mUserSwitchProvider != null) { + if (mStopUserClient != null) { + Slog.d(TAG, "[Waiting for StopUser] " + mStopUserClient); + } else { + mStopUserClient = mUserSwitchProvider + .getStopUserClient(currentUserId); + final UserSwitchClientCallback finishedCallback = + new UserSwitchClientCallback(mStopUserClient); + + Slog.d(TAG, "[Stopping User] current: " + currentUserId + + ", next: " + nextUserId + ". " + mStopUserClient); + mCurrentOperation = new BiometricSchedulerOperation( + mStopUserClient, finishedCallback, STATE_STARTED); + mStopUserClient.start(finishedCallback); + } + } else { + Slog.e(TAG, "Cannot start next operation."); + } } - protected void startNextOperationIfIdle() { + protected void startNextOperationIfIdleLegacy() { if (mCurrentOperation != null) { - Slog.v(getTag(), "Not idle, current operation: " + mCurrentOperation); + Slog.v(TAG, "Not idle, current operation: " + mCurrentOperation); return; } if (mPendingOperations.isEmpty()) { - Slog.d(getTag(), "No operations, returning to idle"); + Slog.d(TAG, "No operations, returning to idle"); return; } mCurrentOperation = mPendingOperations.poll(); - Slog.d(getTag(), "[Polled] " + mCurrentOperation); + Slog.d(TAG, "[Polled] " + mCurrentOperation); // If the operation at the front of the queue has been marked for cancellation, send // ERROR_CANCELED. No need to start this client. if (mCurrentOperation.isMarkedCanceling()) { - Slog.d(getTag(), "[Now Cancelling] " + mCurrentOperation); + Slog.d(TAG, "[Now Cancelling] " + mCurrentOperation); mCurrentOperation.cancel(mHandler, mInternalCallback); // Now we wait for the client to send its FinishCallback, which kicks off the next // operation. @@ -289,7 +401,7 @@ public class BiometricScheduler { // Note down current length of queue final int pendingOperationsLength = mPendingOperations.size(); final BiometricSchedulerOperation lastOperation = mPendingOperations.peekLast(); - Slog.e(getTag(), "[Unable To Start] " + mCurrentOperation + Slog.e(TAG, "[Unable To Start] " + mCurrentOperation + ". Last pending operation: " + lastOperation); // Then for each operation currently in the pending queue at the time of this @@ -298,10 +410,10 @@ public class BiometricScheduler { for (int i = 0; i < pendingOperationsLength; i++) { final BiometricSchedulerOperation operation = mPendingOperations.pollFirst(); if (operation != null) { - Slog.w(getTag(), "[Aborting Operation] " + operation); + Slog.w(TAG, "[Aborting Operation] " + operation); operation.abort(); } else { - Slog.e(getTag(), "Null operation, index: " + i + Slog.e(TAG, "Null operation, index: " + i + ", expected length: " + pendingOperationsLength); } } @@ -317,9 +429,9 @@ public class BiometricScheduler { mBiometricService.onReadyForAuthentication( mCurrentOperation.getClientMonitor().getRequestId(), cookie); } catch (RemoteException e) { - Slog.e(getTag(), "Remote exception when contacting BiometricService", e); + Slog.e(TAG, "Remote exception when contacting BiometricService", e); } - Slog.d(getTag(), "Waiting for cookie before starting: " + mCurrentOperation); + Slog.d(TAG, "Waiting for cookie before starting: " + mCurrentOperation); } } @@ -338,14 +450,14 @@ public class BiometricScheduler { */ public void startPreparedClient(int cookie) { if (mCurrentOperation == null) { - Slog.e(getTag(), "Current operation is null"); + Slog.e(TAG, "Current operation is null"); return; } if (mCurrentOperation.startWithCookie(mInternalCallback, cookie)) { - Slog.d(getTag(), "[Started] Prepared client: " + mCurrentOperation); + Slog.d(TAG, "[Started] Prepared client: " + mCurrentOperation); } else { - Slog.e(getTag(), "[Unable To Start] Prepared client: " + mCurrentOperation); + Slog.e(TAG, "[Unable To Start] Prepared client: " + mCurrentOperation); mCurrentOperation = null; startNextOperationIfIdle(); } @@ -374,13 +486,13 @@ public class BiometricScheduler { if (clientMonitor.interruptsPrecedingClients()) { for (BiometricSchedulerOperation operation : mPendingOperations) { if (operation.markCanceling()) { - Slog.d(getTag(), "New client, marking pending op as canceling: " + operation); + Slog.d(TAG, "New client, marking pending op as canceling: " + operation); } } } mPendingOperations.add(new BiometricSchedulerOperation(clientMonitor, clientCallback)); - Slog.d(getTag(), "[Added] " + clientMonitor + Slog.d(TAG, "[Added] " + clientMonitor + ", new queue size: " + mPendingOperations.size()); // If the new operation should interrupt preceding clients, and if the current operation is @@ -389,7 +501,7 @@ public class BiometricScheduler { && mCurrentOperation != null && mCurrentOperation.isInterruptable() && mCurrentOperation.isStarted()) { - Slog.d(getTag(), "[Cancelling Interruptable]: " + mCurrentOperation); + Slog.d(TAG, "[Cancelling Interruptable]: " + mCurrentOperation); mCurrentOperation.cancel(mHandler, mInternalCallback); } else { startNextOperationIfIdle(); @@ -401,16 +513,16 @@ public class BiometricScheduler { * @param token from the caller, should match the token passed in when requesting enrollment */ public void cancelEnrollment(IBinder token, long requestId) { - Slog.d(getTag(), "cancelEnrollment, requestId: " + requestId); + Slog.d(TAG, "cancelEnrollment, requestId: " + requestId); if (mCurrentOperation != null && canCancelEnrollOperation(mCurrentOperation, token, requestId)) { - Slog.d(getTag(), "Cancelling enrollment op: " + mCurrentOperation); + Slog.d(TAG, "Cancelling enrollment op: " + mCurrentOperation); mCurrentOperation.cancel(mHandler, mInternalCallback); } else { for (BiometricSchedulerOperation operation : mPendingOperations) { if (canCancelEnrollOperation(operation, token, requestId)) { - Slog.d(getTag(), "Cancelling pending enrollment op: " + operation); + Slog.d(TAG, "Cancelling pending enrollment op: " + operation); operation.markCanceling(); } } @@ -423,16 +535,16 @@ public class BiometricScheduler { * @param requestId the id returned when requesting authentication */ public void cancelAuthenticationOrDetection(IBinder token, long requestId) { - Slog.d(getTag(), "cancelAuthenticationOrDetection, requestId: " + requestId); + Slog.d(TAG, "cancelAuthenticationOrDetection, requestId: " + requestId); if (mCurrentOperation != null && canCancelAuthOperation(mCurrentOperation, token, requestId)) { - Slog.d(getTag(), "Cancelling auth/detect op: " + mCurrentOperation); + Slog.d(TAG, "Cancelling auth/detect op: " + mCurrentOperation); mCurrentOperation.cancel(mHandler, mInternalCallback); } else { for (BiometricSchedulerOperation operation : mPendingOperations) { if (canCancelAuthOperation(operation, token, requestId)) { - Slog.d(getTag(), "Cancelling pending auth/detect op: " + operation); + Slog.d(TAG, "Cancelling pending auth/detect op: " + operation); operation.markCanceling(); } } @@ -504,11 +616,11 @@ public class BiometricScheduler { mCurrentOperation != null ? mCurrentOperation.toString() : null, pendingOperations); mCrashStates.add(crashState); - Slog.e(getTag(), "Recorded crash state: " + crashState.toString()); + Slog.e(TAG, "Recorded crash state: " + crashState.toString()); } public void dump(PrintWriter pw) { - pw.println("Dump of BiometricScheduler " + getTag()); + pw.println("Dump of BiometricScheduler " + TAG); pw.println("Type: " + mSensorType); pw.println("Current operation: " + mCurrentOperation); pw.println("Pending operations: " + mPendingOperations.size()); @@ -548,7 +660,7 @@ public class BiometricScheduler { * HAL dies. */ public void reset() { - Slog.d(getTag(), "Resetting scheduler"); + Slog.d(TAG, "Resetting scheduler"); mPendingOperations.clear(); mCurrentOperation = null; } @@ -562,11 +674,11 @@ public class BiometricScheduler { return; } for (BiometricSchedulerOperation pendingOperation : mPendingOperations) { - Slog.d(getTag(), "[Watchdog cancelling pending] " + Slog.d(TAG, "[Watchdog cancelling pending] " + pendingOperation.getClientMonitor()); pendingOperation.markCancelingForWatchdog(); } - Slog.d(getTag(), "[Watchdog cancelling current] " + Slog.d(TAG, "[Watchdog cancelling current] " + mCurrentOperation.getClientMonitor()); mCurrentOperation.cancel(mHandler, getInternalCallback()); } @@ -590,9 +702,23 @@ public class BiometricScheduler { /** * Handle stop user client when user switching occurs. */ - public void onUserStopped() {} + public void onUserStopped() { + if (mStopUserClient == null) { + Slog.e(TAG, "Unexpected onUserStopped"); + return; + } + + Slog.d(TAG, "[OnUserStopped]: " + mStopUserClient); + mStopUserClient.onUserStopped(); + mStopUserClient = null; + } public Handler getHandler() { return mHandler; } + + @Nullable + public StopUserClient<?> getStopUserClient() { + return mStopUserClient; + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java b/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java index e8654dc059a4..e01c4ec76ed2 100644 --- a/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java @@ -30,7 +30,10 @@ import java.util.function.Supplier; /** * Abstract class for stopping a user. - * @param <T> Interface for stopping the user. + * + * @param <T> Session for stopping the user. It should be either an instance of + * {@link com.android.server.biometrics.sensors.fingerprint.aidl.AidlSession} or + * {@link com.android.server.biometrics.sensors.face.aidl.AidlSession}. */ public abstract class StopUserClient<T> extends HalClientMonitor<T> { diff --git a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java index 3753bbdba276..7ca10e3f9c98 100644 --- a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java @@ -33,10 +33,14 @@ import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDisp /** * A user-aware scheduler that requests user-switches based on scheduled operation's targetUserId. + * TODO (b/304604965): Remove class when Flags.FLAG_DE_HIDL is removed. + * + * @param <T> Hal instance for starting the user. + * @param <U> Session associated with the current user id. */ -public class UserAwareBiometricScheduler extends BiometricScheduler { +public class UserAwareBiometricScheduler<T, U> extends BiometricScheduler<T, U> { - private static final String BASE_TAG = "UaBiometricScheduler"; + private static final String TAG = "UaBiometricScheduler"; /** * Interface to retrieve the owner's notion of the current userId. Note that even though @@ -66,13 +70,13 @@ public class UserAwareBiometricScheduler extends BiometricScheduler { @Override public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { mHandler.post(() -> { - Slog.d(getTag(), "[Client finished] " + clientMonitor + ", success: " + success); + Slog.d(TAG, "[Client finished] " + clientMonitor + ", success: " + success); // Set mStopUserClient to null when StopUserClient fails. Otherwise it's possible // for that the queue will wait indefinitely until the field is cleared. if (clientMonitor instanceof StopUserClient<?>) { if (!success) { - Slog.w(getTag(), "StopUserClient failed(), is the HAL stuck? " + Slog.w(TAG, "StopUserClient failed(), is the HAL stuck? " + "Clearing mStopUserClient"); } mStopUserClient = null; @@ -82,7 +86,7 @@ public class UserAwareBiometricScheduler extends BiometricScheduler { } else { // can happen if the hal dies and is usually okay // do not unset the current operation that may be newer - Slog.w(getTag(), "operation is already null or different (reset?): " + Slog.w(TAG, "operation is already null or different (reset?): " + mCurrentOperation); } startNextOperationIfIdle(); @@ -98,7 +102,7 @@ public class UserAwareBiometricScheduler extends BiometricScheduler { @NonNull IBiometricService biometricService, @NonNull CurrentUserRetriever currentUserRetriever, @NonNull UserSwitchCallback userSwitchCallback) { - super(tag, handler, sensorType, gestureAvailabilityDispatcher, biometricService, + super(handler, sensorType, gestureAvailabilityDispatcher, biometricService, LOG_NUM_RECENT_OPERATIONS); mCurrentUserRetriever = currentUserRetriever; @@ -117,18 +121,13 @@ public class UserAwareBiometricScheduler extends BiometricScheduler { } @Override - protected String getTag() { - return BASE_TAG + "/" + mBiometricTag; - } - - @Override protected void startNextOperationIfIdle() { if (mCurrentOperation != null) { - Slog.v(getTag(), "Not idle, current operation: " + mCurrentOperation); + Slog.v(TAG, "Not idle, current operation: " + mCurrentOperation); return; } if (mPendingOperations.isEmpty()) { - Slog.d(getTag(), "No operations, returning to idle"); + Slog.d(TAG, "No operations, returning to idle"); return; } @@ -143,20 +142,20 @@ public class UserAwareBiometricScheduler extends BiometricScheduler { final ClientFinishedCallback finishedCallback = new ClientFinishedCallback(startClient); - Slog.d(getTag(), "[Starting User] " + startClient); + Slog.d(TAG, "[Starting User] " + startClient); mCurrentOperation = new BiometricSchedulerOperation( startClient, finishedCallback, STATE_STARTED); startClient.start(finishedCallback); } else { if (mStopUserClient != null) { - Slog.d(getTag(), "[Waiting for StopUser] " + mStopUserClient); + Slog.d(TAG, "[Waiting for StopUser] " + mStopUserClient); } else { mStopUserClient = mUserSwitchCallback .getStopUserClient(currentUserId); final ClientFinishedCallback finishedCallback = new ClientFinishedCallback(mStopUserClient); - Slog.d(getTag(), "[Stopping User] current: " + currentUserId + Slog.d(TAG, "[Stopping User] current: " + currentUserId + ", next: " + nextUserId + ". " + mStopUserClient); mCurrentOperation = new BiometricSchedulerOperation( mStopUserClient, finishedCallback, STATE_STARTED); @@ -168,11 +167,11 @@ public class UserAwareBiometricScheduler extends BiometricScheduler { @Override public void onUserStopped() { if (mStopUserClient == null) { - Slog.e(getTag(), "Unexpected onUserStopped"); + Slog.e(TAG, "Unexpected onUserStopped"); return; } - Slog.d(getTag(), "[OnUserStopped]: " + mStopUserClient); + Slog.d(TAG, "[OnUserStopped]: " + mStopUserClient); mStopUserClient.onUserStopped(); mStopUserClient = null; } diff --git a/services/core/java/com/android/server/biometrics/sensors/UserSwitchProvider.java b/services/core/java/com/android/server/biometrics/sensors/UserSwitchProvider.java new file mode 100644 index 000000000000..bc5c55b99ab3 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/UserSwitchProvider.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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; + +import android.annotation.NonNull; + +/** + * Interface to get the appropriate start and stop user clients. + * + * @param <T> Hal instance for starting the user. + * @param <U> Session associated with the current user id. + */ +public interface UserSwitchProvider<T, U> { + @NonNull + StartUserClient<T, U> getStartUserClient(int newUserId); + @NonNull + StopUserClient<U> getStopUserClient(int userId); +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java index af46f441d6ce..3d61f993b7d2 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java @@ -53,12 +53,10 @@ public class AidlSession { mAidlResponseHandler = aidlResponseHandler; } - /** The underlying {@link ISession}. */ @NonNull public ISession getSession() { return mSession; } - /** The user id associated with the session. */ public int getUserId() { return mUserId; } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java index 9fa15b8ea3a1..e4ecf1a61155 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java @@ -39,6 +39,7 @@ import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.face.IFaceServiceReceiver; import android.os.Binder; import android.os.Handler; +import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; @@ -88,6 +89,8 @@ import java.util.concurrent.atomic.AtomicLong; * Provider for a single instance of the {@link IFace} HAL. */ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { + + private static final String TAG = "FaceProvider"; private static final int ENROLL_TIMEOUT_SEC = 75; private boolean mTestHalEnabled; @@ -159,7 +162,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { @NonNull BiometricContext biometricContext, boolean resetLockoutRequiresChallenge) { this(context, biometricStateCallback, props, halInstanceName, lockoutResetDispatcher, - biometricContext, null /* daemon */, resetLockoutRequiresChallenge, + biometricContext, null /* daemon */, getHandler(), resetLockoutRequiresChallenge, false /* testHalEnabled */); } @@ -169,13 +172,19 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { @NonNull String halInstanceName, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull BiometricContext biometricContext, - @Nullable IFace daemon, boolean resetLockoutRequiresChallenge, + @Nullable IFace daemon, + @NonNull Handler handler, + boolean resetLockoutRequiresChallenge, boolean testHalEnabled) { mContext = context; mBiometricStateCallback = biometricStateCallback; mHalInstanceName = halInstanceName; mFaceSensors = new SensorList<>(ActivityManager.getService()); - mHandler = new Handler(Looper.getMainLooper()); + if (Flags.deHidl()) { + mHandler = handler; + } else { + mHandler = new Handler(Looper.getMainLooper()); + } mUsageStats = new UsageStats(context); mLockoutResetDispatcher = lockoutResetDispatcher; mActivityTaskManager = ActivityTaskManager.getInstance(); @@ -189,6 +198,13 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { initSensors(resetLockoutRequiresChallenge, props); } + @NonNull + private static Handler getHandler() { + HandlerThread handlerThread = new HandlerThread(TAG); + handlerThread.start(); + return new Handler(handlerThread.getLooper()); + } + private void initAuthenticationBroadcastReceiver() { new AuthenticationStatsBroadcastReceiver( mContext, @@ -230,8 +246,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { prop.commonProps.maxEnrollmentsPerUser, componentInfo, prop.sensorType, prop.supportsDetectInteraction, prop.halControlsPreview, false /* resetLockoutRequiresChallenge */); - final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, - mContext, mHandler, internalProp, mLockoutResetDispatcher, + final Sensor sensor = new Sensor(this, + mContext, mHandler, internalProp, mBiometricContext); sensor.init(mLockoutResetDispatcher, this); final int userId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL : @@ -250,9 +266,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { private void addHidlSensors(SensorProps prop, boolean resetLockoutRequiresChallenge) { final int sensorId = prop.commonProps.sensorId; - final Sensor sensor = new HidlToAidlSensorAdapter(getTag() + "/" + sensorId, this, - mContext, mHandler, prop, mLockoutResetDispatcher, - mBiometricContext, resetLockoutRequiresChallenge, + final Sensor sensor = new HidlToAidlSensorAdapter(this, mContext, mHandler, prop, + mLockoutResetDispatcher, mBiometricContext, resetLockoutRequiresChallenge, () -> { //TODO: update to make this testable scheduleInternalCleanup(sensorId, ActivityManager.getCurrentUser(), @@ -279,8 +294,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { private void addAidlSensors(SensorProps prop, boolean resetLockoutRequiresChallenge) { final int sensorId = prop.commonProps.sensorId; - final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, - mHandler, prop, mLockoutResetDispatcher, mBiometricContext, + final Sensor sensor = new Sensor(this, mContext, mHandler, prop, mBiometricContext, resetLockoutRequiresChallenge); sensor.init(mLockoutResetDispatcher, this); final int userId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL : @@ -296,7 +310,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { } private String getTag() { - return "FaceProvider/" + mHalInstanceName; + return TAG + "/" + mHalInstanceName; } boolean hasHalInstance() { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java index 0110ae991ae4..e5ae8e336dcb 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java @@ -19,6 +19,7 @@ package com.android.server.biometrics.sensors.face.aidl; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.hardware.biometrics.face.ISession; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; @@ -30,10 +31,10 @@ import com.android.server.biometrics.sensors.StopUserClient; import java.util.function.Supplier; -public class FaceStopUserClient extends StopUserClient<AidlSession> { +public class FaceStopUserClient extends StopUserClient<ISession> { private static final String TAG = "FaceStopUserClient"; - public FaceStopUserClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon, + public FaceStopUserClient(@NonNull Context context, @NonNull Supplier<ISession> lazyDaemon, @Nullable IBinder token, int userId, int sensorId, @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, @NonNull UserStoppedCallback callback) { @@ -49,7 +50,7 @@ public class FaceStopUserClient extends StopUserClient<AidlSession> { @Override protected void startHalOperation() { try { - getFreshDaemon().getSession().close(); + getFreshDaemon().close(); } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); getCallback().onClientFinished(this, false /* success */); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java index 3e5c59914913..635e79a31a96 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java @@ -58,6 +58,7 @@ import com.android.server.biometrics.sensors.LockoutTracker; import com.android.server.biometrics.sensors.StartUserClient; import com.android.server.biometrics.sensors.StopUserClient; import com.android.server.biometrics.sensors.UserAwareBiometricScheduler; +import com.android.server.biometrics.sensors.UserSwitchProvider; import com.android.server.biometrics.sensors.face.FaceUtils; import java.util.ArrayList; @@ -71,15 +72,16 @@ import java.util.function.Supplier; */ public class Sensor { + private static final String TAG = "Sensor"; + private boolean mTestHalEnabled; - @NonNull private final String mTag; @NonNull private final FaceProvider mProvider; @NonNull private final Context mContext; @NonNull private final IBinder mToken; @NonNull private final Handler mHandler; @NonNull private final FaceSensorPropertiesInternal mSensorProperties; - @NonNull private BiometricScheduler mScheduler; + @NonNull private BiometricScheduler<IFace, ISession> mScheduler; @Nullable private LockoutTracker mLockoutTracker; @NonNull private final Map<Integer, Long> mAuthenticatorIds; @@ -88,11 +90,9 @@ public class Sensor { @NonNull BiometricContext mBiometricContext; - Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context, + Sensor(@NonNull FaceProvider provider, @NonNull Context context, @NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties, - @NonNull LockoutResetDispatcher lockoutResetDispatcher, - @NonNull BiometricContext biometricContext, @Nullable AidlSession session) { - mTag = tag; + @NonNull BiometricContext biometricContext) { mProvider = provider; mContext = context; mToken = new Binder(); @@ -102,105 +102,135 @@ public class Sensor { mAuthenticatorIds = new HashMap<>(); } - Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context, - @NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties, - @NonNull LockoutResetDispatcher lockoutResetDispatcher, - @NonNull BiometricContext biometricContext) { - this(tag, provider, context, handler, sensorProperties, lockoutResetDispatcher, - biometricContext, null); - } - - public Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context, + public Sensor(@NonNull FaceProvider provider, @NonNull Context context, @NonNull Handler handler, @NonNull SensorProps prop, - @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull BiometricContext biometricContext, boolean resetLockoutRequiresChallenge) { - this(tag, provider, context, handler, + this(provider, context, handler, getFaceSensorPropertiesInternal(prop, resetLockoutRequiresChallenge), - lockoutResetDispatcher, biometricContext, null); + biometricContext); } /** * Initialize biometric scheduler, lockout tracker and session for the sensor. */ - public void init(LockoutResetDispatcher lockoutResetDispatcher, - FaceProvider provider) { - mScheduler = new UserAwareBiometricScheduler(mTag, - BiometricScheduler.SENSOR_TYPE_FACE, null /* gestureAvailabilityDispatcher */, + public void init(@NonNull LockoutResetDispatcher lockoutResetDispatcher, + @NonNull FaceProvider provider) { + if (Flags.deHidl()) { + setScheduler(getBiometricSchedulerForInit(lockoutResetDispatcher, provider)); + } else { + setScheduler(getUserAwareBiometricSchedulerForInit(lockoutResetDispatcher, provider)); + } + mLazySession = () -> mCurrentSession != null ? mCurrentSession : null; + mLockoutTracker = new LockoutCache(); + } + + private BiometricScheduler<IFace, ISession> getBiometricSchedulerForInit( + @NonNull LockoutResetDispatcher lockoutResetDispatcher, + @NonNull FaceProvider provider) { + return new BiometricScheduler<>(mHandler, + BiometricScheduler.SENSOR_TYPE_FACE, + null /* gestureAvailabilityDispatcher */, () -> mCurrentSession != null ? mCurrentSession.getUserId() : UserHandle.USER_NULL, - new UserAwareBiometricScheduler.UserSwitchCallback() { + new UserSwitchProvider<IFace, ISession>() { @NonNull @Override - public StopUserClient<?> getStopUserClient(int userId) { - return new FaceStopUserClient(mContext, mLazySession, mToken, userId, - mSensorProperties.sensorId, - BiometricLogger.ofUnknown(mContext), mBiometricContext, - () -> mCurrentSession = null); + public StopUserClient<ISession> getStopUserClient(int userId) { + return new FaceStopUserClient(mContext, + () -> mLazySession.get().getSession(), mToken, userId, + mSensorProperties.sensorId, BiometricLogger.ofUnknown(mContext), + mBiometricContext, () -> mCurrentSession = null); } @NonNull @Override - public StartUserClient<?, ?> getStartUserClient(int newUserId) { + public StartUserClient<IFace, ISession> getStartUserClient(int newUserId) { final int sensorId = mSensorProperties.sensorId; + final AidlResponseHandler resultController = new AidlResponseHandler( + mContext, mScheduler, sensorId, newUserId, + mLockoutTracker, lockoutResetDispatcher, + mBiometricContext.getAuthSessionCoordinator(), () -> { + }, + new AidlResponseHandler.AidlResponseHandlerCallback() { + @Override + public void onEnrollSuccess() { + mProvider.scheduleLoadAuthenticatorIdsForUser(sensorId, + newUserId); + mProvider.scheduleInvalidationRequest(sensorId, + newUserId); + } - final AidlResponseHandler resultController; - if (Flags.deHidl()) { - resultController = new AidlResponseHandler( - mContext, mScheduler, sensorId, newUserId, - mLockoutTracker, lockoutResetDispatcher, - mBiometricContext.getAuthSessionCoordinator(), () -> {}, - new AidlResponseHandler.AidlResponseHandlerCallback() { - @Override - public void onEnrollSuccess() { - mProvider.scheduleLoadAuthenticatorIdsForUser(sensorId, - newUserId); - mProvider.scheduleInvalidationRequest(sensorId, - newUserId); - } - - @Override - public void onHardwareUnavailable() { - Slog.e(mTag, "Face sensor hardware unavailable."); - mCurrentSession = null; - } - }); - } else { - resultController = new AidlResponseHandler( - mContext, mScheduler, sensorId, newUserId, - mLockoutTracker, lockoutResetDispatcher, - mBiometricContext.getAuthSessionCoordinator(), () -> { - Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE"); - mCurrentSession = null; - }); - } - - final StartUserClient.UserStartedCallback<ISession> userStartedCallback = - (userIdStarted, newSession, halInterfaceVersion) -> { - Slog.d(mTag, "New session created for user: " - + userIdStarted + " with hal version: " - + halInterfaceVersion); - mCurrentSession = new AidlSession(halInterfaceVersion, - newSession, userIdStarted, resultController); - if (FaceUtils.getLegacyInstance(sensorId) - .isInvalidationInProgress(mContext, userIdStarted)) { - Slog.w(mTag, - "Scheduling unfinished invalidation request for " - + "sensor: " - + sensorId - + ", user: " + userIdStarted); - provider.scheduleInvalidationRequest(sensorId, - userIdStarted); + @Override + public void onHardwareUnavailable() { + Slog.e(TAG, "Face sensor hardware unavailable."); + mCurrentSession = null; } - }; + }); - return new FaceStartUserClient(mContext, provider::getHalInstance, - mToken, newUserId, mSensorProperties.sensorId, - BiometricLogger.ofUnknown(mContext), mBiometricContext, - resultController, userStartedCallback); + return Sensor.this.getStartUserClient(resultController, sensorId, + newUserId, provider); } }); - mLazySession = () -> mCurrentSession != null ? mCurrentSession : null; - mLockoutTracker = new LockoutCache(); + } + + private UserAwareBiometricScheduler<IFace, ISession> getUserAwareBiometricSchedulerForInit( + LockoutResetDispatcher lockoutResetDispatcher, + FaceProvider provider) { + return new UserAwareBiometricScheduler<>(TAG, + BiometricScheduler.SENSOR_TYPE_FACE, null /* gestureAvailabilityDispatcher */, + () -> mCurrentSession != null ? mCurrentSession.getUserId() : UserHandle.USER_NULL, + new UserAwareBiometricScheduler.UserSwitchCallback() { + @NonNull + @Override + public StopUserClient<ISession> getStopUserClient(int userId) { + return new FaceStopUserClient(mContext, + () -> mLazySession.get().getSession(), mToken, userId, + mSensorProperties.sensorId, BiometricLogger.ofUnknown(mContext), + mBiometricContext, () -> mCurrentSession = null); + } + + @NonNull + @Override + public StartUserClient<IFace, ISession> getStartUserClient(int newUserId) { + final int sensorId = mSensorProperties.sensorId; + final AidlResponseHandler resultController = new AidlResponseHandler( + mContext, mScheduler, sensorId, newUserId, + mLockoutTracker, lockoutResetDispatcher, + mBiometricContext.getAuthSessionCoordinator(), () -> { + Slog.e(TAG, "Face sensor hardware unavailable."); + mCurrentSession = null; + }); + + return Sensor.this.getStartUserClient(resultController, sensorId, + newUserId, provider); + } + }); + } + + private FaceStartUserClient getStartUserClient(@NonNull AidlResponseHandler resultController, + int sensorId, int newUserId, @NonNull FaceProvider provider) { + final StartUserClient.UserStartedCallback<ISession> userStartedCallback = + (userIdStarted, newSession, halInterfaceVersion) -> { + Slog.d(TAG, "New face session created for user: " + + userIdStarted + " with hal version: " + + halInterfaceVersion); + mCurrentSession = new AidlSession(halInterfaceVersion, + newSession, userIdStarted, resultController); + if (FaceUtils.getLegacyInstance(sensorId) + .isInvalidationInProgress(mContext, userIdStarted)) { + Slog.w(TAG, + "Scheduling unfinished invalidation request for " + + "face sensor: " + + sensorId + + ", user: " + userIdStarted); + provider.scheduleInvalidationRequest(sensorId, + userIdStarted); + } + }; + + return new FaceStartUserClient(mContext, provider::getHalInstance, mToken, newUserId, + mSensorProperties.sensorId, BiometricLogger.ofUnknown(mContext), mBiometricContext, + resultController, userStartedCallback); } private static FaceSensorPropertiesInternal getFaceSensorPropertiesInternal(SensorProps prop, @@ -213,13 +243,11 @@ public class Sensor { info.softwareVersion)); } } - final FaceSensorPropertiesInternal internalProp = new FaceSensorPropertiesInternal( + return new FaceSensorPropertiesInternal( prop.commonProps.sensorId, prop.commonProps.sensorStrength, prop.commonProps.maxEnrollmentsPerUser, componentInfo, prop.sensorType, prop.supportsDetectInteraction, prop.halControlsPreview, resetLockoutRequiresChallenge); - - return internalProp; } @NonNull public Supplier<AidlSession> getLazySession() { @@ -243,7 +271,7 @@ public class Sensor { mProvider, this); } - @NonNull public BiometricScheduler getScheduler() { + @NonNull public BiometricScheduler<IFace, ISession> getScheduler() { return mScheduler; } @@ -259,17 +287,17 @@ public class Sensor { } void setTestHalEnabled(boolean enabled) { - Slog.w(mTag, "setTestHalEnabled: " + enabled); + Slog.w(TAG, "Face setTestHalEnabled: " + enabled); if (enabled != mTestHalEnabled) { // The framework should retrieve a new session from the HAL. try { if (mCurrentSession != null) { // TODO(181984005): This should be scheduled instead of directly invoked - Slog.d(mTag, "Closing old session"); + Slog.d(TAG, "Closing old face session"); mCurrentSession.getSession().close(); } } catch (RemoteException e) { - Slog.e(mTag, "RemoteException", e); + Slog.e(TAG, "RemoteException", e); } mCurrentSession = null; } @@ -308,7 +336,7 @@ public class Sensor { public void onBinderDied() { final BaseClientMonitor client = mScheduler.getCurrentClient(); if (client != null && client.isInterruptable()) { - Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client); + Slog.e(TAG, "Sending face hardware unavailable error for client: " + client); final ErrorConsumer errorConsumer = (ErrorConsumer) client; errorConsumer.onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); 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 46ce0b62e6d5..53376669b387 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 @@ -120,7 +120,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { @NonNull private final FaceSensorPropertiesInternal mSensorProperties; @NonNull private final BiometricStateCallback mBiometricStateCallback; @NonNull private final Context mContext; - @NonNull private final BiometricScheduler mScheduler; + @NonNull private final BiometricScheduler<IBiometricsFace, AidlSession> mScheduler; @NonNull private final Handler mHandler; @NonNull private final Supplier<IBiometricsFace> mLazyDaemon; @NonNull private final LockoutHalImpl mLockoutTracker; @@ -163,14 +163,15 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { private final int mSensorId; @NonNull private final Context mContext; @NonNull private final Handler mHandler; - @NonNull private final BiometricScheduler mScheduler; + @NonNull private final BiometricScheduler<IBiometricsFace, AidlSession> mScheduler; @Nullable private Callback mCallback; @NonNull private final LockoutHalImpl mLockoutTracker; @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher; HalResultController(int sensorId, @NonNull Context context, @NonNull Handler handler, - @NonNull BiometricScheduler scheduler, @NonNull LockoutHalImpl lockoutTracker, + @NonNull BiometricScheduler<IBiometricsFace, AidlSession> scheduler, + @NonNull LockoutHalImpl lockoutTracker, @NonNull LockoutResetDispatcher lockoutResetDispatcher) { mSensorId = sensorId; mContext = context; @@ -352,7 +353,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { @NonNull FaceSensorPropertiesInternal sensorProps, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull Handler handler, - @NonNull BiometricScheduler scheduler, + @NonNull BiometricScheduler<IBiometricsFace, AidlSession> scheduler, @NonNull BiometricContext biometricContext) { mSensorProperties = sensorProps; mContext = context; @@ -395,7 +396,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { @NonNull LockoutResetDispatcher lockoutResetDispatcher) { final Handler handler = new Handler(Looper.getMainLooper()); return new Face10(context, biometricStateCallback, sensorProps, lockoutResetDispatcher, - handler, new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_FACE, + handler, new BiometricScheduler<>( + BiometricScheduler.SENSOR_TYPE_FACE, null /* gestureAvailabilityTracker */), BiometricContext.getInstance(context)); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java index 6355cb57a752..a004cae4ae26 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.pm.UserInfo; +import android.hardware.biometrics.face.ISession; import android.hardware.biometrics.face.SensorProps; import android.hardware.biometrics.face.V1_0.IBiometricsFace; import android.os.Handler; @@ -67,8 +68,7 @@ public class HidlToAidlSensorAdapter extends Sensor implements IHwBinder.DeathRe }; private LockoutHalImpl mLockoutTracker; - public HidlToAidlSensorAdapter(@NonNull String tag, - @NonNull FaceProvider provider, + public HidlToAidlSensorAdapter(@NonNull FaceProvider provider, @NonNull Context context, @NonNull Handler handler, @NonNull SensorProps prop, @@ -76,15 +76,14 @@ public class HidlToAidlSensorAdapter extends Sensor implements IHwBinder.DeathRe @NonNull BiometricContext biometricContext, boolean resetLockoutRequiresChallenge, @NonNull Runnable internalCleanupAndGetFeatureRunnable) { - this(tag, provider, context, handler, prop, lockoutResetDispatcher, biometricContext, + this(provider, context, handler, prop, lockoutResetDispatcher, biometricContext, resetLockoutRequiresChallenge, internalCleanupAndGetFeatureRunnable, new AuthSessionCoordinator(), null /* daemon */, null /* onEnrollSuccessCallback */); } @VisibleForTesting - HidlToAidlSensorAdapter(@NonNull String tag, - @NonNull FaceProvider provider, + HidlToAidlSensorAdapter(@NonNull FaceProvider provider, @NonNull Context context, @NonNull Handler handler, @NonNull SensorProps prop, @@ -95,7 +94,7 @@ public class HidlToAidlSensorAdapter extends Sensor implements IHwBinder.DeathRe @NonNull AuthSessionCoordinator authSessionCoordinator, @Nullable IBiometricsFace daemon, @Nullable AidlResponseHandler.AidlResponseHandlerCallback aidlResponseHandlerCallback) { - super(tag, provider, context, handler, prop, lockoutResetDispatcher, biometricContext, + super(provider, context, handler, prop, biometricContext, resetLockoutRequiresChallenge); mInternalCleanupAndGetFeatureRunnable = internalCleanupAndGetFeatureRunnable; mFaceProvider = provider; @@ -124,7 +123,7 @@ public class HidlToAidlSensorAdapter extends Sensor implements IHwBinder.DeathRe @Override public void serviceDied(long cookie) { - Slog.d(TAG, "HAL died."); + Slog.d(TAG, "Face HAL died."); mDaemon = null; } @@ -140,10 +139,12 @@ public class HidlToAidlSensorAdapter extends Sensor implements IHwBinder.DeathRe } @Override - public void init(LockoutResetDispatcher lockoutResetDispatcher, - FaceProvider provider) { - setScheduler(new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_FACE, - null /* gestureAvailabilityTracker */)); + public void init(@NonNull LockoutResetDispatcher lockoutResetDispatcher, + @NonNull FaceProvider provider) { + setScheduler(new BiometricScheduler<ISession, AidlSession>(getHandler(), + BiometricScheduler.SENSOR_TYPE_FACE, + null /* gestureAvailabilityTracker */, () -> mCurrentUserId, + null /* userSwitchProvider */)); setLazySession(this::getSession); mLockoutTracker = new LockoutHalImpl(); } @@ -188,7 +189,7 @@ public class HidlToAidlSensorAdapter extends Sensor implements IHwBinder.DeathRe return mDaemon; } - Slog.d(TAG, "Daemon was null, reconnecting, current operation: " + Slog.d(TAG, "Face daemon was null, reconnecting, current operation: " + getScheduler().getCurrentClient()); try { @@ -213,7 +214,7 @@ public class HidlToAidlSensorAdapter extends Sensor implements IHwBinder.DeathRe } @VisibleForTesting void handleUserChanged(int newUserId) { - Slog.d(TAG, "User changed. Current user is " + newUserId); + Slog.d(TAG, "User changed. Current user for face sensor is " + newUserId); mSession = null; mCurrentUserId = newUserId; } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapter.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapter.java index 5daf2d4fbcf4..fa953615a1d6 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapter.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapter.java @@ -282,7 +282,7 @@ public class HidlToAidlSessionAdapter implements ISession { @Override public ICancellationSignal enrollWithOptions(FaceEnrollOptions options) { - //Unsupported in HIDL + Slog.e(TAG, "enrollWithOptions unsupported in HIDL"); return null; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java index 8ff105baa981..0d4dac089907 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java @@ -51,12 +51,10 @@ public class AidlSession { mAidlResponseHandler = aidlResponseHandler; } - /** The underlying {@link ISession}. */ @NonNull public ISession getSession() { return mSession; } - /** The user id associated with the session. */ public int getUserId() { return mUserId; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index 88a11d9c0ceb..c0388d1c4f21 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -46,6 +46,7 @@ import android.hardware.fingerprint.ISidefpsController; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.Binder; import android.os.Handler; +import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; @@ -102,6 +103,8 @@ import java.util.stream.Collectors; @SuppressWarnings("deprecation") public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvider { + private static final String TAG = "FingerprintProvider"; + private boolean mTestHalEnabled; @NonNull @@ -172,7 +175,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi boolean resetLockoutRequiresHardwareAuthToken) { this(context, biometricStateCallback, authenticationStateListeners, props, halInstanceName, lockoutResetDispatcher, gestureAvailabilityDispatcher, biometricContext, - null /* daemon */, resetLockoutRequiresHardwareAuthToken, + null /* daemon */, getHandler(), resetLockoutRequiresHardwareAuthToken, false /* testHalEnabled */); } @@ -184,6 +187,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, @NonNull BiometricContext biometricContext, @Nullable IFingerprint daemon, + @NonNull Handler handler, boolean resetLockoutRequiresHardwareAuthToken, boolean testHalEnabled) { mContext = context; @@ -191,7 +195,11 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mAuthenticationStateListeners = authenticationStateListeners; mHalInstanceName = halInstanceName; mFingerprintSensors = new SensorList<>(ActivityManager.getService()); - mHandler = new Handler(Looper.getMainLooper()); + if (Flags.deHidl()) { + mHandler = handler; + } else { + mHandler = new Handler(Looper.getMainLooper()); + } mLockoutResetDispatcher = lockoutResetDispatcher; mActivityTaskManager = ActivityTaskManager.getInstance(); mTaskStackListener = new BiometricTaskStackListener(); @@ -204,6 +212,13 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi initSensors(resetLockoutRequiresHardwareAuthToken, props, gestureAvailabilityDispatcher); } + @NonNull + private static Handler getHandler() { + HandlerThread handlerThread = new HandlerThread(TAG); + handlerThread.start(); + return new Handler(handlerThread.getLooper()); + } + private void initAuthenticationBroadcastReceiver() { new AuthenticationStatsBroadcastReceiver( mContext, @@ -262,11 +277,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi location.sensorLocationY, location.sensorRadius)) .collect(Collectors.toList())); - final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, - mHandler, internalProp, mLockoutResetDispatcher, - gestureAvailabilityDispatcher, mBiometricContext); - sensor.init(gestureAvailabilityDispatcher, - mLockoutResetDispatcher); + final Sensor sensor = new Sensor(this, mContext, mHandler, internalProp, + mBiometricContext); + sensor.init(gestureAvailabilityDispatcher, mLockoutResetDispatcher); final int sessionUserId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL : sensor.getLazySession().get().getUserId(); @@ -286,10 +299,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, boolean resetLockoutRequiresHardwareAuthToken) { final int sensorId = prop.commonProps.sensorId; - final Sensor sensor = new HidlToAidlSensorAdapter(getTag() + "/" - + sensorId, this, mContext, mHandler, - prop, mLockoutResetDispatcher, gestureAvailabilityDispatcher, - mBiometricContext, resetLockoutRequiresHardwareAuthToken, + final Sensor sensor = new HidlToAidlSensorAdapter(this, mContext, mHandler, prop, + mLockoutResetDispatcher, mBiometricContext, resetLockoutRequiresHardwareAuthToken, () -> scheduleInternalCleanup(sensorId, ActivityManager.getCurrentUser(), null /* callback */)); sensor.init(gestureAvailabilityDispatcher, mLockoutResetDispatcher); @@ -307,14 +318,11 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi private void addAidlSensors(@NonNull SensorProps prop, @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, - List<SensorLocationInternal> workaroundLocations, + @NonNull List<SensorLocationInternal> workaroundLocations, boolean resetLockoutRequiresHardwareAuthToken) { final int sensorId = prop.commonProps.sensorId; - final Sensor sensor = new Sensor(getTag() + "/" + sensorId, - this, mContext, mHandler, - prop, mLockoutResetDispatcher, gestureAvailabilityDispatcher, - mBiometricContext, workaroundLocations, - resetLockoutRequiresHardwareAuthToken); + final Sensor sensor = new Sensor(this, mContext, mHandler, prop, mBiometricContext, + workaroundLocations, resetLockoutRequiresHardwareAuthToken); sensor.init(gestureAvailabilityDispatcher, mLockoutResetDispatcher); final int sessionUserId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL : sensor.getLazySession().get().getUserId(); @@ -329,7 +337,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi } private String getTag() { - return "FingerprintProvider/" + mHalInstanceName; + return TAG + "/" + mHalInstanceName; } boolean hasHalInstance() { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java index 2cc1879c0851..394f04520531 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java @@ -19,6 +19,7 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.hardware.biometrics.fingerprint.ISession; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; @@ -30,11 +31,11 @@ import com.android.server.biometrics.sensors.StopUserClient; import java.util.function.Supplier; -public class FingerprintStopUserClient extends StopUserClient<AidlSession> { +public class FingerprintStopUserClient extends StopUserClient<ISession> { private static final String TAG = "FingerprintStopUserClient"; public FingerprintStopUserClient(@NonNull Context context, - @NonNull Supplier<AidlSession> lazyDaemon, @Nullable IBinder token, int userId, + @NonNull Supplier<ISession> lazyDaemon, @Nullable IBinder token, int userId, int sensorId, @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, @NonNull UserStoppedCallback callback) { @@ -50,7 +51,7 @@ public class FingerprintStopUserClient extends StopUserClient<AidlSession> { @Override protected void startHalOperation() { try { - getFreshDaemon().getSession().close(); + getFreshDaemon().close(); } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); getCallback().onClientFinished(this, false /* success */); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java index dd887bb05c12..af88c62904fc 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java @@ -59,6 +59,7 @@ import com.android.server.biometrics.sensors.LockoutTracker; import com.android.server.biometrics.sensors.StartUserClient; import com.android.server.biometrics.sensors.StopUserClient; import com.android.server.biometrics.sensors.UserAwareBiometricScheduler; +import com.android.server.biometrics.sensors.UserSwitchProvider; import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils; import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher; @@ -77,15 +78,16 @@ import java.util.stream.Collectors; @SuppressWarnings("deprecation") public class Sensor { + private static final String TAG = "Sensor"; + private boolean mTestHalEnabled; - @NonNull private final String mTag; @NonNull private final FingerprintProvider mProvider; @NonNull private final Context mContext; @NonNull private final IBinder mToken; @NonNull private final Handler mHandler; @NonNull private final FingerprintSensorPropertiesInternal mSensorProperties; - @NonNull private BiometricScheduler mScheduler; + @NonNull private BiometricScheduler<IFingerprint, ISession> mScheduler; @NonNull private LockoutTracker mLockoutTracker; @NonNull private final Map<Integer, Long> mAuthenticatorIds; @NonNull private final BiometricContext mBiometricContext; @@ -93,13 +95,10 @@ public class Sensor { @Nullable AidlSession mCurrentSession; @NonNull private Supplier<AidlSession> mLazySession; - public Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, + public Sensor(@NonNull FingerprintProvider provider, @NonNull Context context, @NonNull Handler handler, @NonNull FingerprintSensorPropertiesInternal sensorProperties, - @NonNull LockoutResetDispatcher lockoutResetDispatcher, - @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, @NonNull BiometricContext biometricContext, AidlSession session) { - mTag = tag; mProvider = provider; mContext = context; mToken = new Binder(); @@ -110,41 +109,52 @@ public class Sensor { mCurrentSession = session; } - Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context, + Sensor(@NonNull FingerprintProvider provider, @NonNull Context context, @NonNull Handler handler, @NonNull FingerprintSensorPropertiesInternal sensorProperties, - @NonNull LockoutResetDispatcher lockoutResetDispatcher, - @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, @NonNull BiometricContext biometricContext) { - this(tag, provider, context, handler, sensorProperties, lockoutResetDispatcher, - gestureAvailabilityDispatcher, biometricContext, null); + this(provider, context, handler, sensorProperties, + biometricContext, null); } - Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context, + Sensor(@NonNull FingerprintProvider provider, @NonNull Context context, @NonNull Handler handler, @NonNull SensorProps sensorProp, - @NonNull LockoutResetDispatcher lockoutResetDispatcher, - @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, @NonNull BiometricContext biometricContext, @NonNull List<SensorLocationInternal> workaroundLocation, boolean resetLockoutRequiresHardwareAuthToken) { - this(tag, provider, context, handler, getFingerprintSensorPropertiesInternal(sensorProp, + this(provider, context, handler, getFingerprintSensorPropertiesInternal(sensorProp, workaroundLocation, resetLockoutRequiresHardwareAuthToken), - lockoutResetDispatcher, gestureAvailabilityDispatcher, biometricContext, null); + biometricContext, null); } /** * Initialize biometric scheduler, lockout tracker and session for the sensor. */ - public void init(GestureAvailabilityDispatcher gestureAvailabilityDispatcher, - LockoutResetDispatcher lockoutResetDispatcher) { - mScheduler = new UserAwareBiometricScheduler(mTag, + public void init(@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, + @NonNull LockoutResetDispatcher lockoutResetDispatcher) { + if (Flags.deHidl()) { + setScheduler(getBiometricSchedulerForInit(gestureAvailabilityDispatcher, + lockoutResetDispatcher)); + } else { + setScheduler(getUserAwareBiometricSchedulerForInit(gestureAvailabilityDispatcher, + lockoutResetDispatcher)); + } + mLockoutTracker = new LockoutCache(); + mLazySession = () -> mCurrentSession != null ? mCurrentSession : null; + } + + private BiometricScheduler<IFingerprint, ISession> getBiometricSchedulerForInit( + @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, + @NonNull LockoutResetDispatcher lockoutResetDispatcher) { + return new BiometricScheduler<>(mHandler, BiometricScheduler.sensorTypeFromFingerprintProperties(mSensorProperties), gestureAvailabilityDispatcher, () -> mCurrentSession != null ? mCurrentSession.getUserId() : UserHandle.USER_NULL, - new UserAwareBiometricScheduler.UserSwitchCallback() { + new UserSwitchProvider<IFingerprint, ISession>() { @NonNull @Override - public StopUserClient<?> getStopUserClient(int userId) { - return new FingerprintStopUserClient(mContext, mLazySession, mToken, + public StopUserClient<ISession> getStopUserClient(int userId) { + return new FingerprintStopUserClient(mContext, + () -> mLazySession.get().getSession(), mToken, userId, mSensorProperties.sensorId, BiometricLogger.ofUnknown(mContext), mBiometricContext, () -> mCurrentSession = null); @@ -152,69 +162,100 @@ public class Sensor { @NonNull @Override - public StartUserClient<?, ?> getStartUserClient(int newUserId) { + public StartUserClient<IFingerprint, ISession> getStartUserClient( + int newUserId) { final int sensorId = mSensorProperties.sensorId; - - final AidlResponseHandler resultController; - - if (Flags.deHidl()) { - resultController = new AidlResponseHandler( - mContext, mScheduler, sensorId, newUserId, - mLockoutTracker, lockoutResetDispatcher, - mBiometricContext.getAuthSessionCoordinator(), () -> {}, - new AidlResponseHandler.AidlResponseHandlerCallback() { - @Override - public void onEnrollSuccess() { - mProvider.scheduleLoadAuthenticatorIdsForUser(sensorId, - newUserId); - mProvider.scheduleInvalidationRequest(sensorId, - newUserId); - } - - @Override - public void onHardwareUnavailable() { - Slog.e(mTag, - "Fingerprint sensor hardware unavailable."); - mCurrentSession = null; - } - }); - } else { - resultController = new AidlResponseHandler( - mContext, mScheduler, sensorId, newUserId, - mLockoutTracker, lockoutResetDispatcher, - mBiometricContext.getAuthSessionCoordinator(), () -> { - Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE"); - mCurrentSession = null; - }); - } - - final StartUserClient.UserStartedCallback<ISession> userStartedCallback = - (userIdStarted, newSession, halInterfaceVersion) -> { - Slog.d(mTag, "New session created for user: " - + userIdStarted + " with hal version: " - + halInterfaceVersion); - mCurrentSession = new AidlSession(halInterfaceVersion, - newSession, userIdStarted, resultController); - if (FingerprintUtils.getInstance(sensorId) - .isInvalidationInProgress(mContext, userIdStarted)) { - Slog.w(mTag, - "Scheduling unfinished invalidation request for " - + "sensor: " - + sensorId - + ", user: " + userIdStarted); + final AidlResponseHandler resultController = new AidlResponseHandler( + mContext, mScheduler, sensorId, newUserId, + mLockoutTracker, lockoutResetDispatcher, + mBiometricContext.getAuthSessionCoordinator(), () -> {}, + new AidlResponseHandler.AidlResponseHandlerCallback() { + @Override + public void onEnrollSuccess() { + mProvider.scheduleLoadAuthenticatorIdsForUser(sensorId, + newUserId); mProvider.scheduleInvalidationRequest(sensorId, - userIdStarted); + newUserId); + } + + @Override + public void onHardwareUnavailable() { + Slog.e(TAG, + "Fingerprint sensor hardware unavailable."); + mCurrentSession = null; } - }; + }); - return new FingerprintStartUserClient(mContext, mProvider::getHalInstance, - mToken, newUserId, mSensorProperties.sensorId, + return Sensor.this.getStartUserClient(resultController, sensorId, + newUserId); + } + }); + } + + private UserAwareBiometricScheduler<ISession, AidlSession> + getUserAwareBiometricSchedulerForInit( + GestureAvailabilityDispatcher gestureAvailabilityDispatcher, + LockoutResetDispatcher lockoutResetDispatcher) { + return new UserAwareBiometricScheduler<>(TAG, + BiometricScheduler.sensorTypeFromFingerprintProperties(mSensorProperties), + gestureAvailabilityDispatcher, + () -> mCurrentSession != null ? mCurrentSession.getUserId() : UserHandle.USER_NULL, + new UserAwareBiometricScheduler.UserSwitchCallback() { + @NonNull + @Override + public StopUserClient<ISession> getStopUserClient(int userId) { + return new FingerprintStopUserClient(mContext, + () -> mLazySession.get().getSession(), mToken, + userId, mSensorProperties.sensorId, BiometricLogger.ofUnknown(mContext), mBiometricContext, - resultController, userStartedCallback); + () -> mCurrentSession = null); + } + + @NonNull + @Override + public StartUserClient<IFingerprint, ISession> getStartUserClient( + int newUserId) { + final int sensorId = mSensorProperties.sensorId; + + final AidlResponseHandler resultController = new AidlResponseHandler( + mContext, mScheduler, sensorId, newUserId, + mLockoutTracker, lockoutResetDispatcher, + mBiometricContext.getAuthSessionCoordinator(), () -> { + Slog.e(TAG, "Fingerprint hardware unavailable."); + mCurrentSession = null; + }); + + return Sensor.this.getStartUserClient(resultController, sensorId, + newUserId); } }); - mLockoutTracker = new LockoutCache(); - mLazySession = () -> mCurrentSession != null ? mCurrentSession : null; + } + + private FingerprintStartUserClient getStartUserClient(AidlResponseHandler resultController, + int sensorId, int newUserId) { + final StartUserClient.UserStartedCallback<ISession> userStartedCallback = + (userIdStarted, newSession, halInterfaceVersion) -> { + Slog.d(TAG, "New fingerprint session created for user: " + + userIdStarted + " with hal version: " + + halInterfaceVersion); + mCurrentSession = new AidlSession(halInterfaceVersion, + newSession, userIdStarted, resultController); + if (FingerprintUtils.getInstance(sensorId) + .isInvalidationInProgress(mContext, userIdStarted)) { + Slog.w(TAG, + "Scheduling unfinished invalidation request for " + + "fingerprint sensor: " + + sensorId + + ", user: " + userIdStarted); + mProvider.scheduleInvalidationRequest(sensorId, + userIdStarted); + } + }; + + return new FingerprintStartUserClient(mContext, mProvider::getHalInstance, + mToken, newUserId, mSensorProperties.sensorId, + BiometricLogger.ofUnknown(mContext), mBiometricContext, + resultController, userStartedCallback); } protected static FingerprintSensorPropertiesInternal getFingerprintSensorPropertiesInternal( @@ -267,7 +308,7 @@ public class Sensor { biometricStateCallback, mProvider, this); } - @NonNull public BiometricScheduler getScheduler() { + @NonNull public BiometricScheduler<IFingerprint, ISession> getScheduler() { return mScheduler; } @@ -283,17 +324,17 @@ public class Sensor { } void setTestHalEnabled(boolean enabled) { - Slog.w(mTag, "setTestHalEnabled: " + enabled); + Slog.w(TAG, "Fingerprint setTestHalEnabled: " + enabled); if (enabled != mTestHalEnabled) { // The framework should retrieve a new session from the HAL. try { if (mCurrentSession != null) { // TODO(181984005): This should be scheduled instead of directly invoked - Slog.d(mTag, "Closing old session"); + Slog.d(TAG, "Closing old fingerprint session"); mCurrentSession.getSession().close(); } } catch (RemoteException e) { - Slog.e(mTag, "RemoteException", e); + Slog.e(TAG, "RemoteException", e); } mCurrentSession = null; } @@ -335,7 +376,7 @@ public class Sensor { public void onBinderDied() { final BaseClientMonitor client = mScheduler.getCurrentClient(); if (client instanceof ErrorConsumer) { - Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client); + Slog.e(TAG, "Sending fingerprint hardware unavailable error for client: " + client); final ErrorConsumer errorConsumer = (ErrorConsumer) client; errorConsumer.onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index d3cecd0e34c7..4accf8f7ff30 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -119,7 +119,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @NonNull private final AuthenticationStateListeners mAuthenticationStateListeners; private final ActivityTaskManager mActivityTaskManager; @NonNull private final FingerprintSensorPropertiesInternal mSensorProperties; - private final BiometricScheduler mScheduler; + private final BiometricScheduler<IBiometricsFingerprint, AidlSession> mScheduler; private final Handler mHandler; private final LockoutResetDispatcher mLockoutResetDispatcher; private final LockoutFrameworkImpl mLockoutTracker; @@ -198,11 +198,11 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider private final int mSensorId; @NonNull private final Context mContext; @NonNull final Handler mHandler; - @NonNull final BiometricScheduler mScheduler; + @NonNull final BiometricScheduler<IBiometricsFingerprint, AidlSession> mScheduler; @Nullable private Callback mCallback; HalResultController(int sensorId, @NonNull Context context, @NonNull Handler handler, - @NonNull BiometricScheduler scheduler) { + @NonNull BiometricScheduler<IBiometricsFingerprint, AidlSession> scheduler) { mSensorId = sensorId; mContext = context; mHandler = handler; @@ -336,7 +336,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @NonNull BiometricStateCallback biometricStateCallback, @NonNull AuthenticationStateListeners authenticationStateListeners, @NonNull FingerprintSensorPropertiesInternal sensorProps, - @NonNull BiometricScheduler scheduler, + @NonNull BiometricScheduler<IBiometricsFingerprint, AidlSession> scheduler, @NonNull Handler handler, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull HalResultController controller, @@ -389,8 +389,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @NonNull Handler handler, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { - final BiometricScheduler scheduler = - new BiometricScheduler(TAG, + final BiometricScheduler<IBiometricsFingerprint, AidlSession> scheduler = + new BiometricScheduler<>( BiometricScheduler.sensorTypeFromFingerprintProperties(sensorProps), gestureAvailabilityDispatcher); final HalResultController controller = new HalResultController(sensorProps.sensorId, @@ -533,8 +533,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider private void scheduleUpdateActiveUserWithoutHandler(int targetUserId, boolean force) { final boolean hasEnrolled = !getEnrolledFingerprints(mSensorProperties.sensorId, targetUserId).isEmpty(); - final FingerprintUpdateActiveUserClient client = - new FingerprintUpdateActiveUserClient(mContext, mLazyDaemon, targetUserId, + final FingerprintUpdateActiveUserClientLegacy client = + new FingerprintUpdateActiveUserClientLegacy(mContext, mLazyDaemon, targetUserId, mContext.getOpPackageName(), mSensorProperties.sensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN, diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java index 88dae6fcc453..9232e11a05ee 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java @@ -140,9 +140,9 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage private static class TestableBiometricScheduler extends BiometricScheduler { @NonNull private Fingerprint21UdfpsMock mFingerprint21; - TestableBiometricScheduler(@NonNull String tag, @NonNull Handler handler, + TestableBiometricScheduler( @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { - super(tag, BiometricScheduler.SENSOR_TYPE_FP_OTHER, gestureAvailabilityDispatcher); + super(BiometricScheduler.SENSOR_TYPE_FP_OTHER, gestureAvailabilityDispatcher); } void init(@NonNull Fingerprint21UdfpsMock fingerprint21) { @@ -258,7 +258,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage final Handler handler = new Handler(Looper.getMainLooper()); final TestableBiometricScheduler scheduler = - new TestableBiometricScheduler(TAG, handler, gestureAvailabilityDispatcher); + new TestableBiometricScheduler(gestureAvailabilityDispatcher); final MockHalResultController controller = new MockHalResultController(sensorProps.sensorId, context, handler, scheduler); return new Fingerprint21UdfpsMock(context, biometricStateCallback, diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java index 5c5b9928f57a..59e64cd06667 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java @@ -18,7 +18,7 @@ package com.android.server.biometrics.sensors.fingerprint.hidl; import android.annotation.NonNull; import android.content.Context; -import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; +import android.hardware.biometrics.fingerprint.ISession; import android.os.Build; import android.os.Environment; import android.os.RemoteException; @@ -39,8 +39,8 @@ import java.util.function.Supplier; /** * Sets the HAL's current active user, and updates the framework's authenticatorId cache. */ -public class FingerprintUpdateActiveUserClient extends - StartUserClient<IBiometricsFingerprint, AidlSession> { +public class FingerprintUpdateActiveUserClient extends StartUserClient<ISession, + AidlSession> { private static final String TAG = "FingerprintUpdateActiveUserClient"; private static final String FP_DATA_DIR = "fpdata"; @@ -52,19 +52,7 @@ public class FingerprintUpdateActiveUserClient extends private File mDirectory; FingerprintUpdateActiveUserClient(@NonNull Context context, - @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, int userId, - @NonNull String owner, int sensorId, - @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, - @NonNull Supplier<Integer> currentUserId, - boolean hasEnrolledBiometrics, @NonNull Map<Integer, Long> authenticatorIds, - boolean forceUpdateAuthenticatorId) { - this(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext, currentUserId, - hasEnrolledBiometrics, authenticatorIds, forceUpdateAuthenticatorId, - (newUserId, newUser, halInterfaceVersion) -> {}); - } - - FingerprintUpdateActiveUserClient(@NonNull Context context, - @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, int userId, + @NonNull Supplier<ISession> lazyDaemon, int userId, @NonNull String owner, int sensorId, @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, @NonNull Supplier<Integer> currentUserId, @@ -132,9 +120,10 @@ public class FingerprintUpdateActiveUserClient extends try { final int targetId = getTargetUserId(); Slog.d(TAG, "Setting active user: " + targetId); - getFreshDaemon().setActiveGroup(targetId, mDirectory.getAbsolutePath()); + HidlToAidlSessionAdapter sessionAdapter = (HidlToAidlSessionAdapter) getFreshDaemon(); + sessionAdapter.setActiveGroup(targetId, mDirectory.getAbsolutePath()); mAuthenticatorIds.put(targetId, mHasEnrolledBiometrics - ? getFreshDaemon().getAuthenticatorId() : 0L); + ? sessionAdapter.getAuthenticatorIdForUpdateClient() : 0L); mUserStartedCallback.onUserStarted(targetId, null, 0); mCallback.onClientFinished(this, true /* success */); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClientLegacy.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClientLegacy.java new file mode 100644 index 000000000000..fc85402edef4 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClientLegacy.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.fingerprint.hidl; + +import android.annotation.NonNull; +import android.content.Context; +import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; +import android.os.Build; +import android.os.Environment; +import android.os.RemoteException; +import android.os.SELinux; +import android.util.Slog; + +import com.android.server.biometrics.BiometricsProto; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; +import com.android.server.biometrics.sensors.ClientMonitorCallback; +import com.android.server.biometrics.sensors.HalClientMonitor; + +import java.io.File; +import java.util.Map; +import java.util.function.Supplier; + +/** + * TODO(b/304604965): Delete this class once Flags.DE_HIDL is ready for release. + */ +public class FingerprintUpdateActiveUserClientLegacy extends + HalClientMonitor<IBiometricsFingerprint> { + private static final String TAG = "FingerprintUpdateActiveUserClient"; + private static final String FP_DATA_DIR = "fpdata"; + + private final Supplier<Integer> mCurrentUserId; + private final boolean mForceUpdateAuthenticatorId; + private final boolean mHasEnrolledBiometrics; + private final Map<Integer, Long> mAuthenticatorIds; + private File mDirectory; + + FingerprintUpdateActiveUserClientLegacy(@NonNull Context context, + @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, int userId, + @NonNull String owner, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, + @NonNull Supplier<Integer> currentUserId, + boolean hasEnrolledBiometrics, @NonNull Map<Integer, Long> authenticatorIds, + boolean forceUpdateAuthenticatorId) { + super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner, + 0 /* cookie */, sensorId, logger, biometricContext); + mCurrentUserId = currentUserId; + mForceUpdateAuthenticatorId = forceUpdateAuthenticatorId; + mHasEnrolledBiometrics = hasEnrolledBiometrics; + mAuthenticatorIds = authenticatorIds; + } + + @Override + public void start(@NonNull ClientMonitorCallback callback) { + super.start(callback); + + if (mCurrentUserId.get() == getTargetUserId() && !mForceUpdateAuthenticatorId) { + Slog.d(TAG, "Already user: " + mCurrentUserId + ", returning"); + callback.onClientFinished(this, true /* success */); + return; + } + + int firstSdkInt = Build.VERSION.DEVICE_INITIAL_SDK_INT; + if (firstSdkInt < Build.VERSION_CODES.BASE) { + Slog.e(TAG, "First SDK version " + firstSdkInt + " is invalid; must be " + + "at least VERSION_CODES.BASE"); + } + File baseDir; + if (firstSdkInt <= Build.VERSION_CODES.O_MR1) { + baseDir = Environment.getUserSystemDirectory(getTargetUserId()); + } else { + baseDir = Environment.getDataVendorDeDirectory(getTargetUserId()); + } + + mDirectory = new File(baseDir, FP_DATA_DIR); + if (!mDirectory.exists()) { + if (!mDirectory.mkdir()) { + Slog.e(TAG, "Cannot make directory: " + mDirectory.getAbsolutePath()); + callback.onClientFinished(this, false /* success */); + return; + } + // Calling mkdir() from this process will create a directory with our + // permissions (inherited from the containing dir). This command fixes + // the label. + if (!SELinux.restorecon(mDirectory)) { + Slog.e(TAG, "Restorecons failed. Directory will have wrong label."); + callback.onClientFinished(this, false /* success */); + return; + } + } + + startHalOperation(); + } + + @Override + public void unableToStart() { + // Nothing to do here + } + + @Override + protected void startHalOperation() { + try { + final int targetId = getTargetUserId(); + Slog.d(TAG, "Setting active user: " + targetId); + getFreshDaemon().setActiveGroup(targetId, mDirectory.getAbsolutePath()); + mAuthenticatorIds.put(targetId, mHasEnrolledBiometrics + ? getFreshDaemon().getAuthenticatorId() : 0L); + mCallback.onClientFinished(this, true /* success */); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to setActiveGroup: " + e); + mCallback.onClientFinished(this, false /* success */); + } + } + + @Override + public int getProtoEnum() { + return BiometricsProto.CM_UPDATE_ACTIVE_USER; + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java index 90da74ccaa1c..47fdcdb92a99 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.pm.UserInfo; +import android.hardware.biometrics.fingerprint.ISession; import android.hardware.biometrics.fingerprint.SensorProps; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; import android.os.Handler; @@ -39,7 +40,7 @@ import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.LockoutTracker; import com.android.server.biometrics.sensors.StartUserClient; import com.android.server.biometrics.sensors.StopUserClient; -import com.android.server.biometrics.sensors.UserAwareBiometricScheduler; +import com.android.server.biometrics.sensors.UserSwitchProvider; import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils; import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher; import com.android.server.biometrics.sensors.fingerprint.aidl.AidlResponseHandler; @@ -71,37 +72,33 @@ public class HidlToAidlSensorAdapter extends Sensor implements IHwBinder.DeathRe } }; - public HidlToAidlSensorAdapter(@NonNull String tag, @NonNull FingerprintProvider provider, - @NonNull Context context, @NonNull Handler handler, + public HidlToAidlSensorAdapter(@NonNull FingerprintProvider provider, + @NonNull Context context, + @NonNull Handler handler, @NonNull SensorProps prop, @NonNull LockoutResetDispatcher lockoutResetDispatcher, - @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, @NonNull BiometricContext biometricContext, boolean resetLockoutRequiresHardwareAuthToken, @NonNull Runnable internalCleanupRunnable) { - this(tag, provider, context, handler, prop, lockoutResetDispatcher, - gestureAvailabilityDispatcher, biometricContext, + this(provider, context, handler, prop, lockoutResetDispatcher, biometricContext, resetLockoutRequiresHardwareAuthToken, internalCleanupRunnable, new AuthSessionCoordinator(), null /* daemon */, null /* onEnrollSuccessCallback */); } @VisibleForTesting - HidlToAidlSensorAdapter(@NonNull String tag, @NonNull FingerprintProvider provider, + HidlToAidlSensorAdapter(@NonNull FingerprintProvider provider, @NonNull Context context, @NonNull Handler handler, @NonNull SensorProps prop, @NonNull LockoutResetDispatcher lockoutResetDispatcher, - @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, @NonNull BiometricContext biometricContext, boolean resetLockoutRequiresHardwareAuthToken, @NonNull Runnable internalCleanupRunnable, @NonNull AuthSessionCoordinator authSessionCoordinator, @Nullable IBiometricsFingerprint daemon, @Nullable AidlResponseHandler.AidlResponseHandlerCallback aidlResponseHandlerCallback) { - super(tag, provider, context, handler, getFingerprintSensorPropertiesInternal(prop, + super(provider, context, handler, getFingerprintSensorPropertiesInternal(prop, new ArrayList<>(), resetLockoutRequiresHardwareAuthToken), - lockoutResetDispatcher, - gestureAvailabilityDispatcher, biometricContext, null /* session */); mLockoutResetDispatcher = lockoutResetDispatcher; mInternalCleanupRunnable = internalCleanupRunnable; @@ -127,7 +124,7 @@ public class HidlToAidlSensorAdapter extends Sensor implements IHwBinder.DeathRe @Override public void serviceDied(long cookie) { - Slog.d(TAG, "HAL died."); + Slog.d(TAG, "Fingerprint HAL died."); mSession = null; mDaemon = null; } @@ -139,12 +136,12 @@ public class HidlToAidlSensorAdapter extends Sensor implements IHwBinder.DeathRe } @Override - public void init(GestureAvailabilityDispatcher gestureAvailabilityDispatcher, - LockoutResetDispatcher lockoutResetDispatcher) { + public void init(@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, + @NonNull LockoutResetDispatcher lockoutResetDispatcher) { setLazySession(this::getSession); - setScheduler(new UserAwareBiometricScheduler(TAG, + setScheduler(new BiometricScheduler<ISession, AidlSession>(getHandler(), BiometricScheduler.sensorTypeFromFingerprintProperties(getSensorProperties()), - gestureAvailabilityDispatcher, () -> mCurrentUserId, getUserSwitchCallback())); + gestureAvailabilityDispatcher, () -> mCurrentUserId, getUserSwitchProvider())); mLockoutTracker = new LockoutFrameworkImpl(getContext(), userId -> mLockoutResetDispatcher.notifyLockoutResetCallbacks( getSensorProperties().sensorId), getHandler()); @@ -152,6 +149,7 @@ public class HidlToAidlSensorAdapter extends Sensor implements IHwBinder.DeathRe @Override @Nullable + @VisibleForTesting protected AidlSession getSessionForUser(int userId) { if (mSession != null && mSession.getUserId() == userId) { return mSession; @@ -217,21 +215,18 @@ public class HidlToAidlSensorAdapter extends Sensor implements IHwBinder.DeathRe } mDaemon.asBinder().linkToDeath(this, 0 /* flags */); - - Slog.d(TAG, "Fingerprint HAL ready"); - scheduleLoadAuthenticatorIds(); mInternalCleanupRunnable.run(); return mDaemon; } - private UserAwareBiometricScheduler.UserSwitchCallback getUserSwitchCallback() { - return new UserAwareBiometricScheduler.UserSwitchCallback() { + private UserSwitchProvider<ISession, AidlSession> getUserSwitchProvider() { + return new UserSwitchProvider<>() { @NonNull @Override - public StopUserClient<?> getStopUserClient(int userId) { - return new StopUserClient<IBiometricsFingerprint>(getContext(), - HidlToAidlSensorAdapter.this::getIBiometricsFingerprint, + public StopUserClient<AidlSession> getStopUserClient(int userId) { + return new StopUserClient<>(getContext(), + HidlToAidlSensorAdapter.this::getSession, null /* token */, userId, getSensorProperties().sensorId, BiometricLogger.ofUnknown(getContext()), getBiometricContext(), () -> { @@ -258,7 +253,7 @@ public class HidlToAidlSensorAdapter extends Sensor implements IHwBinder.DeathRe @NonNull @Override - public StartUserClient<?, ?> getStartUserClient(int newUserId) { + public StartUserClient<ISession, AidlSession> getStartUserClient(int newUserId) { return getFingerprintUpdateActiveUserClient(newUserId, false /* forceUpdateAuthenticatorId */); } @@ -268,7 +263,7 @@ public class HidlToAidlSensorAdapter extends Sensor implements IHwBinder.DeathRe private FingerprintUpdateActiveUserClient getFingerprintUpdateActiveUserClient(int newUserId, boolean forceUpdateAuthenticatorIds) { return new FingerprintUpdateActiveUserClient(getContext(), - this::getIBiometricsFingerprint, newUserId, TAG, + () -> getSession().getSession(), newUserId, TAG, getSensorProperties().sensorId, BiometricLogger.ofUnknown(getContext()), getBiometricContext(), () -> mCurrentUserId, !FingerprintUtils.getInstance(getSensorProperties().sensorId) @@ -290,7 +285,7 @@ public class HidlToAidlSensorAdapter extends Sensor implements IHwBinder.DeathRe } @VisibleForTesting void handleUserChanged(int newUserId) { - Slog.d(TAG, "User changed. Current user is " + newUserId); + Slog.d(TAG, "User changed. Current user for fingerprint sensor is " + newUserId); mSession = null; mCurrentUserId = newUserId; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapter.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapter.java index 2fc00e126354..b469752d49cf 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapter.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapter.java @@ -209,6 +209,14 @@ public class HidlToAidlSessionAdapter implements ISession { return null; } + public long getAuthenticatorIdForUpdateClient() throws RemoteException { + return mSession.get().getAuthenticatorId(); + } + + public void setActiveGroup(int userId, String absolutePath) throws RemoteException { + mSession.get().setActiveGroup(userId, absolutePath); + } + private void setCallback(AidlResponseHandler aidlResponseHandler) { mHidlToAidlCallbackConverter = new HidlToAidlCallbackConverter(aidlResponseHandler); try { diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index fbac924be283..9cf9119aff82 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -3390,17 +3390,10 @@ public final class DisplayManagerService extends SystemService { // with the corresponding displaydevice. HighBrightnessModeMetadata hbmMetadata = mHighBrightnessModeMetadataMapper.getHighBrightnessModeMetadataLocked(display); - if (mConfigParameterProvider.isNewPowerControllerFeatureEnabled()) { - displayPowerController = new DisplayPowerController2( - mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler, - mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting, - () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted, mFlags); - } else { - displayPowerController = new DisplayPowerController( - mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler, - mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting, - () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted, mFlags); - } + displayPowerController = new DisplayPowerController( + mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler, + mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting, + () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted, mFlags); mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController); return displayPowerController; } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 734381b1ddb0..087cacf9a570 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,11 +17,12 @@ package com.android.server.display; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE; +import static com.android.server.display.config.DisplayBrightnessMappingConfig.autoBrightnessPresetToString; import android.animation.Animator; import android.animation.ObjectAnimator; -import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.UserIdInt; @@ -31,8 +32,6 @@ import android.content.pm.ParceledListSlice; import android.content.res.Resources; import android.database.ContentObserver; import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.display.AmbientBrightnessDayStats; import android.hardware.display.BrightnessChangeEvent; @@ -45,6 +44,7 @@ import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; import android.metrics.LogMaker; import android.net.Uri; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.IBinder; import android.os.Looper; import android.os.Message; @@ -56,12 +56,12 @@ import android.os.Trace; import android.os.UserHandle; import android.provider.Settings; import android.util.FloatProperty; +import android.util.IndentingPrintWriter; import android.util.MathUtils; import android.util.MutableFloat; import android.util.MutableInt; import android.util.Slog; import android.util.SparseArray; -import android.util.TimeUtils; import android.view.Display; import com.android.internal.R; @@ -78,10 +78,15 @@ import com.android.server.am.BatteryStatsService; import com.android.server.display.RampAnimator.DualRampAnimator; import com.android.server.display.brightness.BrightnessEvent; import com.android.server.display.brightness.BrightnessReason; +import com.android.server.display.brightness.BrightnessUtils; +import com.android.server.display.brightness.DisplayBrightnessController; +import com.android.server.display.brightness.clamper.BrightnessClamperController; +import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy; import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal; import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener; import com.android.server.display.feature.DisplayManagerFlags; import com.android.server.display.layout.Layout; +import com.android.server.display.state.DisplayStateController; import com.android.server.display.utils.DebugUtils; import com.android.server.display.utils.SensorUtils; import com.android.server.display.whitebalance.DisplayWhiteBalanceController; @@ -119,12 +124,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private static final String SCREEN_ON_BLOCKED_TRACE_NAME = "Screen on blocked"; private static final String SCREEN_OFF_BLOCKED_TRACE_NAME = "Screen off blocked"; - private static final String TAG = "DisplayPowerController"; + private static final String TAG = "DisplayPowerController2"; // To enable these logs, run: - // 'adb shell setprop persist.log.tag.DisplayPowerController DEBUG && adb reboot' + // 'adb shell setprop persist.log.tag.DisplayPowerController2 DEBUG && adb reboot' private static final boolean DEBUG = DebugUtils.isDebuggable(TAG); - - private static final boolean DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT = false; + private static final String SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME = + "Screen on blocked by displayoffload"; // If true, uses the color fade on animation. // We might want to turn this off if we cannot get a guarantee that the screen @@ -138,36 +143,28 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private static final int COLOR_FADE_OFF_ANIMATION_DURATION_MILLIS = 400; private static final int MSG_UPDATE_POWER_STATE = 1; - private static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 2; - private static final int MSG_SCREEN_ON_UNBLOCKED = 3; - private static final int MSG_SCREEN_OFF_UNBLOCKED = 4; - private static final int MSG_CONFIGURE_BRIGHTNESS = 5; - 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 MSG_UPDATE_BRIGHTNESS = 10; - private static final int MSG_UPDATE_RBC = 11; - private static final int MSG_BRIGHTNESS_RAMP_DONE = 12; - private static final int MSG_STATSD_HBM_BRIGHTNESS = 13; - private static final int MSG_SWITCH_USER = 14; - private static final int MSG_BOOT_COMPLETED = 15; - private static final int MSG_SET_DWBC_STRONG_MODE = 16; - private static final int MSG_SET_DWBC_COLOR_OVERRIDE = 17; - private static final int MSG_SET_DWBC_LOGGING_ENABLED = 18; - - private static final int PROXIMITY_UNKNOWN = -1; - private static final int PROXIMITY_NEGATIVE = 0; - private static final int PROXIMITY_POSITIVE = 1; - - // Proximity sensor debounce delay in milliseconds for positive or negative transitions. - private static final int PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY = 0; - private static final int PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY = 250; + private static final int MSG_SCREEN_ON_UNBLOCKED = 2; + private static final int MSG_SCREEN_OFF_UNBLOCKED = 3; + private static final int MSG_CONFIGURE_BRIGHTNESS = 4; + private static final int MSG_SET_TEMPORARY_BRIGHTNESS = 5; + private static final int MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT = 6; + private static final int MSG_STOP = 7; + private static final int MSG_UPDATE_BRIGHTNESS = 8; + private static final int MSG_UPDATE_RBC = 9; + private static final int MSG_BRIGHTNESS_RAMP_DONE = 10; + private static final int MSG_STATSD_HBM_BRIGHTNESS = 11; + private static final int MSG_SWITCH_USER = 12; + private static final int MSG_BOOT_COMPLETED = 13; + private static final int MSG_SET_DWBC_STRONG_MODE = 14; + private static final int MSG_SET_DWBC_COLOR_OVERRIDE = 15; + private static final int MSG_SET_DWBC_LOGGING_ENABLED = 16; + private static final int MSG_SET_BRIGHTNESS_FROM_OFFLOAD = 17; + private static final int MSG_OFFLOADING_SCREEN_ON_UNBLOCKED = 18; + + private static final int BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS = 500; - // Trigger proximity if distance is less than 5 cm. - private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f; // State machine constants for tracking initial brightness ramp skipping when enabled. private static final int RAMP_STATE_SKIP_NONE = 0; @@ -181,6 +178,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private static final int REPORTED_TO_POLICY_SCREEN_TURNING_OFF = 3; private static final int RINGBUFFER_MAX = 100; + private static final int RINGBUFFER_RBC_MAX = 20; private static final float[] BRIGHTNESS_RANGE_BOUNDARIES = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80, @@ -236,10 +234,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Our handler. private final DisplayControllerHandler mHandler; - // Asynchronous callbacks into the power manager service. - // Only invoked from the handler thread while no locks are held. - private final DisplayPowerCallbacks mCallbacks; - // Battery stats. @Nullable private final IBatteryStats mBatteryStats; @@ -253,10 +247,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // The display blanker. private final DisplayBlanker mBlanker; - // The LogicalDisplay tied to this DisplayPowerController. + // The LogicalDisplay tied to this DisplayPowerController2. private final LogicalDisplay mLogicalDisplay; - // The ID of the LogicalDisplay tied to this DisplayPowerController. + // The ID of the LogicalDisplay tied to this DisplayPowerController2. private final int mDisplayId; // The ID of the display which this display follows for brightness purposes. @@ -272,34 +266,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Tracker for brightness settings changes. private final SettingsObserver mSettingsObserver; - // The proximity sensor, or null if not available or needed. - private Sensor mProximitySensor; - // The doze screen brightness. private final float mScreenBrightnessDozeConfig; - // The dim screen brightness. - private final float mScreenBrightnessDimConfig; - - // The minimum dim amount to use if the screen brightness is already below - // mScreenBrightnessDimConfig. - private final float mScreenBrightnessMinimumDimAmount; - - private final float mScreenBrightnessDefault; - // True if auto-brightness should be used. private boolean mUseSoftwareAutoBrightnessConfig; - // True if should use light sensor to automatically determine doze screen brightness. - private final boolean mAllowAutoBrightnessWhileDozingConfig; - - // True if we want to persist the brightness value in nits even if the underlying display - // device changes. - private final boolean mPersistBrightnessNitsForDefaultDisplay; - - // True if the brightness config has changed and the short-term model needs to be reset - private boolean mShouldResetShortTermModel; - // Whether or not the color fade on screen on / off is enabled. private final boolean mColorFadeEnabled; @@ -340,10 +312,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call @GuardedBy("mLock") private DisplayPowerRequest mPendingRequestLocked; - // True if a request has been made to wait for the proximity sensor to go negative. - @GuardedBy("mLock") - private boolean mPendingWaitForNegativeProximityLocked; - // True if the pending power request or wait for negative proximity flag // has been changed since the last update occurred. @GuardedBy("mLock") @@ -370,67 +338,36 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Must only be accessed on the handler thread. private DisplayPowerState mPowerState; - // True if the device should wait for negative proximity sensor before - // waking up the screen. This is set to false as soon as a negative - // proximity sensor measurement is observed or when the device is forced to - // go to sleep by the user. While true, the screen remains off. - private boolean mWaitingForNegativeProximity; - - // True if the device should not take into account the proximity sensor - // until either the proximity sensor state changes, or there is no longer a - // request to listen to proximity sensor. - private boolean mIgnoreProximityUntilChanged; - - // The actual proximity sensor threshold value. - private float mProximityThreshold; - // Set to true if the proximity sensor listener has been registered - // with the sensor manager. - private boolean mProximitySensorEnabled; - - // The debounced proximity sensor state. - private int mProximity = PROXIMITY_UNKNOWN; - - // The raw non-debounced proximity sensor state. - private int mPendingProximity = PROXIMITY_UNKNOWN; - private long mPendingProximityDebounceTime = -1; // -1 if fully debounced - - // True if the screen was turned off because of the proximity sensor. - // When the screen turns on again, we report user activity to the power manager. - private boolean mScreenOffBecauseOfProximity; // The currently active screen on unblocker. This field is non-null whenever // we are waiting for a callback to release it and unblock the screen. private ScreenOnUnblocker mPendingScreenOnUnblocker; private ScreenOffUnblocker mPendingScreenOffUnblocker; + private Runnable mPendingScreenOnUnblockerByDisplayOffload; // True if we were in the process of turning off the screen. // This allows us to recover more gracefully from situations where we abort // turning off the screen. private boolean mPendingScreenOff; - // True if we have unfinished business and are holding a suspend blocker. - private boolean mUnfinishedBusiness; - // The elapsed real time when the screen on was blocked. private long mScreenOnBlockStartRealTime; private long mScreenOffBlockStartRealTime; + private long mScreenOnBlockByDisplayOffloadStartRealTime; // Screen state we reported to policy. Must be one of REPORTED_TO_POLICY_* fields. private int mReportedScreenStateToPolicy = REPORTED_TO_POLICY_UNREPORTED; + // Used to deduplicate the displayoffload blocking screen on logic. One block per turning on. + // This value is reset when screen on is reported or the blocking is cancelled. + private boolean mScreenTurningOnWasBlockedByDisplayOffload; + // If the last recorded screen state was dozing or not. private boolean mDozing; - // Remembers whether certain kinds of brightness adjustments - // were recently applied so that we can decide how to transition. - private boolean mAppliedAutoBrightness; private boolean mAppliedDimming; - private boolean mAppliedLowPower; - private boolean mAppliedScreenBrightnessOverride; - private boolean mAppliedTemporaryBrightness; - private boolean mAppliedTemporaryAutoBrightnessAdjustment; - private boolean mAppliedBrightnessBoost; + private boolean mAppliedThrottling; // Reason for which the brightness was last changed. See {@link BrightnessReason} for more @@ -456,7 +393,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private final boolean mSkipScreenOnBrightnessRamp; // Display white balance components. - // Critical methods must be called on DPC handler thread. + // Critical methods must be called on DPC2 handler thread. @Nullable private final DisplayWhiteBalanceSettings mDisplayWhiteBalanceSettings; @Nullable @@ -468,21 +405,39 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private final BrightnessRangeController mBrightnessRangeController; - @Nullable - private final HighBrightnessModeMetadata mHighBrightnessModeMetadata; - private final BrightnessThrottler mBrightnessThrottler; - private final BrightnessSetting mBrightnessSetting; + private final BrightnessClamperController mBrightnessClamperController; private final Runnable mOnBrightnessChangeRunnable; private final BrightnessEvent mLastBrightnessEvent; private final BrightnessEvent mTempBrightnessEvent; + private final DisplayBrightnessController mDisplayBrightnessController; + // Keeps a record of brightness changes for dumpsys. private RingBuffer<BrightnessEvent> mBrightnessEventRingBuffer; + // Keeps a record of rbc changes for dumpsys. + private final RingBuffer<BrightnessEvent> mRbcEventRingBuffer = + new RingBuffer<>(BrightnessEvent.class, RINGBUFFER_RBC_MAX); + + // Controls and tracks all the wakelocks that are acquired/released by the system. Also acts as + // a medium of communication between this class and the PowerManagerService. + private final WakelockController mWakelockController; + + // Tracks and manages the proximity state of the associated display. + private final DisplayPowerProximityStateController mDisplayPowerProximityStateController; + + // Tracks and manages the display state of the associated display. + private final DisplayStateController mDisplayStateController; + + + // Responsible for evaluating and tracking the automatic brightness relevant states. + // Todo: This is a temporary workaround. Ideally DPC2 should never talk to the strategies + private final AutomaticBrightnessStrategy mAutomaticBrightnessStrategy; + // A record of state for skipping brightness ramps. private int mSkipRampState = RAMP_STATE_SKIP_NONE; @@ -500,81 +455,18 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private Sensor mLightSensor; private Sensor mScreenOffBrightnessSensor; - // The current brightness configuration. - @Nullable - private BrightnessConfiguration mBrightnessConfiguration; - - // The last brightness that was set by the user and not temporary. Set to - // PowerManager.BRIGHTNESS_INVALID_FLOAT when a brightness has yet to be recorded. - private float mLastUserSetScreenBrightness = Float.NaN; - - // The screen brightness setting has changed but not taken effect yet. If this is different - // from the current screen brightness setting then this is coming from something other than us - // and should be considered a user interaction. - private float mPendingScreenBrightnessSetting; - - // The last observed screen brightness setting, either set by us or by the settings app on - // behalf of the user. - private float mCurrentScreenBrightnessSetting; - - // The temporary screen brightness. Typically set when a user is interacting with the - // brightness slider but hasn't settled on a choice yet. Set to - // PowerManager.BRIGHTNESS_INVALID_FLOAT when there's no temporary brightness set. - private float mTemporaryScreenBrightness; - - // This brightness value is set in concurrent displays mode. It is the brightness value - // of the lead display that this DPC should follow. - private float mBrightnessToFollow; - - // Indicates whether we should ramp slowly to the brightness value to follow. - private boolean mBrightnessToFollowSlowChange; - - // The last auto brightness adjustment that was set by the user and not temporary. Set to - // Float.NaN when an auto-brightness adjustment hasn't been recorded yet. - private float mAutoBrightnessAdjustment; - - // The pending auto brightness adjustment that will take effect on the next power state update. - private float mPendingAutoBrightnessAdjustment; - - // The temporary auto brightness adjustment. Typically set when a user is interacting with the - // adjustment slider but hasn't settled on a choice yet. Set to - // PowerManager.BRIGHTNESS_INVALID_FLOAT when there's no temporary adjustment set. - private float mTemporaryAutoBrightnessAdjustment; - - private boolean mUseAutoBrightness; - private boolean mIsRbcActive; - // Whether there's a callback to tell listeners the display has changed scheduled to run. When - // true it implies a wakelock is being held to guarantee the update happens before we collapse - // into suspend and so needs to be cleaned up if the thread is exiting. - // Should only be accessed on the Handler thread. - private boolean mOnStateChangedPending; - - // Count of proximity messages currently on this DPC's Handler. Used to keep track of how many - // suspend blocker acquisitions are pending when shutting down this DPC. - // Should only be accessed on the Handler thread. - private int mOnProximityPositiveMessages; - private int mOnProximityNegativeMessages; - // Animators. private ObjectAnimator mColorFadeOnAnimator; private ObjectAnimator mColorFadeOffAnimator; private DualRampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator; - private BrightnessSetting.BrightnessSettingListener mBrightnessSettingListener; - // True if this DisplayPowerController has been stopped and should no longer be running. + // True if this DisplayPowerController2 has been stopped and should no longer be running. private boolean mStopped; private DisplayDeviceConfig mDisplayDeviceConfig; - // Identifiers for suspend blocker acquisition requests - private final String mSuspendBlockerIdUnfinishedBusiness; - private final String mSuspendBlockerIdOnStateChanged; - private final String mSuspendBlockerIdProxPositive; - private final String mSuspendBlockerIdProxNegative; - private final String mSuspendBlockerIdProxDebounce; - private boolean mIsEnabled; private boolean mIsInTransition; private boolean mIsDisplayInternal; @@ -585,13 +477,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // DPCs following the brightness of this DPC. This is used in concurrent displays mode - there // is one lead display, the additional displays follow the brightness value of the lead display. @GuardedBy("mLock") - private final SparseArray<DisplayPowerControllerInterface> mDisplayBrightnessFollowers = - new SparseArray<>(); + private SparseArray<DisplayPowerControllerInterface> mDisplayBrightnessFollowers = + new SparseArray(); private boolean mBootCompleted; private final DisplayManagerFlags mFlags; - private int mDozeStateOverride = Display.STATE_UNKNOWN; - private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession; + + private DisplayOffloadSession mDisplayOffloadSession; /** * Creates the display power controller. @@ -607,26 +499,28 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mClock = mInjector.getClock(); mLogicalDisplay = logicalDisplay; mDisplayId = mLogicalDisplay.getDisplayIdLocked(); - mTag = TAG + "[" + mDisplayId + "]"; - mHighBrightnessModeMetadata = hbmMetadata; - mSuspendBlockerIdUnfinishedBusiness = getSuspendBlockerUnfinishedBusinessId(mDisplayId); - mSuspendBlockerIdOnStateChanged = getSuspendBlockerOnStateChangedId(mDisplayId); - mSuspendBlockerIdProxPositive = getSuspendBlockerProxPositiveId(mDisplayId); - mSuspendBlockerIdProxNegative = getSuspendBlockerProxNegativeId(mDisplayId); - mSuspendBlockerIdProxDebounce = getSuspendBlockerProxDebounceId(mDisplayId); - - mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked(); - mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId(); - mDisplayStatsId = mUniqueDisplayId.hashCode(); + mSensorManager = sensorManager; + mHandler = new DisplayControllerHandler(handler.getLooper()); + mDisplayDeviceConfig = logicalDisplay.getPrimaryDisplayDeviceLocked() + .getDisplayDeviceConfig(); mIsEnabled = logicalDisplay.isEnabledLocked(); mIsInTransition = logicalDisplay.isInTransitionLocked(); mIsDisplayInternal = logicalDisplay.getPrimaryDisplayDeviceLocked() .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL; - mHandler = new DisplayControllerHandler(handler.getLooper()); - mLastBrightnessEvent = new BrightnessEvent(mDisplayId); - mTempBrightnessEvent = new BrightnessEvent(mDisplayId); + mWakelockController = mInjector.getWakelockController(mDisplayId, callbacks); + mDisplayPowerProximityStateController = mInjector.getDisplayPowerProximityStateController( + mWakelockController, mDisplayDeviceConfig, mHandler.getLooper(), + () -> updatePowerState(), mDisplayId, mSensorManager); + mDisplayStateController = new DisplayStateController(mDisplayPowerProximityStateController); + mTag = TAG + "[" + mDisplayId + "]"; mThermalBrightnessThrottlingDataId = logicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId; + mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked(); + mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId(); + mDisplayStatsId = mUniqueDisplayId.hashCode(); + + mLastBrightnessEvent = new BrightnessEvent(mDisplayId); + mTempBrightnessEvent = new BrightnessEvent(mDisplayId); if (mDisplayId == Display.DEFAULT_DISPLAY) { mBatteryStats = BatteryStatsService.getService(); @@ -635,14 +529,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } mSettingsObserver = new SettingsObserver(mHandler); - mCallbacks = callbacks; - mSensorManager = sensorManager; mWindowManagerPolicy = LocalServices.getService(WindowManagerPolicy.class); mBlanker = blanker; mContext = context; mBrightnessTracker = brightnessTracker; - // TODO: b/186428377 update brightness setting when display changes - mBrightnessSetting = brightnessSetting; mOnBrightnessChangeRunnable = onBrightnessChangeRunnable; PowerManager pm = context.getSystemService(PowerManager.class); @@ -650,30 +540,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call final Resources resources = context.getResources(); // DOZE AND DIM SETTINGS - mScreenBrightnessDozeConfig = clampAbsoluteBrightness( + mScreenBrightnessDozeConfig = BrightnessUtils.clampAbsoluteBrightness( pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DOZE)); - mScreenBrightnessDimConfig = clampAbsoluteBrightness( - pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM)); - mScreenBrightnessMinimumDimAmount = resources.getFloat( - com.android.internal.R.dimen.config_screenBrightnessMinimumDimAmountFloat); - - - // NORMAL SCREEN SETTINGS - mScreenBrightnessDefault = clampAbsoluteBrightness( - mLogicalDisplay.getDisplayInfoLocked().brightnessDefault); - - mAllowAutoBrightnessWhileDozingConfig = resources.getBoolean( - com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing); - - mPersistBrightnessNitsForDefaultDisplay = resources.getBoolean( - com.android.internal.R.bool.config_persistBrightnessNitsForDefaultDisplay); - - mDisplayDeviceConfig = logicalDisplay.getPrimaryDisplayDeviceLocked() - .getDisplayDeviceConfig(); - loadBrightnessRampRates(); mSkipScreenOnBrightnessRamp = resources.getBoolean( - com.android.internal.R.bool.config_skipScreenOnBrightnessRamp); + R.bool.config_skipScreenOnBrightnessRamp); + Runnable modeChangeCallback = () -> { sendUpdatePowerState(); postBrightnessChangeRunnable(); @@ -683,23 +555,38 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } }; - HighBrightnessModeController hbmController = createHbmControllerLocked(modeChangeCallback); + HighBrightnessModeController hbmController = createHbmControllerLocked(hbmMetadata, + modeChangeCallback); + mBrightnessThrottler = createBrightnessThrottlerLocked(); - mBrightnessRangeController = new BrightnessRangeController(hbmController, + mBrightnessRangeController = mInjector.getBrightnessRangeController(hbmController, modeChangeCallback, mDisplayDeviceConfig, mHandler, flags, mDisplayDevice.getDisplayTokenLocked(), mDisplayDevice.getDisplayDeviceInfoLocked()); - mBrightnessThrottler = createBrightnessThrottlerLocked(); + mDisplayBrightnessController = + new DisplayBrightnessController(context, null, + mDisplayId, mLogicalDisplay.getDisplayInfoLocked().brightnessDefault, + brightnessSetting, () -> postBrightnessChangeRunnable(), + new HandlerExecutor(mHandler), flags); + mBrightnessClamperController = mInjector.getBrightnessClamperController( + mHandler, modeChangeCallback::run, + new BrightnessClamperController.DisplayDeviceData( + mUniqueDisplayId, + mThermalBrightnessThrottlingDataId, + logicalDisplay.getPowerThrottlingDataIdLocked(), + mDisplayDeviceConfig), mContext, flags); // Seed the cached brightness saveBrightnessInfo(getScreenBrightnessSetting()); + mAutomaticBrightnessStrategy = + mDisplayBrightnessController.getAutomaticBrightnessStrategy(); DisplayWhiteBalanceSettings displayWhiteBalanceSettings = null; DisplayWhiteBalanceController displayWhiteBalanceController = null; if (mDisplayId == Display.DEFAULT_DISPLAY) { try { - displayWhiteBalanceController = injector.getDisplayWhiteBalanceController( + displayWhiteBalanceController = mInjector.getDisplayWhiteBalanceController( mHandler, mSensorManager, resources); displayWhiteBalanceSettings = new DisplayWhiteBalanceSettings(mContext, mHandler); displayWhiteBalanceSettings.setCallbacks(this); @@ -715,21 +602,24 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (mDisplayId == Display.DEFAULT_DISPLAY) { mCdsi = LocalServices.getService(ColorDisplayServiceInternal.class); - boolean active = mCdsi.setReduceBrightColorsListener(new ReduceBrightColorsListener() { - @Override - public void onReduceBrightColorsActivationChanged(boolean activated, - boolean userInitiated) { - applyReduceBrightColorsSplineAdjustment(); - - } - - @Override - public void onReduceBrightColorsStrengthChanged(int strength) { + if (mCdsi != null) { + boolean active = mCdsi.setReduceBrightColorsListener( + new ReduceBrightColorsListener() { + @Override + public void onReduceBrightColorsActivationChanged(boolean activated, + boolean userInitiated) { + applyReduceBrightColorsSplineAdjustment(); + + } + + @Override + public void onReduceBrightColorsStrengthChanged(int strength) { + applyReduceBrightColorsSplineAdjustment(); + } + }); + if (active) { applyReduceBrightColorsSplineAdjustment(); } - }); - if (active) { - applyReduceBrightColorsSplineAdjustment(); } } else { mCdsi = null; @@ -737,27 +627,17 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call setUpAutoBrightness(context, handler); - mColorFadeEnabled = !ActivityManager.isLowRamDeviceStatic() + mColorFadeEnabled = mInjector.isColorFadeEnabled() && !resources.getBoolean( com.android.internal.R.bool.config_displayColorFadeDisabled); mColorFadeFadesConfig = resources.getBoolean( - com.android.internal.R.bool.config_animateScreenLights); + R.bool.config_animateScreenLights); mDisplayBlanksAfterDozeConfig = resources.getBoolean( - com.android.internal.R.bool.config_displayBlanksAfterDoze); + R.bool.config_displayBlanksAfterDoze); mBrightnessBucketsInDozeConfig = resources.getBoolean( - com.android.internal.R.bool.config_displayBrightnessBucketsInDoze); - - loadProximitySensor(); - - loadNitBasedBrightnessSetting(); - mBrightnessToFollow = PowerManager.BRIGHTNESS_INVALID_FLOAT; - mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting(); - mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; - mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT; - mTemporaryAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT; - mPendingAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT; + R.bool.config_displayBrightnessBucketsInDoze); mBootCompleted = bootCompleted; } @@ -785,7 +665,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call */ @Override public boolean isProximitySensorAvailable() { - return mProximitySensor != null; + return mDisplayPowerProximityStateController.isProximitySensorAvailable(); } /** @@ -818,64 +698,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } - @Override - public int getDisplayId() { - return mDisplayId; - } - - @Override - public int getLeadDisplayId() { - return mLeadDisplayId; - } - - @Override - public void setBrightnessToFollow(float leadDisplayBrightness, float nits, float ambientLux, - boolean slowChange) { - mBrightnessRangeController.onAmbientLuxChange(ambientLux); - if (nits == BrightnessMappingStrategy.INVALID_NITS) { - mBrightnessToFollow = leadDisplayBrightness; - } else { - float brightness = getBrightnessFromNits(nits); - if (isValidBrightnessValue(brightness)) { - mBrightnessToFollow = brightness; - } else { - // The device does not support nits - mBrightnessToFollow = leadDisplayBrightness; - } - } - mBrightnessToFollowSlowChange = slowChange; - sendUpdatePowerState(); - } - - @Override - public void addDisplayBrightnessFollower(@NonNull DisplayPowerControllerInterface follower) { - synchronized (mLock) { - mDisplayBrightnessFollowers.append(follower.getDisplayId(), follower); - sendUpdatePowerStateLocked(); - } - } - - @Override - public void removeDisplayBrightnessFollower(@NonNull DisplayPowerControllerInterface follower) { - synchronized (mLock) { - mDisplayBrightnessFollowers.remove(follower.getDisplayId()); - mHandler.postAtTime(() -> follower.setBrightnessToFollow( - PowerManager.BRIGHTNESS_INVALID_FLOAT, BrightnessMappingStrategy.INVALID_NITS, - /* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis()); - } - } - - @GuardedBy("mLock") - private void clearDisplayBrightnessFollowersLocked() { - for (int i = 0; i < mDisplayBrightnessFollowers.size(); i++) { - DisplayPowerControllerInterface follower = mDisplayBrightnessFollowers.valueAt(i); - mHandler.postAtTime(() -> follower.setBrightnessToFollow( - PowerManager.BRIGHTNESS_INVALID_FLOAT, BrightnessMappingStrategy.INVALID_NITS, - /* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis()); - } - mDisplayBrightnessFollowers.clear(); - } - @Nullable @Override public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats( @@ -923,13 +745,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call return true; } - boolean changed = false; - - if (waitForNegativeProximity - && !mPendingWaitForNegativeProximityLocked) { - mPendingWaitForNegativeProximityLocked = true; - changed = true; - } + boolean changed = mDisplayPowerProximityStateController + .setPendingWaitForNegativeProximityLocked(waitForNegativeProximity); if (mPendingRequestLocked == null) { mPendingRequestLocked = new DisplayPowerRequest(request); @@ -953,18 +770,23 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call @Override public void overrideDozeScreenState(int displayState) { - synchronized (mLock) { - if (mDisplayOffloadSession == null || - !DisplayOffloadSession.isSupportedOffloadState(displayState)) { + mHandler.postAtTime(() -> { + if (mDisplayOffloadSession == null + || !(DisplayOffloadSession.isSupportedOffloadState(displayState) + || displayState == Display.STATE_UNKNOWN)) { return; } - mDozeStateOverride = displayState; + mDisplayStateController.overrideDozeScreenState(displayState); sendUpdatePowerState(); - } + }, mClock.uptimeMillis()); } @Override public void setDisplayOffloadSession(DisplayOffloadSession session) { + if (session == mDisplayOffloadSession) { + return; + } + unblockScreenOnByDisplayOffload(); mDisplayOffloadSession = session; } @@ -981,14 +803,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call * when displays get swapped on foldable devices. For example, different brightness properties * of each display need to be properly reflected in AutomaticBrightnessController. * - * Make sure DisplayManagerService.mSyncRoot is held when this is called + * Make sure DisplayManagerService.mSyncRoot lock is held when this is called */ @Override public void onDisplayChanged(HighBrightnessModeMetadata hbmMetadata, int leadDisplayId) { mLeadDisplayId = leadDisplayId; final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked(); if (device == null) { - Slog.wtf(mTag, "Display Device is null in DisplayPowerController for display: " + Slog.wtf(mTag, "Display Device is null in DisplayPowerController2 for display: " + mLogicalDisplay.getDisplayIdLocked()); return; } @@ -1004,6 +826,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL; final String thermalBrightnessThrottlingDataId = mLogicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId; + final String powerThrottlingDataId = + mLogicalDisplay.getPowerThrottlingDataIdLocked(); + mHandler.postAtTime(() -> { boolean changed = false; if (mDisplayDevice != device) { @@ -1014,9 +839,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mDisplayDeviceConfig = config; mThermalBrightnessThrottlingDataId = thermalBrightnessThrottlingDataId; loadFromDisplayDeviceConfig(token, info, hbmMetadata); - loadNitBasedBrightnessSetting(); + mDisplayPowerProximityStateController.notifyDisplayDeviceChanged(config); - /// Since the underlying display-device changed, we really don't know the + // Since the underlying display-device changed, we really don't know the // last command that was sent to change it's state. Let's assume it is unknown so // that we trigger a change immediately. mPowerState.resetScreenState(); @@ -1034,7 +859,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mIsEnabled = isEnabled; mIsInTransition = isInTransition; } + mIsDisplayInternal = isDisplayInternal; + // using local variables here, when mBrightnessThrottler is removed, + // mThermalBrightnessThrottlingDataId could be removed as well + // changed = true will be not needed - clampers are maintaining their state and + // will call updatePowerState if needed. + mBrightnessClamperController.onDisplayChanged( + new BrightnessClamperController.DisplayDeviceData(uniqueId, + thermalBrightnessThrottlingDataId, powerThrottlingDataId, config)); + if (changed) { updatePowerState(); } @@ -1044,7 +878,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call /** * 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 + * This method should be called when the DisplayPowerController2 is no longer in use; i.e. when * the {@link #mDisplayId display} has been removed. */ @Override @@ -1060,9 +894,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mAutomaticBrightnessController.stop(); } - if (mBrightnessSetting != null) { - mBrightnessSetting.unregisterListener(mBrightnessSettingListener); - } + mDisplayBrightnessController.stop(); mContext.getContentResolver().unregisterContentObserver(mSettingsObserver); } @@ -1073,11 +905,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // All properties that depend on the associated DisplayDevice and the DDC must be // updated here. loadBrightnessRampRates(); - loadProximitySensor(); loadNitsRange(mContext.getResources()); setUpAutoBrightness(mContext, mHandler); reloadReduceBrightColours(); setAnimatorRampSpeeds(/* isIdleMode= */ false); + mBrightnessRangeController.loadFromConfig(hbmMetadata, token, info, mDisplayDeviceConfig); mBrightnessThrottler.loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig( mDisplayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId(), @@ -1126,22 +958,30 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call noteScreenBrightness(mPowerState.getScreenBrightness()); // Initialize all of the brightness tracking state - final float brightness = convertToAdjustedNits(mPowerState.getScreenBrightness()); + final float brightness = mDisplayBrightnessController.convertToAdjustedNits( + mPowerState.getScreenBrightness()); if (mBrightnessTracker != null && brightness >= PowerManager.BRIGHTNESS_MIN) { mBrightnessTracker.start(brightness); } - mBrightnessSettingListener = brightnessValue -> { + + BrightnessSetting.BrightnessSettingListener brightnessSettingListener = brightnessValue -> { Message msg = mHandler.obtainMessage(MSG_UPDATE_BRIGHTNESS, brightnessValue); mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); }; + mDisplayBrightnessController + .registerBrightnessSettingChangeListener(brightnessSettingListener); - mBrightnessSetting.registerListener(mBrightnessSettingListener); mContext.getContentResolver().registerContentObserver( Settings.System.getUriFor(Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ), false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL); mContext.getContentResolver().registerContentObserver( Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE), false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL); + if (mFlags.areAutoBrightnessModesEnabled()) { + mContext.getContentResolver().registerContentObserver( + Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_ALS), + /* notifyForDescendants= */ false, mSettingsObserver, UserHandle.USER_CURRENT); + } handleBrightnessModeChange(); } @@ -1165,12 +1005,21 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (isIdleScreenBrightnessEnabled) { BrightnessMappingStrategy idleModeBrightnessMapper = BrightnessMappingStrategy.create(context, mDisplayDeviceConfig, - AUTO_BRIGHTNESS_MODE_IDLE, mDisplayWhiteBalanceController); + AUTO_BRIGHTNESS_MODE_IDLE, + mDisplayWhiteBalanceController); if (idleModeBrightnessMapper != null) { - brightnessMappers.append(AUTO_BRIGHTNESS_MODE_IDLE, idleModeBrightnessMapper); + brightnessMappers.append(AUTO_BRIGHTNESS_MODE_IDLE, + idleModeBrightnessMapper); } } + BrightnessMappingStrategy dozeModeBrightnessMapper = + BrightnessMappingStrategy.create(context, mDisplayDeviceConfig, + AUTO_BRIGHTNESS_MODE_DOZE, mDisplayWhiteBalanceController); + if (mFlags.areAutoBrightnessModesEnabled() && dozeModeBrightnessMapper != null) { + brightnessMappers.put(AUTO_BRIGHTNESS_MODE_DOZE, dozeModeBrightnessMapper); + } + float userLux = BrightnessMappingStrategy.INVALID_LUX; float userNits = BrightnessMappingStrategy.INVALID_NITS; if (mAutomaticBrightnessController != null) { @@ -1180,7 +1029,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (defaultModeBrightnessMapper != null) { final float dozeScaleFactor = context.getResources().getFraction( - com.android.internal.R.fraction.config_screenAutoBrightnessDozeScaleFactor, + R.fraction.config_screenAutoBrightnessDozeScaleFactor, 1, 1); // Ambient Lux - Active Mode Brightness Thresholds @@ -1264,14 +1113,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call long darkeningLightDebounceIdle = mDisplayDeviceConfig .getAutoBrightnessDarkeningLightDebounceIdle(); boolean autoBrightnessResetAmbientLuxAfterWarmUp = context.getResources().getBoolean( - com.android.internal.R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp); + R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp); int lightSensorWarmUpTimeConfig = context.getResources().getInteger( - com.android.internal.R.integer.config_lightSensorWarmupTime); + R.integer.config_lightSensorWarmupTime); int lightSensorRate = context.getResources().getInteger( - com.android.internal.R.integer.config_autoBrightnessLightSensorRate); + R.integer.config_autoBrightnessLightSensorRate); int initialLightSensorRate = context.getResources().getInteger( - com.android.internal.R.integer.config_autoBrightnessInitialLightSensorRate); + R.integer.config_autoBrightnessInitialLightSensorRate); if (initialLightSensorRate == -1) { initialLightSensorRate = lightSensorRate; } else if (initialLightSensorRate > lightSensorRate) { @@ -1301,7 +1150,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call screenBrightnessThresholdsIdle, mContext, mBrightnessRangeController, mBrightnessThrottler, mDisplayDeviceConfig.getAmbientHorizonShort(), mDisplayDeviceConfig.getAmbientHorizonLong(), userLux, userNits); + mDisplayBrightnessController.setAutomaticBrightnessController( + mAutomaticBrightnessController); + mAutomaticBrightnessStrategy + .setAutomaticBrightnessController(mAutomaticBrightnessController); mBrightnessEventRingBuffer = new RingBuffer<>(BrightnessEvent.class, RINGBUFFER_MAX); @@ -1351,7 +1204,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } else { Slog.w(mTag, "Screen brightness nits configuration is unavailable; falling back"); mNitsRange = BrightnessMappingStrategy.getFloatArray(resources - .obtainTypedArray(com.android.internal.R.array.config_screenBrightnessNits)); + .obtainTypedArray(R.array.config_screenBrightnessNits)); } } @@ -1369,7 +1222,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mAutomaticBrightnessController.switchMode(mode); setAnimatorRampSpeeds(isIdle); } - Message msg = mHandler.obtainMessage(); msg.what = MSG_SET_DWBC_STRONG_MODE; msg.arg1 = isIdle ? 1 : 0; @@ -1421,28 +1273,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call /** Clean up all resources that are accessed via the {@link #mHandler} thread. */ private void cleanupHandlerThreadAfterStop() { - setProximitySensorEnabled(false); + mDisplayPowerProximityStateController.cleanup(); mBrightnessRangeController.stop(); mBrightnessThrottler.stop(); + mBrightnessClamperController.stop(); mHandler.removeCallbacksAndMessages(null); // Release any outstanding wakelocks we're still holding because of pending messages. - if (mUnfinishedBusiness) { - mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdUnfinishedBusiness); - mUnfinishedBusiness = false; - } - if (mOnStateChangedPending) { - mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdOnStateChanged); - mOnStateChangedPending = false; - } - for (int i = 0; i < mOnProximityPositiveMessages; i++) { - mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdProxPositive); - } - mOnProximityPositiveMessages = 0; - for (int i = 0; i < mOnProximityNegativeMessages; i++) { - mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdProxNegative); - } - mOnProximityNegativeMessages = 0; + mWakelockController.releaseAll(); final float brightness = mPowerState != null ? mPowerState.getScreenBrightness() @@ -1457,10 +1295,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (mScreenOffBrightnessSensorController != null) { mScreenOffBrightnessSensorController.stop(); } + if (mDisplayWhiteBalanceController != null) { mDisplayWhiteBalanceController.setEnabled(false); } - } // Call from handler thread @@ -1476,7 +1314,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call final boolean mustNotify; final int previousPolicy; boolean mustInitialize = false; - int brightnessAdjustmentFlags = 0; mBrightnessReasonTemp.set(null); mTempBrightnessEvent.reset(); SparseArray<DisplayPowerControllerInterface> displayBrightnessFollowers; @@ -1491,7 +1328,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (mPowerRequest == null) { mPowerRequest = new DisplayPowerRequest(mPendingRequestLocked); - updatePendingProximityRequestsLocked(); + mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked(); mPendingRequestChangedLocked = false; mustInitialize = true; // Assume we're on and bright until told otherwise, since that's the state we turn @@ -1500,7 +1337,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } else if (mPendingRequestChangedLocked) { previousPolicy = mPowerRequest.policy; mPowerRequest.copyFrom(mPendingRequestLocked); - updatePendingProximityRequestsLocked(); + mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked(); mPendingRequestChangedLocked = false; mDisplayReadyLocked = false; } else { @@ -1512,105 +1349,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call displayBrightnessFollowers = mDisplayBrightnessFollowers.clone(); } - // Compute the basic display state using the policy. - // We might override this below based on other factors. - // Initialise brightness as invalid. - int state; - float brightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT; - boolean performScreenOffTransition = false; - switch (mPowerRequest.policy) { - case DisplayPowerRequest.POLICY_OFF: - state = Display.STATE_OFF; - performScreenOffTransition = true; - break; - case DisplayPowerRequest.POLICY_DOZE: - if (mDozeStateOverride != Display.STATE_UNKNOWN) { - state = mDozeStateOverride; - } else if (mPowerRequest.dozeScreenState != Display.STATE_UNKNOWN) { - state = mPowerRequest.dozeScreenState; - } else { - state = Display.STATE_DOZE; - } - if (!mAllowAutoBrightnessWhileDozingConfig) { - brightnessState = mPowerRequest.dozeScreenBrightness; - mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE); - } - break; - case DisplayPowerRequest.POLICY_DIM: - case DisplayPowerRequest.POLICY_BRIGHT: - default: - state = Display.STATE_ON; - break; - } - assert (state != Display.STATE_UNKNOWN); - - if (mScreenOffBrightnessSensorController != null) { - mScreenOffBrightnessSensorController.setLightSensorEnabled(mUseAutoBrightness - && mIsEnabled && (state == Display.STATE_OFF || (state == Display.STATE_DOZE - && !mAllowAutoBrightnessWhileDozingConfig)) - && mLeadDisplayId == Layout.NO_LEAD_DISPLAY); - } - - boolean skipRampBecauseOfProximityChangeToNegative = false; - // Apply the proximity sensor. - if (mProximitySensor != null) { - if (mPowerRequest.useProximitySensor && state != Display.STATE_OFF) { - // At this point the policy says that the screen should be on, but we've been - // asked to listen to the prox sensor to adjust the display state, so lets make - // sure the sensor is on. - setProximitySensorEnabled(true); - if (!mScreenOffBecauseOfProximity - && mProximity == PROXIMITY_POSITIVE - && !mIgnoreProximityUntilChanged) { - // Prox sensor already reporting "near" so we should turn off the screen. - // Also checked that we aren't currently set to ignore the proximity sensor - // temporarily. - mScreenOffBecauseOfProximity = true; - sendOnProximityPositiveWithWakelock(); - } - } else if (mWaitingForNegativeProximity - && mScreenOffBecauseOfProximity - && mProximity == PROXIMITY_POSITIVE - && state != Display.STATE_OFF) { - // The policy says that we should have the screen on, but it's off due to the prox - // and we've been asked to wait until the screen is far from the user to turn it - // back on. Let keep the prox sensor on so we can tell when it's far again. - setProximitySensorEnabled(true); - } else { - // We haven't been asked to use the prox sensor and we're not waiting on the screen - // to turn back on...so lets shut down the prox sensor. - setProximitySensorEnabled(false); - mWaitingForNegativeProximity = false; - } - - if (mScreenOffBecauseOfProximity - && (mProximity != PROXIMITY_POSITIVE || mIgnoreProximityUntilChanged)) { - // The screen *was* off due to prox being near, but now it's "far" so lets turn - // the screen back on. Also turn it back on if we've been asked to ignore the - // prox sensor temporarily. - mScreenOffBecauseOfProximity = false; - skipRampBecauseOfProximityChangeToNegative = true; - sendOnProximityNegativeWithWakelock(); - } - } else { - setProximitySensorEnabled(false); - mWaitingForNegativeProximity = false; - mIgnoreProximityUntilChanged = false; - - if (mScreenOffBecauseOfProximity) { - // The screen *was* off due to prox being near, but now there's no prox sensor, so - // let's turn the screen back on. - mScreenOffBecauseOfProximity = false; - skipRampBecauseOfProximityChangeToNegative = true; - sendOnProximityNegativeWithWakelock(); - } - } - - if (!mIsEnabled - || mIsInTransition - || mScreenOffBecauseOfProximity) { - state = Display.STATE_OFF; - } + int state = mDisplayStateController + .updateDisplayState(mPowerRequest, mIsEnabled, mIsInTransition); // Initialize things the first time the power state is changed. if (mustInitialize) { @@ -1620,157 +1360,100 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Animate the screen state change unless already animating. // The transition may be deferred, so after this point we will use the // actual state instead of the desired one. - final int oldState = mPowerState.getScreenState(); - animateScreenStateChange(state, performScreenOffTransition); + animateScreenStateChange(state, mDisplayStateController.shouldPerformScreenOffTransition()); state = mPowerState.getScreenState(); - boolean slowChange = false; - if (state == Display.STATE_OFF) { - brightnessState = PowerManager.BRIGHTNESS_OFF_FLOAT; - mBrightnessReasonTemp.setReason(BrightnessReason.REASON_SCREEN_OFF); + // Switch to doze auto-brightness mode if needed + if (mFlags.areAutoBrightnessModesEnabled() && mAutomaticBrightnessController != null + && !mAutomaticBrightnessController.isInIdleMode()) { + setAutomaticScreenBrightnessMode(Display.isDozeState(state) + ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT); } - if (Float.isNaN(brightnessState) && isValidBrightnessValue(mBrightnessToFollow)) { - brightnessState = mBrightnessToFollow; - slowChange = mBrightnessToFollowSlowChange; - mBrightnessReasonTemp.setReason(BrightnessReason.REASON_FOLLOWER); - } + final boolean userSetBrightnessChanged = mDisplayBrightnessController + .updateUserSetScreenBrightness(); - if ((Float.isNaN(brightnessState)) - && isValidBrightnessValue(mPowerRequest.screenBrightnessOverride)) { - brightnessState = mPowerRequest.screenBrightnessOverride; - mBrightnessReasonTemp.setReason(BrightnessReason.REASON_OVERRIDE); - mAppliedScreenBrightnessOverride = true; - } else { - mAppliedScreenBrightnessOverride = false; - } - - final boolean autoBrightnessEnabledInDoze = - mAllowAutoBrightnessWhileDozingConfig && Display.isDozeState(state); - final boolean autoBrightnessEnabled = mUseAutoBrightness - && (state == Display.STATE_ON || autoBrightnessEnabledInDoze) - && mBrightnessReasonTemp.getReason() != BrightnessReason.REASON_OVERRIDE - && mAutomaticBrightnessController != null; - final boolean autoBrightnessDisabledDueToDisplayOff = mUseAutoBrightness - && !(state == Display.STATE_ON || autoBrightnessEnabledInDoze); - final int autoBrightnessState = autoBrightnessEnabled - && mBrightnessReasonTemp.getReason() != BrightnessReason.REASON_FOLLOWER - ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED - : autoBrightnessDisabledDueToDisplayOff - ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE - : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED; - - final boolean userSetBrightnessChanged = updateUserSetScreenBrightness(); + DisplayBrightnessState displayBrightnessState = mDisplayBrightnessController + .updateBrightness(mPowerRequest, state); + float brightnessState = displayBrightnessState.getBrightness(); + float rawBrightnessState = displayBrightnessState.getBrightness(); + mBrightnessReasonTemp.set(displayBrightnessState.getBrightnessReason()); + boolean slowChange = displayBrightnessState.isSlowChange(); + // custom transition duration + float customAnimationRate = displayBrightnessState.getCustomAnimationRate(); - // Use the temporary screen brightness if there isn't an override, either from - // WindowManager or based on the display state. - if (isValidBrightnessValue(mTemporaryScreenBrightness)) { - brightnessState = mTemporaryScreenBrightness; - mAppliedTemporaryBrightness = true; - mBrightnessReasonTemp.setReason(BrightnessReason.REASON_TEMPORARY); - } else { - mAppliedTemporaryBrightness = false; + // Set up the ScreenOff controller used when coming out of SCREEN_OFF and the ALS sensor + // doesn't yet have a valid lux value to use with auto-brightness. + if (mScreenOffBrightnessSensorController != null) { + mScreenOffBrightnessSensorController + .setLightSensorEnabled(displayBrightnessState.getShouldUseAutoBrightness() + && mIsEnabled && (state == Display.STATE_OFF + || (state == Display.STATE_DOZE + && !mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig())) + && mLeadDisplayId == Layout.NO_LEAD_DISPLAY); } - final boolean autoBrightnessAdjustmentChanged = updateAutoBrightnessAdjustment(); - - // Use the autobrightness adjustment override if set. - final float autoBrightnessAdjustment; - if (!Float.isNaN(mTemporaryAutoBrightnessAdjustment)) { - autoBrightnessAdjustment = mTemporaryAutoBrightnessAdjustment; - brightnessAdjustmentFlags = BrightnessReason.ADJUSTMENT_AUTO_TEMP; - mAppliedTemporaryAutoBrightnessAdjustment = true; - } else { - autoBrightnessAdjustment = mAutoBrightnessAdjustment; - brightnessAdjustmentFlags = BrightnessReason.ADJUSTMENT_AUTO; - mAppliedTemporaryAutoBrightnessAdjustment = false; - } - // Apply brightness boost. - // We do this here after deciding whether auto-brightness is enabled so that we don't - // disable the light sensor during this temporary state. That way when boost ends we will - // be able to resume normal auto-brightness behavior without any delay. - if (mPowerRequest.boostScreenBrightness - && brightnessState != PowerManager.BRIGHTNESS_OFF_FLOAT) { - brightnessState = PowerManager.BRIGHTNESS_MAX; - mBrightnessReasonTemp.setReason(BrightnessReason.REASON_BOOST); - mAppliedBrightnessBoost = true; - } else { - mAppliedBrightnessBoost = false; - } + // Take note if the short term model was already active before applying the current + // request changes. + final boolean wasShortTermModelActive = + mAutomaticBrightnessStrategy.isShortTermModelActive(); + mAutomaticBrightnessStrategy.setAutoBrightnessState(state, + mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig(), + mBrightnessReasonTemp.getReason(), mPowerRequest.policy, + mDisplayBrightnessController.getLastUserSetScreenBrightness(), + userSetBrightnessChanged); // If the brightness is already set then it's been overridden by something other than the // user, or is a temporary adjustment. boolean userInitiatedChange = (Float.isNaN(brightnessState)) - && (autoBrightnessAdjustmentChanged || userSetBrightnessChanged); - boolean wasShortTermModelActive = false; - // Configure auto-brightness. - if (mAutomaticBrightnessController != null) { - wasShortTermModelActive = mAutomaticBrightnessController.hasUserDataPoints(); - mAutomaticBrightnessController.configure(autoBrightnessState, - mBrightnessConfiguration, - mLastUserSetScreenBrightness, - userSetBrightnessChanged, autoBrightnessAdjustment, - autoBrightnessAdjustmentChanged, mPowerRequest.policy, - mShouldResetShortTermModel); - mShouldResetShortTermModel = false; - } - mBrightnessRangeController.setAutoBrightnessEnabled(autoBrightnessEnabled + && (mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentChanged() + || userSetBrightnessChanged); + + mBrightnessRangeController.setAutoBrightnessEnabled( + mAutomaticBrightnessStrategy.isAutoBrightnessEnabled() ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED - : autoBrightnessDisabledDueToDisplayOff + : mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff() ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED); - if (mBrightnessTracker != null) { - mBrightnessTracker.setShouldCollectColorSample(mBrightnessConfiguration != null - && mBrightnessConfiguration.shouldCollectColorSamples()); - } - - boolean updateScreenBrightnessSetting = false; - float rawBrightnessState = brightnessState; - + boolean updateScreenBrightnessSetting = + displayBrightnessState.shouldUpdateScreenBrightnessSetting(); + float currentBrightnessSetting = mDisplayBrightnessController.getCurrentBrightness(); // Apply auto-brightness. + int brightnessAdjustmentFlags = 0; if (Float.isNaN(brightnessState)) { - float newAutoBrightnessAdjustment = autoBrightnessAdjustment; - if (autoBrightnessEnabled) { - rawBrightnessState = mAutomaticBrightnessController - .getRawAutomaticScreenBrightness(); - brightnessState = mAutomaticBrightnessController.getAutomaticScreenBrightness( + if (mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()) { + brightnessState = mAutomaticBrightnessStrategy.getAutomaticScreenBrightness( mTempBrightnessEvent); - newAutoBrightnessAdjustment = - mAutomaticBrightnessController.getAutomaticScreenBrightnessAdjustment(); - } - if (isValidBrightnessValue(brightnessState) - || brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT) { - // Use current auto-brightness value and slowly adjust to changes. - brightnessState = clampScreenBrightness(brightnessState); - if (mAppliedAutoBrightness && !autoBrightnessAdjustmentChanged) { - slowChange = true; // slowly adapt to auto-brightness - } - updateScreenBrightnessSetting = mCurrentScreenBrightnessSetting != brightnessState; - mAppliedAutoBrightness = true; - mBrightnessReasonTemp.setReason(BrightnessReason.REASON_AUTOMATIC); - if (mScreenOffBrightnessSensorController != null) { - mScreenOffBrightnessSensorController.setLightSensorEnabled(false); + if (BrightnessUtils.isValidBrightnessValue(brightnessState) + || brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT) { + rawBrightnessState = mAutomaticBrightnessController + .getRawAutomaticScreenBrightness(); + brightnessState = clampScreenBrightness(brightnessState); + // slowly adapt to auto-brightness + // TODO(b/253226419): slowChange should be decided by strategy.updateBrightness + slowChange = mAutomaticBrightnessStrategy.hasAppliedAutoBrightness() + && !mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentChanged(); + brightnessAdjustmentFlags = + mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentReasonsFlags(); + updateScreenBrightnessSetting = currentBrightnessSetting != brightnessState; + mAutomaticBrightnessStrategy.setAutoBrightnessApplied(true); + mBrightnessReasonTemp.setReason(BrightnessReason.REASON_AUTOMATIC); + if (mScreenOffBrightnessSensorController != null) { + mScreenOffBrightnessSensorController.setLightSensorEnabled(false); + } + } else { + mAutomaticBrightnessStrategy.setAutoBrightnessApplied(false); } - } else { - mAppliedAutoBrightness = false; - } - if (autoBrightnessAdjustment != newAutoBrightnessAdjustment) { - // If the autobrightness controller has decided to change the adjustment value - // used, make sure that's reflected in settings. - putAutoBrightnessAdjustmentSetting(newAutoBrightnessAdjustment); - } else { - // Adjustment values resulted in no change - brightnessAdjustmentFlags = 0; } } else { // Any non-auto-brightness values such as override or temporary should still be subject // to clamping so that they don't go beyond the current max as specified by HBM // Controller. brightnessState = clampScreenBrightness(brightnessState); - mAppliedAutoBrightness = false; - brightnessAdjustmentFlags = 0; + mAutomaticBrightnessStrategy.setAutoBrightnessApplied(false); } + // Use default brightness when dozing unless overridden. if ((Float.isNaN(brightnessState)) && Display.isDozeState(state)) { @@ -1781,14 +1464,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // The ALS is not available yet - use the screen off sensor to determine the initial // brightness - if (Float.isNaN(brightnessState) && autoBrightnessEnabled + if (Float.isNaN(brightnessState) && mAutomaticBrightnessStrategy.isAutoBrightnessEnabled() && mScreenOffBrightnessSensorController != null) { rawBrightnessState = mScreenOffBrightnessSensorController.getAutomaticScreenBrightness(); brightnessState = rawBrightnessState; - if (isValidBrightnessValue(brightnessState)) { + if (BrightnessUtils.isValidBrightnessValue(brightnessState)) { brightnessState = clampScreenBrightness(brightnessState); - updateScreenBrightnessSetting = mCurrentScreenBrightnessSetting != brightnessState; + updateScreenBrightnessSetting = mDisplayBrightnessController.getCurrentBrightness() + != brightnessState; mBrightnessReasonTemp.setReason( BrightnessReason.REASON_SCREEN_OFF_BRIGHTNESS_SENSOR); } @@ -1796,9 +1480,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Apply manual brightness. if (Float.isNaN(brightnessState)) { - rawBrightnessState = mCurrentScreenBrightnessSetting; + rawBrightnessState = currentBrightnessSetting; brightnessState = clampScreenBrightness(rawBrightnessState); - if (brightnessState != mCurrentScreenBrightnessSetting) { + if (brightnessState != currentBrightnessSetting) { // The manually chosen screen brightness is outside of the currently allowed // range (i.e., high-brightness-mode), make sure we tell the rest of the system // by updating the setting. @@ -1811,7 +1495,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call : mAutomaticBrightnessController.getAmbientLux(); for (int i = 0; i < displayBrightnessFollowers.size(); i++) { DisplayPowerControllerInterface follower = displayBrightnessFollowers.valueAt(i); - follower.setBrightnessToFollow(rawBrightnessState, convertToNits(rawBrightnessState), + follower.setBrightnessToFollow(rawBrightnessState, + mDisplayBrightnessController.convertToNits(rawBrightnessState), ambientLux, slowChange); } @@ -1823,64 +1508,25 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Note throttling effectively changes the allowed brightness range, so, similarly to HBM, // we broadcast this change through setting. final float unthrottledBrightnessState = brightnessState; - if (mBrightnessThrottler.isThrottled()) { - mTempBrightnessEvent.setThermalMax(mBrightnessThrottler.getBrightnessCap()); - brightnessState = Math.min(brightnessState, mBrightnessThrottler.getBrightnessCap()); - mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_THROTTLED); - if (!mAppliedThrottling) { - // Brightness throttling is needed, so do so quickly. - // Later, when throttling is removed, we let other mechanisms decide on speed. - slowChange = false; - } - mAppliedThrottling = true; - } else if (mAppliedThrottling) { - mAppliedThrottling = false; - } + DisplayBrightnessState clampedState = mBrightnessClamperController.clamp(mPowerRequest, + brightnessState, slowChange); + + brightnessState = clampedState.getBrightness(); + slowChange = clampedState.isSlowChange(); + // faster rate wins, at this point customAnimationRate == -1, strategy does not control + // customAnimationRate. Should be revisited if strategy start setting this value + customAnimationRate = Math.max(customAnimationRate, clampedState.getCustomAnimationRate()); + mBrightnessReasonTemp.addModifier(clampedState.getBrightnessReason().getModifier()); if (updateScreenBrightnessSetting) { // Tell the rest of the system about the new brightness in case we had to change it // for things like auto-brightness or high-brightness-mode. Note that we do this - // before applying the low power or dim transformations so that the slider - // accurately represents the full possible range, even if they range changes what - // it means in absolute terms. - updateScreenBrightnessSetting(brightnessState); - } - - // Apply dimming by at least some minimum amount when user activity - // timeout is about to expire. - if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) { - if (brightnessState > PowerManager.BRIGHTNESS_MIN) { - brightnessState = Math.max( - Math.min(brightnessState - mScreenBrightnessMinimumDimAmount, - mScreenBrightnessDimConfig), - PowerManager.BRIGHTNESS_MIN); - mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_DIMMED); - } - if (!mAppliedDimming) { - slowChange = false; - } - mAppliedDimming = true; - } else if (mAppliedDimming) { - slowChange = false; - mAppliedDimming = false; - } - // If low power mode is enabled, scale brightness by screenLowPowerBrightnessFactor - // as long as it is above the minimum threshold. - if (mPowerRequest.lowPowerMode) { - if (brightnessState > PowerManager.BRIGHTNESS_MIN) { - final float brightnessFactor = - Math.min(mPowerRequest.screenLowPowerBrightnessFactor, 1); - final float lowPowerBrightnessFloat = (brightnessState * brightnessFactor); - brightnessState = Math.max(lowPowerBrightnessFloat, PowerManager.BRIGHTNESS_MIN); - mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_LOW_POWER); - } - if (!mAppliedLowPower) { - slowChange = false; - } - mAppliedLowPower = true; - } else if (mAppliedLowPower) { - slowChange = false; - mAppliedLowPower = false; + // only considering maxBrightness (ignoring brightness modifiers like low power or dim) + // so that the slider accurately represents the full possible range, + // even if they range changes what it means in absolute terms. + mDisplayBrightnessController.updateScreenBrightnessSetting( + MathUtils.constrain(unthrottledBrightnessState, + clampedState.getMinBrightness(), clampedState.getMaxBrightness())); } // The current brightness to use has been calculated at this point, and HbmController should @@ -1889,13 +1535,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // brightness sources (such as an app override) are not saved to the setting, but should be // reflected in HBM calculations. mBrightnessRangeController.onBrightnessChanged(brightnessState, unthrottledBrightnessState, - mBrightnessThrottler.getBrightnessMaxReason()); + mBrightnessClamperController.getBrightnessMaxReason()); // Animate the screen brightness when the screen is on or dozing. - // Skip the animation when the screen is off. + // Skip the animation when the screen is off or suspended. boolean brightnessAdjusted = false; final boolean brightnessIsTemporary = - mAppliedTemporaryBrightness || mAppliedTemporaryAutoBrightnessAdjustment; + (mBrightnessReasonTemp.getReason() == BrightnessReason.REASON_TEMPORARY) + || mAutomaticBrightnessStrategy + .isTemporaryAutoBrightnessAdjustmentApplied(); if (!mPendingScreenOff) { if (mSkipScreenOnBrightnessRamp) { if (state == Display.STATE_ON) { @@ -1916,7 +1564,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } final boolean initialRampSkip = (state == Display.STATE_ON && mSkipRampState - != RAMP_STATE_SKIP_NONE) || skipRampBecauseOfProximityChangeToNegative; + != RAMP_STATE_SKIP_NONE) || mDisplayPowerProximityStateController + .shouldSkipRampBecauseOfProximityChangeToNegative(); // While dozing, sometimes the brightness is split into buckets. Rather than animating // through the buckets, which is unlikely to be smooth in the first place, just jump // right to the suggested brightness. @@ -1950,13 +1599,25 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // We want to scale HDR brightness level with the SDR level, we also need to restore // SDR brightness immediately when entering dim or low power mode. animateValue = mBrightnessRangeController.getHdrBrightnessValue(); + customAnimationRate = Math.max(customAnimationRate, + mBrightnessRangeController.getHdrTransitionRate()); mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_HDR); } + // if doze or suspend state is requested, we want to finish brightnes animation fast + // to allow state animation to start + if (mPowerRequest.policy == DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE + && (mPowerRequest.dozeScreenState == Display.STATE_UNKNOWN // dozing + || mPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND + || mPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND)) { + customAnimationRate = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET; + slowChange = false; + } + final float currentBrightness = mPowerState.getScreenBrightness(); final float currentSdrBrightness = mPowerState.getSdrScreenBrightness(); - if (isValidBrightnessValue(animateValue) + if (BrightnessUtils.isValidBrightnessValue(animateValue) && (animateValue != currentBrightness || sdrAnimateValue != currentSdrBrightness)) { boolean skipAnimation = initialRampSkip || hasBrightnessBuckets @@ -1986,6 +1647,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (skipAnimation) { animateScreenBrightness(animateValue, sdrAnimateValue, SCREEN_ANIMATION_RATE_MINIMUM); + } else if (customAnimationRate > 0) { + animateScreenBrightness(animateValue, sdrAnimateValue, + customAnimationRate, /* ignoreAnimationLimits = */true); } else { boolean isIncreasing = animateValue > currentBrightness; final float rampSpeed; @@ -2007,13 +1671,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } notifyBrightnessTrackerChanged(brightnessState, userInitiatedChange, - wasShortTermModelActive, autoBrightnessEnabled, brightnessIsTemporary); + wasShortTermModelActive, mAutomaticBrightnessStrategy.isAutoBrightnessEnabled(), + brightnessIsTemporary, displayBrightnessState.getShouldUseAutoBrightness()); // We save the brightness info *after* the brightness setting has been changed and // adjustments made so that the brightness info reflects the latest value. - brightnessAdjusted = saveBrightnessInfo(getScreenBrightnessSetting(), animateValue); + brightnessAdjusted = saveBrightnessInfo(getScreenBrightnessSetting(), + animateValue, clampedState); } else { - brightnessAdjusted = saveBrightnessInfo(getScreenBrightnessSetting()); + brightnessAdjusted = saveBrightnessInfo(getScreenBrightnessSetting(), clampedState); } // Only notify if the brightness adjustment is not temporary (i.e. slider has been released) @@ -2049,13 +1715,20 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call ? mCdsi.getReduceBrightColorsStrength() : -1); mTempBrightnessEvent.setPowerFactor(mPowerRequest.screenLowPowerBrightnessFactor); mTempBrightnessEvent.setWasShortTermModelActive(wasShortTermModelActive); - mTempBrightnessEvent.setAutomaticBrightnessEnabled(mUseAutoBrightness); + mTempBrightnessEvent.setDisplayBrightnessStrategyName(displayBrightnessState + .getDisplayBrightnessStrategyName()); + mTempBrightnessEvent.setAutomaticBrightnessEnabled( + displayBrightnessState.getShouldUseAutoBrightness()); // Temporary is what we use during slider interactions. We avoid logging those so that // we don't spam logcat when the slider is being used. boolean tempToTempTransition = mTempBrightnessEvent.getReason().getReason() == BrightnessReason.REASON_TEMPORARY && mLastBrightnessEvent.getReason().getReason() == BrightnessReason.REASON_TEMPORARY; + // Purely for dumpsys; + final boolean isRbcEvent = + mLastBrightnessEvent.isRbcEnabled() != mTempBrightnessEvent.isRbcEnabled(); + if ((!mTempBrightnessEvent.equalsMainData(mLastBrightnessEvent) && !tempToTempTransition) || brightnessAdjustmentFlags != 0) { mTempBrightnessEvent.setInitialBrightness(mLastBrightnessEvent.getBrightness()); @@ -2075,6 +1748,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (mBrightnessEventRingBuffer != null) { mBrightnessEventRingBuffer.append(newEvent); } + if (isRbcEvent) { + mRbcEventRingBuffer.append(newEvent); + } + } // Update display white-balance. @@ -2092,6 +1769,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // reporting the display is ready because we only need to ensure the screen is in the // right power state even as it continues to converge on the desired brightness. final boolean ready = mPendingScreenOnUnblocker == null + && mPendingScreenOnUnblockerByDisplayOffload == null && (!mColorFadeEnabled || (!mColorFadeOnAnimator.isStarted() && !mColorFadeOffAnimator.isStarted())) && mPowerState.waitUntilClean(mCleanListener); @@ -2106,12 +1784,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } // Grab a wake lock if we have unfinished business. - if (!finished && !mUnfinishedBusiness) { - if (DEBUG) { - Slog.d(mTag, "Unfinished business..."); - } - mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdUnfinishedBusiness); - mUnfinishedBusiness = true; + if (!finished) { + mWakelockController.acquireWakelock(WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS); } // Notify the power manager when ready. @@ -2130,12 +1804,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } // Release the wake lock when we have no unfinished business. - if (finished && mUnfinishedBusiness) { - if (DEBUG) { - Slog.d(mTag, "Finished business..."); - } - mUnfinishedBusiness = false; - mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdUnfinishedBusiness); + if (finished) { + mWakelockController.releaseWakelock(WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS); } // Record if dozing for future comparison. @@ -2166,9 +1836,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private void setDwbcLoggingEnabled(int arg) { if (mDisplayWhiteBalanceController != null) { - final boolean shouldEnable = (arg == 1); - mDisplayWhiteBalanceController.setLoggingEnabled(shouldEnable); - mDisplayWhiteBalanceSettings.setLoggingEnabled(shouldEnable); + final boolean enabled = (arg == 1); + mDisplayWhiteBalanceController.setLoggingEnabled(enabled); + mDisplayWhiteBalanceSettings.setLoggingEnabled(enabled); } } @@ -2183,7 +1853,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call */ @Override public void ignoreProximitySensorUntilChanged() { - mHandler.sendEmptyMessage(MSG_IGNORE_PROXIMITY); + mDisplayPowerProximityStateController.ignoreProximitySensorUntilChanged(); } @Override @@ -2210,21 +1880,27 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call @Override public void setBrightnessFromOffload(float brightness) { - // The old DPC is no longer supported + Message msg = mHandler.obtainMessage(MSG_SET_BRIGHTNESS_FROM_OFFLOAD, + Float.floatToIntBits(brightness), 0 /*unused*/); + mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); } @Override public float[] getAutoBrightnessLevels( @AutomaticBrightnessController.AutomaticBrightnessMode int mode) { - // The old DPC is no longer supported - return null; + int preset = Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_FOR_ALS, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL, UserHandle.USER_CURRENT); + return mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(mode, preset); } @Override public float[] getAutoBrightnessLuxLevels( @AutomaticBrightnessController.AutomaticBrightnessMode int mode) { - // The old DPC is no longer supported - return null; + int preset = Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_FOR_ALS, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL, UserHandle.USER_CURRENT); + return mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(mode, preset); } @Override @@ -2241,18 +1917,29 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } + @Override + public void onBootCompleted() { + Message msg = mHandler.obtainMessage(MSG_BOOT_COMPLETED); + mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); + } + private boolean saveBrightnessInfo(float brightness) { - return saveBrightnessInfo(brightness, brightness); + return saveBrightnessInfo(brightness, /* state= */ null); + } + + private boolean saveBrightnessInfo(float brightness, @Nullable DisplayBrightnessState state) { + return saveBrightnessInfo(brightness, brightness, state); } - private boolean saveBrightnessInfo(float brightness, float adjustedBrightness) { + private boolean saveBrightnessInfo(float brightness, float adjustedBrightness, + @Nullable DisplayBrightnessState state) { synchronized (mCachedBrightnessInfo) { - final float minBrightness = Math.min( - mBrightnessRangeController.getCurrentBrightnessMin(), - mBrightnessThrottler.getBrightnessCap()); + float stateMax = state != null ? state.getMaxBrightness() : PowerManager.BRIGHTNESS_MAX; + float stateMin = state != null ? state.getMinBrightness() : PowerManager.BRIGHTNESS_MAX; + final float minBrightness = Math.max(stateMin, Math.min( + mBrightnessRangeController.getCurrentBrightnessMin(), stateMax)); final float maxBrightness = Math.min( - mBrightnessRangeController.getCurrentBrightnessMax(), - mBrightnessThrottler.getBrightnessCap()); + mBrightnessRangeController.getCurrentBrightnessMax(), stateMax); boolean changed = false; changed |= @@ -2275,8 +1962,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mBrightnessRangeController.getTransitionPoint()); changed |= mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason, - mBrightnessThrottler.getBrightnessMaxReason()); - + mBrightnessClamperController.getBrightnessMaxReason()); return changed; } } @@ -2288,27 +1974,18 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } private HighBrightnessModeController createHbmControllerLocked( - Runnable modeChangeCallback) { - final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked(); - final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig(); - final IBinder displayToken = - mLogicalDisplay.getPrimaryDisplayDeviceLocked().getDisplayTokenLocked(); - final String displayUniqueId = - mLogicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId(); + HighBrightnessModeMetadata hbmMetadata, Runnable modeChangeCallback) { + final DisplayDeviceConfig ddConfig = mDisplayDevice.getDisplayDeviceConfig(); + final IBinder displayToken = mDisplayDevice.getDisplayTokenLocked(); + final String displayUniqueId = mDisplayDevice.getUniqueId(); final DisplayDeviceConfig.HighBrightnessModeData hbmData = ddConfig != null ? ddConfig.getHighBrightnessModeData() : null; - final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); + final DisplayDeviceInfo info = mDisplayDevice.getDisplayDeviceInfoLocked(); return mInjector.getHighBrightnessModeController(mHandler, info.width, info.height, displayToken, displayUniqueId, PowerManager.BRIGHTNESS_MIN, - PowerManager.BRIGHTNESS_MAX, hbmData, - new HighBrightnessModeController.HdrBrightnessDeviceConfig() { - @Override - public float getHdrBrightnessFromSdr( - float sdrBrightness, float maxDesiredHdrSdrRatio) { - return mDisplayDeviceConfig.getHdrBrightnessFromSdr( - sdrBrightness, maxDesiredHdrSdrRatio); - } - }, modeChangeCallback, mHighBrightnessModeMetadata, mContext); + PowerManager.BRIGHTNESS_MAX, hbmData, (sdrBrightness, maxDesiredHdrSdrRatio) -> + mDisplayDeviceConfig.getHdrBrightnessFromSdr(sdrBrightness, + maxDesiredHdrSdrRatio), modeChangeCallback, hbmMetadata, mContext); } private BrightnessThrottler createBrightnessThrottlerLocked() { @@ -2359,18 +2036,72 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } + private void blockScreenOnByDisplayOffload(DisplayOffloadSession displayOffloadSession) { + if (mPendingScreenOnUnblockerByDisplayOffload != null || displayOffloadSession == null) { + return; + } + mScreenTurningOnWasBlockedByDisplayOffload = true; + + Trace.asyncTraceBegin( + Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME, 0); + mScreenOnBlockByDisplayOffloadStartRealTime = SystemClock.elapsedRealtime(); + + mPendingScreenOnUnblockerByDisplayOffload = + () -> onDisplayOffloadUnblockScreenOn(displayOffloadSession); + if (!displayOffloadSession.blockScreenOn(mPendingScreenOnUnblockerByDisplayOffload)) { + mPendingScreenOnUnblockerByDisplayOffload = null; + long delay = + SystemClock.elapsedRealtime() - mScreenOnBlockByDisplayOffloadStartRealTime; + Slog.w(mTag, "Tried blocking screen on for offloading but failed. So, end trace after " + + delay + " ms."); + Trace.asyncTraceEnd( + Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME, 0); + return; + } + Slog.i(mTag, "Blocking screen on for offloading."); + } + + private void onDisplayOffloadUnblockScreenOn(DisplayOffloadSession displayOffloadSession) { + Message msg = mHandler.obtainMessage(MSG_OFFLOADING_SCREEN_ON_UNBLOCKED, + displayOffloadSession); + mHandler.sendMessage(msg); + } + + private void unblockScreenOnByDisplayOffload() { + if (mPendingScreenOnUnblockerByDisplayOffload == null) { + return; + } + mPendingScreenOnUnblockerByDisplayOffload = null; + long delay = SystemClock.elapsedRealtime() - mScreenOnBlockByDisplayOffloadStartRealTime; + Slog.i(mTag, "Unblocked screen on for offloading after " + delay + " ms"); + Trace.asyncTraceEnd( + Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME, 0); + } + private boolean setScreenState(int state) { return setScreenState(state, false /*reportOnly*/); } private boolean setScreenState(int state, boolean reportOnly) { final boolean isOff = (state == Display.STATE_OFF); + final boolean isOn = (state == Display.STATE_ON); + final boolean changed = mPowerState.getScreenState() != state; + + // If the screen is turning on, give displayoffload a chance to do something before the + // screen actually turns on. + // TODO(b/316941732): add tests for this displayoffload screen-on blocker. + if (isOn && changed && !mScreenTurningOnWasBlockedByDisplayOffload) { + blockScreenOnByDisplayOffload(mDisplayOffloadSession); + } else if (!isOn && mScreenTurningOnWasBlockedByDisplayOffload) { + // No longer turning screen on, so unblock previous screen on blocking immediately. + unblockScreenOnByDisplayOffload(); + mScreenTurningOnWasBlockedByDisplayOffload = false; + } - if (mPowerState.getScreenState() != state - || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) { + if (changed || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) { // If we are trying to turn screen off, give policy a chance to do something before we // actually turn the screen off. - if (isOff && !mScreenOffBecauseOfProximity) { + if (isOff && !mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity()) { if (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_ON || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) { setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_OFF); @@ -2383,8 +2114,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } - if (!reportOnly && mPowerState.getScreenState() != state - && readyToUpdateDisplayState()) { + if (!reportOnly && changed && readyToUpdateDisplayState() + && mPendingScreenOffUnblocker == null + && mPendingScreenOnUnblockerByDisplayOffload == null) { Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenState", state); String propertyKey = "debug.tracing.screen_state"; @@ -2410,7 +2142,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // it is only removed once the window manager tells us that the activity has // finished drawing underneath. if (isOff && mReportedScreenStateToPolicy != REPORTED_TO_POLICY_SCREEN_OFF - && !mScreenOffBecauseOfProximity) { + && !mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity()) { setReportedScreenState(REPORTED_TO_POLICY_SCREEN_OFF); unblockScreenOn(); mWindowManagerPolicy.screenTurnedOff(mDisplayId, mIsInTransition); @@ -2436,12 +2168,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } // Return true if the screen isn't blocked. - return mPendingScreenOnUnblocker == null; + return mPendingScreenOnUnblocker == null + && mPendingScreenOnUnblockerByDisplayOffload == null; } private void setReportedScreenState(int state) { Trace.traceCounter(Trace.TRACE_TAG_POWER, "ReportedScreenStateToPolicy", state); mReportedScreenStateToPolicy = state; + if (state == REPORTED_TO_POLICY_SCREEN_ON) { + mScreenTurningOnWasBlockedByDisplayOffload = false; + } } private void loadAmbientLightSensor() { @@ -2456,18 +2192,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mDisplayDeviceConfig.getScreenOffBrightnessSensor(), SensorUtils.NO_FALLBACK); } - private void loadProximitySensor() { - if (DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT || mDisplayId != Display.DEFAULT_DISPLAY) { - return; - } - mProximitySensor = SensorUtils.findSensor(mSensorManager, - mDisplayDeviceConfig.getProximitySensor(), Sensor.TYPE_PROXIMITY); - if (mProximitySensor != null) { - mProximityThreshold = Math.min(mProximitySensor.getMaximumRange(), - TYPICAL_PROXIMITY_THRESHOLD); - } - } - private float clampScreenBrightness(float value) { if (Float.isNaN(value)) { value = PowerManager.BRIGHTNESS_MIN; @@ -2476,18 +2200,18 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mBrightnessRangeController.getCurrentBrightnessMax()); } - // Checks whether the brightness is within the valid brightness range, not including off. - private boolean isValidBrightnessValue(float brightness) { - return brightness >= PowerManager.BRIGHTNESS_MIN - && brightness <= PowerManager.BRIGHTNESS_MAX; + private void animateScreenBrightness(float target, float sdrTarget, float rate) { + animateScreenBrightness(target, sdrTarget, rate, /* ignoreAnimationLimits = */false); } - private void animateScreenBrightness(float target, float sdrTarget, float rate) { + private void animateScreenBrightness(float target, float sdrTarget, float rate, + boolean ignoreAnimationLimits) { if (DEBUG) { Slog.d(mTag, "Animating brightness: target=" + target + ", sdrTarget=" + sdrTarget + ", rate=" + rate); } - if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate, false)) { + if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate, + ignoreAnimationLimits)) { Trace.traceCounter(Trace.TRACE_TAG_POWER, "TargetScreenBrightness", (int) target); String propertyKey = "debug.tracing.screen_brightness"; @@ -2655,102 +2379,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private final Runnable mCleanListener = this::sendUpdatePowerState; - private void setProximitySensorEnabled(boolean enable) { - if (enable) { - if (!mProximitySensorEnabled) { - // Register the listener. - // Proximity sensor state already cleared initially. - mProximitySensorEnabled = true; - mIgnoreProximityUntilChanged = false; - mSensorManager.registerListener(mProximitySensorListener, mProximitySensor, - SensorManager.SENSOR_DELAY_NORMAL, mHandler); - } - } else { - if (mProximitySensorEnabled) { - // Unregister the listener. - // Clear the proximity sensor state for next time. - mProximitySensorEnabled = false; - mProximity = PROXIMITY_UNKNOWN; - mIgnoreProximityUntilChanged = false; - mPendingProximity = PROXIMITY_UNKNOWN; - mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED); - mSensorManager.unregisterListener(mProximitySensorListener); - clearPendingProximityDebounceTime(); // release wake lock (must be last) - } - } - } - - private void handleProximitySensorEvent(long time, boolean positive) { - if (mProximitySensorEnabled) { - if (mPendingProximity == PROXIMITY_NEGATIVE && !positive) { - return; // no change - } - if (mPendingProximity == PROXIMITY_POSITIVE && positive) { - return; // no change - } - - // Only accept a proximity sensor reading if it remains - // stable for the entire debounce delay. We hold a wake lock while - // debouncing the sensor. - mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED); - if (positive) { - mPendingProximity = PROXIMITY_POSITIVE; - setPendingProximityDebounceTime( - time + PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY); // acquire wake lock - } else { - mPendingProximity = PROXIMITY_NEGATIVE; - setPendingProximityDebounceTime( - time + PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY); // acquire wake lock - } - - // Debounce the new sensor reading. - debounceProximitySensor(); - } - } - - private void debounceProximitySensor() { - if (mProximitySensorEnabled - && mPendingProximity != PROXIMITY_UNKNOWN - && mPendingProximityDebounceTime >= 0) { - final long now = mClock.uptimeMillis(); - if (mPendingProximityDebounceTime <= now) { - if (mProximity != mPendingProximity) { - // if the status of the sensor changed, stop ignoring. - mIgnoreProximityUntilChanged = false; - Slog.i(mTag, "No longer ignoring proximity [" + mPendingProximity + "]"); - } - // Sensor reading accepted. Apply the change then release the wake lock. - mProximity = mPendingProximity; - updatePowerState(); - clearPendingProximityDebounceTime(); // release wake lock (must be last) - } else { - // Need to wait a little longer. - // Debounce again later. We continue holding a wake lock while waiting. - Message msg = mHandler.obtainMessage(MSG_PROXIMITY_SENSOR_DEBOUNCED); - mHandler.sendMessageAtTime(msg, mPendingProximityDebounceTime); - } - } - } - - private void clearPendingProximityDebounceTime() { - if (mPendingProximityDebounceTime >= 0) { - mPendingProximityDebounceTime = -1; - mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdProxDebounce); - } - } - - private void setPendingProximityDebounceTime(long debounceTime) { - if (mPendingProximityDebounceTime < 0) { - mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdProxDebounce); - } - mPendingProximityDebounceTime = debounceTime; - } - private void sendOnStateChangedWithWakelock() { - if (!mOnStateChangedPending) { - mOnStateChangedPending = true; - mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdOnStateChanged); - mHandler.post(mOnStateChangedRunnable); + boolean wakeLockAcquired = mWakelockController.acquireWakelock( + WakelockController.WAKE_LOCK_STATE_CHANGED); + if (wakeLockAcquired) { + mHandler.post(mWakelockController.getOnStateChangedRunnable()); } } @@ -2762,12 +2395,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } private void handleSettingsChange(boolean userSwitch) { - mPendingScreenBrightnessSetting = getScreenBrightnessSetting(); - mPendingAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting(); + mDisplayBrightnessController + .setPendingScreenBrightness(mDisplayBrightnessController + .getScreenBrightnessSetting()); + mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments(userSwitch); if (userSwitch) { // Don't treat user switches as user initiated change. - setCurrentScreenBrightness(mPendingScreenBrightnessSetting); - updateAutoBrightnessAdjustment(); + mDisplayBrightnessController + .setAndNotifyCurrentScreenBrightness(mDisplayBrightnessController + .getPendingScreenBrightness()); if (mAutomaticBrightnessController != null) { mAutomaticBrightnessController.resetShortTermModel(); } @@ -2781,129 +2417,59 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT); mHandler.postAtTime(() -> { - mUseAutoBrightness = screenBrightnessModeSetting - == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC; + mAutomaticBrightnessStrategy.setUseAutoBrightness(screenBrightnessModeSetting + == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); updatePowerState(); }, mClock.uptimeMillis()); } - private float getAutoBrightnessAdjustmentSetting() { - final float adj = Settings.System.getFloatForUser(mContext.getContentResolver(), - Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.0f, UserHandle.USER_CURRENT); - return Float.isNaN(adj) ? 0.0f : clampAutoBrightnessAdjustment(adj); - } @Override public float getScreenBrightnessSetting() { - float brightness = mBrightnessSetting.getBrightness(); - if (Float.isNaN(brightness)) { - brightness = mScreenBrightnessDefault; - } - return clampAbsoluteBrightness(brightness); - } - - private void loadNitBasedBrightnessSetting() { - if (mDisplayId == Display.DEFAULT_DISPLAY && mPersistBrightnessNitsForDefaultDisplay) { - float brightnessNitsForDefaultDisplay = - mBrightnessSetting.getBrightnessNitsForDefaultDisplay(); - if (brightnessNitsForDefaultDisplay >= 0) { - float brightnessForDefaultDisplay = getBrightnessFromNits( - brightnessNitsForDefaultDisplay); - if (isValidBrightnessValue(brightnessForDefaultDisplay)) { - mBrightnessSetting.setBrightness(brightnessForDefaultDisplay); - mCurrentScreenBrightnessSetting = brightnessForDefaultDisplay; - return; - } - } - } - mCurrentScreenBrightnessSetting = getScreenBrightnessSetting(); + return mDisplayBrightnessController.getScreenBrightnessSetting(); } @Override public void setBrightness(float brightnessValue, int userSerial) { - // Update the setting, which will eventually call back into DPC to have us actually update - // the display with the new value. - float clampedBrightnessValue = clampScreenBrightness(brightnessValue); - mBrightnessSetting.setUserSerial(userSerial); - mBrightnessSetting.setBrightness(clampedBrightnessValue); - if (mDisplayId == Display.DEFAULT_DISPLAY && mPersistBrightnessNitsForDefaultDisplay) { - float nits = convertToNits(clampedBrightnessValue); - if (nits >= 0) { - mBrightnessSetting.setBrightnessNitsForDefaultDisplay(nits); - } - } + mDisplayBrightnessController.setBrightness(clampScreenBrightness(brightnessValue), + userSerial); } @Override - public void onBootCompleted() { - Message msg = mHandler.obtainMessage(MSG_BOOT_COMPLETED); - mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); - } - - private void updateScreenBrightnessSetting(float brightnessValue) { - if (!isValidBrightnessValue(brightnessValue) - || brightnessValue == mCurrentScreenBrightnessSetting) { - return; - } - setCurrentScreenBrightness(brightnessValue); - setBrightness(brightnessValue); - } - - private void setCurrentScreenBrightness(float brightnessValue) { - if (brightnessValue != mCurrentScreenBrightnessSetting) { - mCurrentScreenBrightnessSetting = brightnessValue; - postBrightnessChangeRunnable(); - } - } - - private void putAutoBrightnessAdjustmentSetting(float adjustment) { - if (mDisplayId == Display.DEFAULT_DISPLAY) { - mAutoBrightnessAdjustment = adjustment; - Settings.System.putFloatForUser(mContext.getContentResolver(), - Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, adjustment, - UserHandle.USER_CURRENT); - } + public int getDisplayId() { + return mDisplayId; } - private boolean updateAutoBrightnessAdjustment() { - if (Float.isNaN(mPendingAutoBrightnessAdjustment)) { - return false; - } - if (mAutoBrightnessAdjustment == mPendingAutoBrightnessAdjustment) { - mPendingAutoBrightnessAdjustment = Float.NaN; - return false; - } - mAutoBrightnessAdjustment = mPendingAutoBrightnessAdjustment; - mPendingAutoBrightnessAdjustment = Float.NaN; - mTemporaryAutoBrightnessAdjustment = Float.NaN; - return true; + @Override + public int getLeadDisplayId() { + return mLeadDisplayId; } - // We want to return true if the user has set the screen brightness. - // RBC on, off, and intensity changes will return false. - // Slider interactions whilst in RBC will return true, just as when in non-rbc. - private boolean updateUserSetScreenBrightness() { - if ((Float.isNaN(mPendingScreenBrightnessSetting) - || mPendingScreenBrightnessSetting < 0.0f)) { - return false; - } - if (mCurrentScreenBrightnessSetting == mPendingScreenBrightnessSetting) { - mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT; - mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; - return false; + @Override + public void setBrightnessToFollow(float leadDisplayBrightness, float nits, float ambientLux, + boolean slowChange) { + mBrightnessRangeController.onAmbientLuxChange(ambientLux); + if (nits == BrightnessMappingStrategy.INVALID_NITS) { + mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness, slowChange); + } else { + float brightness = mDisplayBrightnessController.getBrightnessFromNits(nits); + if (BrightnessUtils.isValidBrightnessValue(brightness)) { + mDisplayBrightnessController.setBrightnessToFollow(brightness, slowChange); + } else { + // The device does not support nits + mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness, + slowChange); + } } - setCurrentScreenBrightness(mPendingScreenBrightnessSetting); - mLastUserSetScreenBrightness = mPendingScreenBrightnessSetting; - mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT; - mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; - return true; + sendUpdatePowerState(); } private void notifyBrightnessTrackerChanged(float brightness, boolean userInitiated, boolean wasShortTermModelActive, boolean autobrightnessEnabled, - boolean brightnessIsTemporary) { - final float brightnessInNits = convertToAdjustedNits(brightness); + boolean brightnessIsTemporary, boolean shouldUseAutoBrightness) { + final float brightnessInNits = + mDisplayBrightnessController.convertToAdjustedNits(brightness); // Don't report brightness to brightnessTracker: // If brightness is temporary (ie the slider has not been released) // or if we are in idle screen brightness mode. @@ -2915,12 +2481,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call || mAutomaticBrightnessController.isInIdleMode() || !autobrightnessEnabled || mBrightnessTracker == null - || !mUseAutoBrightness + || !shouldUseAutoBrightness || brightnessInNits < 0.0f) { return; } - if (userInitiated && !mAutomaticBrightnessController.hasValidAmbientLux()) { + if (userInitiated && (mAutomaticBrightnessController == null + || !mAutomaticBrightnessController.hasValidAmbientLux())) { // If we don't have a valid lux reading we can't report a valid // slider event so notify as if the system changed the brightness. userInitiated = false; @@ -2939,96 +2506,33 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mAutomaticBrightnessController.getLastSensorTimestamps()); } - private float convertToNits(float brightness) { - if (mAutomaticBrightnessController == null) { - return BrightnessMappingStrategy.INVALID_NITS; - } - return mAutomaticBrightnessController.convertToNits(brightness); - } - - private float convertToAdjustedNits(float brightness) { - if (mAutomaticBrightnessController == null) { - return BrightnessMappingStrategy.INVALID_NITS; + @Override + public void addDisplayBrightnessFollower(DisplayPowerControllerInterface follower) { + synchronized (mLock) { + mDisplayBrightnessFollowers.append(follower.getDisplayId(), follower); + sendUpdatePowerStateLocked(); } - return mAutomaticBrightnessController.convertToAdjustedNits(brightness); } - private float getBrightnessFromNits(float nits) { - if (mAutomaticBrightnessController == null) { - return PowerManager.BRIGHTNESS_INVALID_FLOAT; + @Override + public void removeDisplayBrightnessFollower(DisplayPowerControllerInterface follower) { + synchronized (mLock) { + mDisplayBrightnessFollowers.remove(follower.getDisplayId()); + mHandler.postAtTime(() -> follower.setBrightnessToFollow( + PowerManager.BRIGHTNESS_INVALID_FLOAT, BrightnessMappingStrategy.INVALID_NITS, + /* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis()); } - return mAutomaticBrightnessController.getBrightnessFromNits(nits); } @GuardedBy("mLock") - private void updatePendingProximityRequestsLocked() { - mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked; - mPendingWaitForNegativeProximityLocked = false; - - if (mIgnoreProximityUntilChanged) { - // Also, lets stop waiting for negative proximity if we're ignoring it. - mWaitingForNegativeProximity = false; - } - } - - private void ignoreProximitySensorUntilChangedInternal() { - if (!mIgnoreProximityUntilChanged - && mProximity == PROXIMITY_POSITIVE) { - // Only ignore if it is still reporting positive (near) - mIgnoreProximityUntilChanged = true; - Slog.i(mTag, "Ignoring proximity"); - updatePowerState(); - } - } - - private final Runnable mOnStateChangedRunnable = new Runnable() { - @Override - public void run() { - mOnStateChangedPending = false; - mCallbacks.onStateChanged(); - mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdOnStateChanged); - } - }; - - private void sendOnProximityPositiveWithWakelock() { - mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdProxPositive); - mHandler.post(mOnProximityPositiveRunnable); - mOnProximityPositiveMessages++; - } - - private final Runnable mOnProximityPositiveRunnable = new Runnable() { - @Override - public void run() { - mOnProximityPositiveMessages--; - mCallbacks.onProximityPositive(); - mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdProxPositive); - } - }; - - private void sendOnProximityNegativeWithWakelock() { - mOnProximityNegativeMessages++; - mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdProxNegative); - mHandler.post(mOnProximityNegativeRunnable); - } - - private final Runnable mOnProximityNegativeRunnable = new Runnable() { - @Override - public void run() { - mOnProximityNegativeMessages--; - mCallbacks.onProximityNegative(); - mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdProxNegative); + private void clearDisplayBrightnessFollowersLocked() { + for (int i = 0; i < mDisplayBrightnessFollowers.size(); i++) { + DisplayPowerControllerInterface follower = mDisplayBrightnessFollowers.valueAt(i); + mHandler.postAtTime(() -> follower.setBrightnessToFollow( + PowerManager.BRIGHTNESS_INVALID_FLOAT, BrightnessMappingStrategy.INVALID_NITS, + /* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis()); } - }; - - /** - * Indicates whether the display state is ready to update. If this is the default display, we - * want to update it right away so that we can draw the boot animation on it. If it is not - * the default display, drawing the boot animation on it would look incorrect, so we need - * to wait until boot is completed. - * @return True if the display state is ready to update - */ - private boolean readyToUpdateDisplayState() { - return mDisplayId == Display.DEFAULT_DISPLAY || mBootCompleted; + mDisplayBrightnessFollowers.clear(); } @Override @@ -3040,31 +2544,23 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call pw.println(" mLeadDisplayId=" + mLeadDisplayId); pw.println(" mLightSensor=" + mLightSensor); pw.println(" mDisplayBrightnessFollowers=" + mDisplayBrightnessFollowers); - pw.println(" mDozeStateOverride=" + mDozeStateOverride); pw.println(); pw.println("Display Power Controller Locked State:"); pw.println(" mDisplayReadyLocked=" + mDisplayReadyLocked); pw.println(" mPendingRequestLocked=" + mPendingRequestLocked); pw.println(" mPendingRequestChangedLocked=" + mPendingRequestChangedLocked); - pw.println(" mPendingWaitForNegativeProximityLocked=" - + mPendingWaitForNegativeProximityLocked); pw.println(" mPendingUpdatePowerStateLocked=" + mPendingUpdatePowerStateLocked); } pw.println(); pw.println("Display Power Controller Configuration:"); - pw.println(" mScreenBrightnessRangeDefault=" + mScreenBrightnessDefault); pw.println(" mScreenBrightnessDozeConfig=" + mScreenBrightnessDozeConfig); - pw.println(" mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig); pw.println(" mUseSoftwareAutoBrightnessConfig=" + mUseSoftwareAutoBrightnessConfig); - pw.println(" mAllowAutoBrightnessWhileDozingConfig=" - + mAllowAutoBrightnessWhileDozingConfig); - pw.println(" mPersistBrightnessNitsForDefaultDisplay=" - + mPersistBrightnessNitsForDefaultDisplay); pw.println(" mSkipScreenOnBrightnessRamp=" + mSkipScreenOnBrightnessRamp); pw.println(" mColorFadeFadesConfig=" + mColorFadeFadesConfig); pw.println(" mColorFadeEnabled=" + mColorFadeEnabled); + pw.println(" mIsDisplayInternal=" + mIsDisplayInternal); synchronized (mCachedBrightnessInfo) { pw.println(" mCachedBrightnessInfo.brightness=" + mCachedBrightnessInfo.brightness.value); @@ -3082,7 +2578,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } pw.println(" mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig); pw.println(" mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig); - mHandler.runWithScissors(() -> dumpLocal(pw), 1000); } @@ -3090,35 +2585,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call pw.println(); pw.println("Display Power Controller Thread State:"); pw.println(" mPowerRequest=" + mPowerRequest); - pw.println(" mUnfinishedBusiness=" + mUnfinishedBusiness); - pw.println(" mWaitingForNegativeProximity=" + mWaitingForNegativeProximity); - pw.println(" mProximitySensor=" + mProximitySensor); - pw.println(" mProximitySensorEnabled=" + mProximitySensorEnabled); - pw.println(" mProximityThreshold=" + mProximityThreshold); - pw.println(" mProximity=" + proximityToString(mProximity)); - pw.println(" mPendingProximity=" + proximityToString(mPendingProximity)); - pw.println(" mPendingProximityDebounceTime=" - + TimeUtils.formatUptime(mPendingProximityDebounceTime)); - pw.println(" mScreenOffBecauseOfProximity=" + mScreenOffBecauseOfProximity); - pw.println(" mLastUserSetScreenBrightness=" + mLastUserSetScreenBrightness); - pw.println(" mPendingScreenBrightnessSetting=" - + mPendingScreenBrightnessSetting); - pw.println(" mTemporaryScreenBrightness=" + mTemporaryScreenBrightness); - pw.println(" mBrightnessToFollow=" + mBrightnessToFollow); - pw.println(" mBrightnessToFollowSlowChange=" + mBrightnessToFollowSlowChange); - pw.println(" mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment); pw.println(" mBrightnessReason=" + mBrightnessReason); - pw.println(" mTemporaryAutoBrightnessAdjustment=" + mTemporaryAutoBrightnessAdjustment); - pw.println(" mPendingAutoBrightnessAdjustment=" + mPendingAutoBrightnessAdjustment); - pw.println(" mAppliedAutoBrightness=" + mAppliedAutoBrightness); pw.println(" mAppliedDimming=" + mAppliedDimming); - pw.println(" mAppliedLowPower=" + mAppliedLowPower); pw.println(" mAppliedThrottling=" + mAppliedThrottling); - pw.println(" mAppliedScreenBrightnessOverride=" + mAppliedScreenBrightnessOverride); - pw.println(" mAppliedTemporaryBrightness=" + mAppliedTemporaryBrightness); - pw.println(" mAppliedTemporaryAutoBrightnessAdjustment=" - + mAppliedTemporaryAutoBrightnessAdjustment); - pw.println(" mAppliedBrightnessBoost=" + mAppliedBrightnessBoost); pw.println(" mDozing=" + mDozing); pw.println(" mSkipRampState=" + skipRampStateToString(mSkipRampState)); pw.println(" mScreenOnBlockStartRealTime=" + mScreenOnBlockStartRealTime); @@ -3129,9 +2598,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call pw.println(" mReportedToPolicy=" + reportedToPolicyToString(mReportedScreenStateToPolicy)); pw.println(" mIsRbcActive=" + mIsRbcActive); - pw.println(" mOnStateChangePending=" + mOnStateChangedPending); - pw.println(" mOnProximityPositiveMessages=" + mOnProximityPositiveMessages); - pw.println(" mOnProximityNegativeMessages=" + mOnProximityNegativeMessages); + IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + mAutomaticBrightnessStrategy.dump(ipw); if (mScreenBrightnessRampAnimator != null) { pw.println(" mScreenBrightnessRampAnimator.isAnimating()=" @@ -3156,6 +2624,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call dumpBrightnessEvents(pw); } + dumpRbcEvents(pw); + if (mScreenOffBrightnessSensorController != null) { mScreenOffBrightnessSensorController.dump(pw); } @@ -3173,21 +2643,30 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mDisplayWhiteBalanceController.dump(pw); mDisplayWhiteBalanceSettings.dump(pw); } - } - private static String proximityToString(int state) { - switch (state) { - case PROXIMITY_UNKNOWN: - return "Unknown"; - case PROXIMITY_NEGATIVE: - return "Negative"; - case PROXIMITY_POSITIVE: - return "Positive"; - default: - return Integer.toString(state); + pw.println(); + + if (mWakelockController != null) { + mWakelockController.dumpLocal(pw); + } + + pw.println(); + if (mDisplayBrightnessController != null) { + mDisplayBrightnessController.dump(pw); + } + + pw.println(); + if (mDisplayStateController != null) { + mDisplayStateController.dumpsys(pw); + } + + pw.println(); + if (mBrightnessClamperController != null) { + mBrightnessClamperController.dump(ipw); } } + private static String reportedToPolicyToString(int state) { switch (state) { case REPORTED_TO_POLICY_SCREEN_OFF: @@ -3228,15 +2707,21 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } - private static float clampAbsoluteBrightness(float value) { - return MathUtils.constrain(value, PowerManager.BRIGHTNESS_MIN, - PowerManager.BRIGHTNESS_MAX); - } + private void dumpRbcEvents(PrintWriter pw) { + int size = mRbcEventRingBuffer.size(); + if (size < 1) { + pw.println("No Reduce Bright Colors Adjustments"); + return; + } - private static float clampAutoBrightnessAdjustment(float value) { - return MathUtils.constrain(value, -1.0f, 1.0f); + pw.println("Reduce Bright Colors Adjustments Last " + size + " Events: "); + BrightnessEvent[] eventArray = mRbcEventRingBuffer.toArray(); + for (int i = 0; i < mRbcEventRingBuffer.size(); i++) { + pw.println(" " + eventArray[i]); + } } + private void noteScreenState(int screenState) { // Log screen state change with display id FrameworkStatsLog.write(FrameworkStatsLog.SCREEN_STATE_CHANGED_V2, @@ -3363,20 +2848,21 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // It's easier to check if the brightness is at maximum level using the brightness // value untouched by any modifiers boolean brightnessIsMax = unmodifiedBrightness == event.getHbmMax(); - float brightnessInNits = convertToAdjustedNits(event.getBrightness()); + float brightnessInNits = + mDisplayBrightnessController.convertToAdjustedNits(event.getBrightness()); float appliedLowPowerMode = event.isLowPowerModeSet() ? event.getPowerFactor() : -1f; int appliedRbcStrength = event.isRbcEnabled() ? event.getRbcStrength() : -1; float appliedHbmMaxNits = event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF - ? -1f : convertToAdjustedNits(event.getHbmMax()); + ? -1f : mDisplayBrightnessController.convertToAdjustedNits(event.getHbmMax()); // thermalCapNits set to -1 if not currently capping max brightness float appliedThermalCapNits = event.getThermalMax() == PowerManager.BRIGHTNESS_MAX - ? -1f : convertToAdjustedNits(event.getThermalMax()); - + ? -1f : mDisplayBrightnessController.convertToAdjustedNits(event.getThermalMax()); if (mIsDisplayInternal) { FrameworkStatsLog.write(FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED, - convertToAdjustedNits(event.getInitialBrightness()), + mDisplayBrightnessController + .convertToAdjustedNits(event.getInitialBrightness()), brightnessInNits, event.getLux(), event.getPhysicalDisplayId(), @@ -3393,7 +2879,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT, event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, (modifier & BrightnessReason.MODIFIER_LOW_POWER) > 0, - mBrightnessThrottler.getBrightnessMaxReason(), + mBrightnessClamperController.getBrightnessMaxReason(), + // TODO: (flc) add brightnessMinReason here too. (modifier & BrightnessReason.MODIFIER_DIMMED) > 0, event.isRbcEnabled(), (flags & BrightnessEvent.FLAG_INVALID_LUX) > 0, @@ -3404,6 +2891,17 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } + /** + * Indicates whether the display state is ready to update. If this is the default display, we + * want to update it right away so that we can draw the boot animation on it. If it is not + * the default display, drawing the boot animation on it would look incorrect, so we need + * to wait until boot is completed. + * @return True if the display state is ready to update + */ + private boolean readyToUpdateDisplayState() { + return mDisplayId == Display.DEFAULT_DISPLAY || mBootCompleted; + } + private final class DisplayControllerHandler extends Handler { DisplayControllerHandler(Looper looper) { super(looper, null, true /*async*/); @@ -3416,10 +2914,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call updatePowerState(); break; - case MSG_PROXIMITY_SENSOR_DEBOUNCED: - debounceProximitySensor(); - break; - case MSG_SCREEN_ON_UNBLOCKED: if (mPendingScreenOnUnblocker == msg.obj) { unblockScreenOn(); @@ -3432,27 +2926,38 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call updatePowerState(); } break; + case MSG_OFFLOADING_SCREEN_ON_UNBLOCKED: + if (mDisplayOffloadSession == msg.obj) { + unblockScreenOnByDisplayOffload(); + updatePowerState(); + } + break; case MSG_CONFIGURE_BRIGHTNESS: - mBrightnessConfiguration = (BrightnessConfiguration) msg.obj; - mShouldResetShortTermModel = msg.arg1 == 1; + BrightnessConfiguration brightnessConfiguration = + (BrightnessConfiguration) msg.obj; + mAutomaticBrightnessStrategy.setBrightnessConfiguration(brightnessConfiguration, + msg.arg1 == 1); + if (mBrightnessTracker != null) { + mBrightnessTracker + .setShouldCollectColorSample(brightnessConfiguration != null + && brightnessConfiguration.shouldCollectColorSamples()); + } updatePowerState(); break; case MSG_SET_TEMPORARY_BRIGHTNESS: // TODO: Should we have a a timeout for the temporary brightness? - mTemporaryScreenBrightness = Float.intBitsToFloat(msg.arg1); + mDisplayBrightnessController + .setTemporaryBrightness(Float.intBitsToFloat(msg.arg1)); updatePowerState(); break; case MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT: - mTemporaryAutoBrightnessAdjustment = Float.intBitsToFloat(msg.arg1); + mAutomaticBrightnessStrategy + .setTemporaryAutoBrightnessAdjustment(Float.intBitsToFloat(msg.arg1)); updatePowerState(); break; - case MSG_IGNORE_PROXIMITY: - ignoreProximitySensorUntilChangedInternal(); - break; - case MSG_STOP: cleanupHandlerThreadAfterStop(); break; @@ -3500,27 +3005,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call case MSG_SET_DWBC_LOGGING_ENABLED: setDwbcLoggingEnabled(msg.arg1); break; + case MSG_SET_BRIGHTNESS_FROM_OFFLOAD: + mDisplayBrightnessController.setBrightnessFromOffload( + Float.intBitsToFloat(msg.arg1)); + updatePowerState(); + break; } } } - private final SensorEventListener mProximitySensorListener = new SensorEventListener() { - @Override - public void onSensorChanged(SensorEvent event) { - if (mProximitySensorEnabled) { - final long time = mClock.uptimeMillis(); - final float distance = event.values[0]; - boolean positive = distance >= 0.0f && distance < mProximityThreshold; - handleProximitySensorEvent(time, positive); - } - } - - @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) { - // Not used. - } - }; - private final class SettingsObserver extends ContentObserver { SettingsObserver(Handler handler) { @@ -3531,6 +3024,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call public void onChange(boolean selfChange, Uri uri) { if (uri.equals(Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE))) { handleBrightnessModeChange(); + } else if (uri.equals(Settings.System.getUriFor( + Settings.System.SCREEN_BRIGHTNESS_FOR_ALS))) { + int preset = Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_FOR_ALS, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL, + UserHandle.USER_CURRENT); + Slog.i(mTag, "Setting up auto-brightness for preset " + + autoBrightnessPresetToString(preset)); + setUpAutoBrightness(mContext, mHandler); + sendUpdatePowerState(); } else { handleSettingsChange(false /* userSwitch */); } @@ -3581,28 +3084,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call msg.sendToTarget(); } - @VisibleForTesting - String getSuspendBlockerUnfinishedBusinessId(int displayId) { - return "[" + displayId + "]unfinished business"; - } - - String getSuspendBlockerOnStateChangedId(int displayId) { - return "[" + displayId + "]on state changed"; - } - - String getSuspendBlockerProxPositiveId(int displayId) { - return "[" + displayId + "]prox positive"; - } - - String getSuspendBlockerProxNegativeId(int displayId) { - return "[" + displayId + "]prox negative"; - } - - @VisibleForTesting - String getSuspendBlockerProxDebounceId(int displayId) { - return "[" + displayId + "]prox debounce"; - } - /** Functional interface for providing time. */ @VisibleForTesting interface Clock { @@ -3629,6 +3110,20 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call return new DualRampAnimator(dps, firstProperty, secondProperty); } + WakelockController getWakelockController(int displayId, + DisplayPowerCallbacks displayPowerCallbacks) { + return new WakelockController(displayId, displayPowerCallbacks); + } + + DisplayPowerProximityStateController getDisplayPowerProximityStateController( + WakelockController wakelockController, DisplayDeviceConfig displayDeviceConfig, + Looper looper, Runnable nudgeUpdatePowerState, + int displayId, SensorManager sensorManager) { + return new DisplayPowerProximityStateController(wakelockController, displayDeviceConfig, + looper, nudgeUpdatePowerState, + displayId, sensorManager, /* injector= */ null); + } + AutomaticBrightnessController getAutomaticBrightnessController( AutomaticBrightnessController.Callbacks callbacks, Looper looper, SensorManager sensorManager, Sensor lightSensor, @@ -3711,11 +3206,32 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call hbmChangeCallback, hbmMetadata, context); } + BrightnessRangeController getBrightnessRangeController( + HighBrightnessModeController hbmController, Runnable modeChangeCallback, + DisplayDeviceConfig displayDeviceConfig, Handler handler, + DisplayManagerFlags flags, IBinder displayToken, DisplayDeviceInfo info) { + return new BrightnessRangeController(hbmController, + modeChangeCallback, displayDeviceConfig, handler, flags, displayToken, info); + } + + BrightnessClamperController getBrightnessClamperController(Handler handler, + BrightnessClamperController.ClamperChangeListener clamperChangeListener, + BrightnessClamperController.DisplayDeviceData data, Context context, + DisplayManagerFlags flags) { + + return new BrightnessClamperController(handler, clamperChangeListener, data, context, + flags); + } + DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler, SensorManager sensorManager, Resources resources) { return DisplayWhiteBalanceFactory.create(handler, sensorManager, resources); } + + boolean isColorFadeEnabled() { + return !ActivityManager.isLowRamDeviceStatic(); + } } static class CachedBrightnessInfo { diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java deleted file mode 100644 index 2d860c0cc673..000000000000 --- a/services/core/java/com/android/server/display/DisplayPowerController2.java +++ /dev/null @@ -1,3267 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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.display; - -import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; -import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE; -import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE; -import static com.android.server.display.config.DisplayBrightnessMappingConfig.autoBrightnessPresetToString; - -import android.animation.Animator; -import android.animation.ObjectAnimator; -import android.annotation.Nullable; -import android.annotation.SuppressLint; -import android.annotation.UserIdInt; -import android.app.ActivityManager; -import android.content.Context; -import android.content.pm.ParceledListSlice; -import android.content.res.Resources; -import android.database.ContentObserver; -import android.hardware.Sensor; -import android.hardware.SensorManager; -import android.hardware.display.AmbientBrightnessDayStats; -import android.hardware.display.BrightnessChangeEvent; -import android.hardware.display.BrightnessConfiguration; -import android.hardware.display.BrightnessInfo; -import android.hardware.display.DisplayManagerInternal; -import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession; -import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks; -import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; -import android.metrics.LogMaker; -import android.net.Uri; -import android.os.Handler; -import android.os.HandlerExecutor; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; -import android.os.PowerManager; -import android.os.RemoteException; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.os.Trace; -import android.os.UserHandle; -import android.provider.Settings; -import android.util.FloatProperty; -import android.util.IndentingPrintWriter; -import android.util.MathUtils; -import android.util.MutableFloat; -import android.util.MutableInt; -import android.util.Slog; -import android.util.SparseArray; -import android.view.Display; - -import com.android.internal.R; -import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.app.IBatteryStats; -import com.android.internal.display.BrightnessSynchronizer; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.internal.util.FrameworkStatsLog; -import com.android.internal.util.RingBuffer; -import com.android.server.LocalServices; -import com.android.server.am.BatteryStatsService; -import com.android.server.display.RampAnimator.DualRampAnimator; -import com.android.server.display.brightness.BrightnessEvent; -import com.android.server.display.brightness.BrightnessReason; -import com.android.server.display.brightness.BrightnessUtils; -import com.android.server.display.brightness.DisplayBrightnessController; -import com.android.server.display.brightness.clamper.BrightnessClamperController; -import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy; -import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal; -import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener; -import com.android.server.display.feature.DisplayManagerFlags; -import com.android.server.display.layout.Layout; -import com.android.server.display.state.DisplayStateController; -import com.android.server.display.utils.DebugUtils; -import com.android.server.display.utils.SensorUtils; -import com.android.server.display.whitebalance.DisplayWhiteBalanceController; -import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory; -import com.android.server.display.whitebalance.DisplayWhiteBalanceSettings; -import com.android.server.policy.WindowManagerPolicy; - -import java.io.PrintWriter; -import java.util.Objects; - -/** - * Controls the power state of the display. - * - * Handles the proximity sensor, light sensor, and animations between states - * including the screen off animation. - * - * This component acts independently of the rest of the power manager service. - * In particular, it does not share any state and it only communicates - * via asynchronous callbacks to inform the power manager that something has - * changed. - * - * Everything this class does internally is serialized on its handler although - * it may be accessed by other threads from the outside. - * - * Note that the power manager service guarantees that it will hold a suspend - * blocker as long as the display is not ready. So most of the work done here - * does not need to worry about holding a suspend blocker unless it happens - * independently of the display ready signal. - * - * For debugging, you can make the color fade and brightness animations run - * slower by changing the "animator duration scale" option in Development Settings. - */ -final class DisplayPowerController2 implements AutomaticBrightnessController.Callbacks, - DisplayWhiteBalanceController.Callbacks, DisplayPowerControllerInterface { - private static final String SCREEN_ON_BLOCKED_TRACE_NAME = "Screen on blocked"; - private static final String SCREEN_OFF_BLOCKED_TRACE_NAME = "Screen off blocked"; - - private static final String TAG = "DisplayPowerController2"; - // To enable these logs, run: - // 'adb shell setprop persist.log.tag.DisplayPowerController2 DEBUG && adb reboot' - private static final boolean DEBUG = DebugUtils.isDebuggable(TAG); - private static final String SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME = - "Screen on blocked by displayoffload"; - - // If true, uses the color fade on animation. - // We might want to turn this off if we cannot get a guarantee that the screen - // actually turns on and starts showing new content after the call to set the - // screen state returns. Playing the animation can also be somewhat slow. - private static final boolean USE_COLOR_FADE_ON_ANIMATION = false; - - private static final float SCREEN_ANIMATION_RATE_MINIMUM = 0.0f; - - private static final int COLOR_FADE_ON_ANIMATION_DURATION_MILLIS = 250; - private static final int COLOR_FADE_OFF_ANIMATION_DURATION_MILLIS = 400; - - private static final int MSG_UPDATE_POWER_STATE = 1; - private static final int MSG_SCREEN_ON_UNBLOCKED = 2; - private static final int MSG_SCREEN_OFF_UNBLOCKED = 3; - private static final int MSG_CONFIGURE_BRIGHTNESS = 4; - private static final int MSG_SET_TEMPORARY_BRIGHTNESS = 5; - private static final int MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT = 6; - private static final int MSG_STOP = 7; - private static final int MSG_UPDATE_BRIGHTNESS = 8; - private static final int MSG_UPDATE_RBC = 9; - private static final int MSG_BRIGHTNESS_RAMP_DONE = 10; - private static final int MSG_STATSD_HBM_BRIGHTNESS = 11; - private static final int MSG_SWITCH_USER = 12; - private static final int MSG_BOOT_COMPLETED = 13; - private static final int MSG_SET_DWBC_STRONG_MODE = 14; - private static final int MSG_SET_DWBC_COLOR_OVERRIDE = 15; - private static final int MSG_SET_DWBC_LOGGING_ENABLED = 16; - private static final int MSG_SET_BRIGHTNESS_FROM_OFFLOAD = 17; - private static final int MSG_OFFLOADING_SCREEN_ON_UNBLOCKED = 18; - - - - private static final int BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS = 500; - - - // State machine constants for tracking initial brightness ramp skipping when enabled. - private static final int RAMP_STATE_SKIP_NONE = 0; - private static final int RAMP_STATE_SKIP_INITIAL = 1; - private static final int RAMP_STATE_SKIP_AUTOBRIGHT = 2; - - private static final int REPORTED_TO_POLICY_UNREPORTED = -1; - private static final int REPORTED_TO_POLICY_SCREEN_OFF = 0; - private static final int REPORTED_TO_POLICY_SCREEN_TURNING_ON = 1; - private static final int REPORTED_TO_POLICY_SCREEN_ON = 2; - private static final int REPORTED_TO_POLICY_SCREEN_TURNING_OFF = 3; - - private static final int RINGBUFFER_MAX = 100; - private static final int RINGBUFFER_RBC_MAX = 20; - - private static final float[] BRIGHTNESS_RANGE_BOUNDARIES = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80, - 90, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1200, - 1400, 1600, 1800, 2000, 2250, 2500, 2750, 3000}; - private static final int[] BRIGHTNESS_RANGE_INDEX = { - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_UNKNOWN, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_0_1, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_1_2, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_2_3, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_3_4, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_4_5, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_5_6, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_6_7, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_7_8, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_8_9, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_9_10, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_10_20, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_20_30, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_30_40, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_40_50, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_50_60, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_60_70, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_70_80, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_80_90, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_90_100, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_100_200, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_200_300, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_300_400, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_400_500, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_500_600, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_600_700, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_700_800, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_800_900, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_900_1000, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_1000_1200, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_1200_1400, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_1400_1600, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_1600_1800, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_1800_2000, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_2000_2250, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_2250_2500, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_2500_2750, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_2750_3000, - }; - - private final String mTag; - - private final Object mLock = new Object(); - - private final Context mContext; - - // Our handler. - private final DisplayControllerHandler mHandler; - - // Battery stats. - @Nullable - private final IBatteryStats mBatteryStats; - - // The sensor manager. - private final SensorManager mSensorManager; - - // The window manager policy. - private final WindowManagerPolicy mWindowManagerPolicy; - - // The display blanker. - private final DisplayBlanker mBlanker; - - // The LogicalDisplay tied to this DisplayPowerController2. - private final LogicalDisplay mLogicalDisplay; - - // The ID of the LogicalDisplay tied to this DisplayPowerController2. - private final int mDisplayId; - - // The ID of the display which this display follows for brightness purposes. - private int mLeadDisplayId = Layout.NO_LEAD_DISPLAY; - - // The unique ID of the primary display device currently tied to this logical display - private String mUniqueDisplayId; - - // Tracker for brightness changes. - @Nullable - private final BrightnessTracker mBrightnessTracker; - - // Tracker for brightness settings changes. - private final SettingsObserver mSettingsObserver; - - // The doze screen brightness. - private final float mScreenBrightnessDozeConfig; - - // True if auto-brightness should be used. - private boolean mUseSoftwareAutoBrightnessConfig; - - // Whether or not the color fade on screen on / off is enabled. - private final boolean mColorFadeEnabled; - - @GuardedBy("mCachedBrightnessInfo") - private final CachedBrightnessInfo mCachedBrightnessInfo = new CachedBrightnessInfo(); - - private DisplayDevice mDisplayDevice; - - // True if we should fade the screen while turning it off, false if we should play - // a stylish color fade animation instead. - private final boolean mColorFadeFadesConfig; - - // True if we need to fake a transition to off when coming out of a doze state. - // Some display hardware will blank itself when coming out of doze in order to hide - // artifacts. For these displays we fake a transition into OFF so that policy can appropriately - // blank itself and begin an appropriate power on animation. - private final boolean mDisplayBlanksAfterDozeConfig; - - // True if there are only buckets of brightness values when the display is in the doze state, - // rather than a full range of values. If this is true, then we'll avoid animating the screen - // brightness since it'd likely be multiple jarring brightness transitions instead of just one - // to reach the final state. - private final boolean mBrightnessBucketsInDozeConfig; - - private final Clock mClock; - private final Injector mInjector; - - // Maximum time a ramp animation can take. - private long mBrightnessRampIncreaseMaxTimeMillis; - private long mBrightnessRampDecreaseMaxTimeMillis; - - // Maximum time a ramp animation can take in idle mode. - private long mBrightnessRampIncreaseMaxTimeIdleMillis; - private long mBrightnessRampDecreaseMaxTimeIdleMillis; - - // The pending power request. - // Initially null until the first call to requestPowerState. - @GuardedBy("mLock") - private DisplayPowerRequest mPendingRequestLocked; - - // True if the pending power request or wait for negative proximity flag - // has been changed since the last update occurred. - @GuardedBy("mLock") - private boolean mPendingRequestChangedLocked; - - // Set to true when the important parts of the pending power request have been applied. - // The important parts are mainly the screen state. Brightness changes may occur - // concurrently. - @GuardedBy("mLock") - private boolean mDisplayReadyLocked; - - // Set to true if a power state update is required. - @GuardedBy("mLock") - private boolean mPendingUpdatePowerStateLocked; - - /* The following state must only be accessed by the handler thread. */ - - // The currently requested power state. - // The power controller will progressively update its internal state to match - // the requested power state. Initially null until the first update. - private DisplayPowerRequest mPowerRequest; - - // The current power state. - // Must only be accessed on the handler thread. - private DisplayPowerState mPowerState; - - - - // The currently active screen on unblocker. This field is non-null whenever - // we are waiting for a callback to release it and unblock the screen. - private ScreenOnUnblocker mPendingScreenOnUnblocker; - private ScreenOffUnblocker mPendingScreenOffUnblocker; - private Runnable mPendingScreenOnUnblockerByDisplayOffload; - - // True if we were in the process of turning off the screen. - // This allows us to recover more gracefully from situations where we abort - // turning off the screen. - private boolean mPendingScreenOff; - - // The elapsed real time when the screen on was blocked. - private long mScreenOnBlockStartRealTime; - private long mScreenOffBlockStartRealTime; - private long mScreenOnBlockByDisplayOffloadStartRealTime; - - // Screen state we reported to policy. Must be one of REPORTED_TO_POLICY_* fields. - private int mReportedScreenStateToPolicy = REPORTED_TO_POLICY_UNREPORTED; - - // Used to deduplicate the displayoffload blocking screen on logic. One block per turning on. - // This value is reset when screen on is reported or the blocking is cancelled. - private boolean mScreenTurningOnWasBlockedByDisplayOffload; - - // If the last recorded screen state was dozing or not. - private boolean mDozing; - - private boolean mAppliedDimming; - - private boolean mAppliedThrottling; - - // Reason for which the brightness was last changed. See {@link BrightnessReason} for more - // information. - // At the time of this writing, this value is changed within updatePowerState() only, which is - // limited to the thread used by DisplayControllerHandler. - private final BrightnessReason mBrightnessReason = new BrightnessReason(); - private final BrightnessReason mBrightnessReasonTemp = new BrightnessReason(); - - // Brightness animation ramp rates in brightness units per second - private float mBrightnessRampRateFastDecrease; - private float mBrightnessRampRateFastIncrease; - private float mBrightnessRampRateSlowDecrease; - private float mBrightnessRampRateSlowIncrease; - private float mBrightnessRampRateSlowDecreaseIdle; - private float mBrightnessRampRateSlowIncreaseIdle; - - // Report HBM brightness change to StatsD - private int mDisplayStatsId; - private float mLastStatsBrightness = PowerManager.BRIGHTNESS_MIN; - - // Whether or not to skip the initial brightness ramps into STATE_ON. - private final boolean mSkipScreenOnBrightnessRamp; - - // Display white balance components. - // Critical methods must be called on DPC2 handler thread. - @Nullable - private final DisplayWhiteBalanceSettings mDisplayWhiteBalanceSettings; - @Nullable - private final DisplayWhiteBalanceController mDisplayWhiteBalanceController; - - @Nullable - private final ColorDisplayServiceInternal mCdsi; - private float[] mNitsRange; - - private final BrightnessRangeController mBrightnessRangeController; - - private final BrightnessThrottler mBrightnessThrottler; - - private final BrightnessClamperController mBrightnessClamperController; - - private final Runnable mOnBrightnessChangeRunnable; - - private final BrightnessEvent mLastBrightnessEvent; - private final BrightnessEvent mTempBrightnessEvent; - - private final DisplayBrightnessController mDisplayBrightnessController; - - // Keeps a record of brightness changes for dumpsys. - private RingBuffer<BrightnessEvent> mBrightnessEventRingBuffer; - - // Keeps a record of rbc changes for dumpsys. - private final RingBuffer<BrightnessEvent> mRbcEventRingBuffer = - new RingBuffer<>(BrightnessEvent.class, RINGBUFFER_RBC_MAX); - - // Controls and tracks all the wakelocks that are acquired/released by the system. Also acts as - // a medium of communication between this class and the PowerManagerService. - private final WakelockController mWakelockController; - - // Tracks and manages the proximity state of the associated display. - private final DisplayPowerProximityStateController mDisplayPowerProximityStateController; - - // Tracks and manages the display state of the associated display. - private final DisplayStateController mDisplayStateController; - - - // Responsible for evaluating and tracking the automatic brightness relevant states. - // Todo: This is a temporary workaround. Ideally DPC2 should never talk to the strategies - private final AutomaticBrightnessStrategy mAutomaticBrightnessStrategy; - - // A record of state for skipping brightness ramps. - private int mSkipRampState = RAMP_STATE_SKIP_NONE; - - // The first autobrightness value set when entering RAMP_STATE_SKIP_INITIAL. - private float mInitialAutoBrightness; - - // The controller for the automatic brightness level. - @Nullable - private AutomaticBrightnessController mAutomaticBrightnessController; - - // The controller for the sensor used to estimate ambient lux while the display is off. - @Nullable - private ScreenOffBrightnessSensorController mScreenOffBrightnessSensorController; - - private Sensor mLightSensor; - private Sensor mScreenOffBrightnessSensor; - - private boolean mIsRbcActive; - - // Animators. - private ObjectAnimator mColorFadeOnAnimator; - private ObjectAnimator mColorFadeOffAnimator; - private DualRampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator; - - // True if this DisplayPowerController2 has been stopped and should no longer be running. - private boolean mStopped; - - private DisplayDeviceConfig mDisplayDeviceConfig; - - private boolean mIsEnabled; - private boolean mIsInTransition; - private boolean mIsDisplayInternal; - - // The id of the thermal brightness throttling policy that should be used. - private String mThermalBrightnessThrottlingDataId; - - // DPCs following the brightness of this DPC. This is used in concurrent displays mode - there - // is one lead display, the additional displays follow the brightness value of the lead display. - @GuardedBy("mLock") - private SparseArray<DisplayPowerControllerInterface> mDisplayBrightnessFollowers = - new SparseArray(); - - private boolean mBootCompleted; - private final DisplayManagerFlags mFlags; - - private DisplayOffloadSession mDisplayOffloadSession; - - /** - * Creates the display power controller. - */ - DisplayPowerController2(Context context, Injector injector, - DisplayPowerCallbacks callbacks, Handler handler, - SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay, - BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting, - Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata, - boolean bootCompleted, DisplayManagerFlags flags) { - mFlags = flags; - mInjector = injector != null ? injector : new Injector(); - mClock = mInjector.getClock(); - mLogicalDisplay = logicalDisplay; - mDisplayId = mLogicalDisplay.getDisplayIdLocked(); - mSensorManager = sensorManager; - mHandler = new DisplayControllerHandler(handler.getLooper()); - mDisplayDeviceConfig = logicalDisplay.getPrimaryDisplayDeviceLocked() - .getDisplayDeviceConfig(); - mIsEnabled = logicalDisplay.isEnabledLocked(); - mIsInTransition = logicalDisplay.isInTransitionLocked(); - mIsDisplayInternal = logicalDisplay.getPrimaryDisplayDeviceLocked() - .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL; - mWakelockController = mInjector.getWakelockController(mDisplayId, callbacks); - mDisplayPowerProximityStateController = mInjector.getDisplayPowerProximityStateController( - mWakelockController, mDisplayDeviceConfig, mHandler.getLooper(), - () -> updatePowerState(), mDisplayId, mSensorManager); - mDisplayStateController = new DisplayStateController(mDisplayPowerProximityStateController); - mTag = TAG + "[" + mDisplayId + "]"; - mThermalBrightnessThrottlingDataId = - logicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId; - mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked(); - mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId(); - mDisplayStatsId = mUniqueDisplayId.hashCode(); - - mLastBrightnessEvent = new BrightnessEvent(mDisplayId); - mTempBrightnessEvent = new BrightnessEvent(mDisplayId); - - if (mDisplayId == Display.DEFAULT_DISPLAY) { - mBatteryStats = BatteryStatsService.getService(); - } else { - mBatteryStats = null; - } - - mSettingsObserver = new SettingsObserver(mHandler); - mWindowManagerPolicy = LocalServices.getService(WindowManagerPolicy.class); - mBlanker = blanker; - mContext = context; - mBrightnessTracker = brightnessTracker; - mOnBrightnessChangeRunnable = onBrightnessChangeRunnable; - - PowerManager pm = context.getSystemService(PowerManager.class); - - final Resources resources = context.getResources(); - - // DOZE AND DIM SETTINGS - mScreenBrightnessDozeConfig = BrightnessUtils.clampAbsoluteBrightness( - pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DOZE)); - loadBrightnessRampRates(); - mSkipScreenOnBrightnessRamp = resources.getBoolean( - R.bool.config_skipScreenOnBrightnessRamp); - - Runnable modeChangeCallback = () -> { - sendUpdatePowerState(); - postBrightnessChangeRunnable(); - // TODO(b/192258832): Switch the HBMChangeCallback to a listener pattern. - if (mAutomaticBrightnessController != null) { - mAutomaticBrightnessController.update(); - } - }; - - HighBrightnessModeController hbmController = createHbmControllerLocked(hbmMetadata, - modeChangeCallback); - mBrightnessThrottler = createBrightnessThrottlerLocked(); - - mBrightnessRangeController = mInjector.getBrightnessRangeController(hbmController, - modeChangeCallback, mDisplayDeviceConfig, mHandler, flags, - mDisplayDevice.getDisplayTokenLocked(), - mDisplayDevice.getDisplayDeviceInfoLocked()); - - mDisplayBrightnessController = - new DisplayBrightnessController(context, null, - mDisplayId, mLogicalDisplay.getDisplayInfoLocked().brightnessDefault, - brightnessSetting, () -> postBrightnessChangeRunnable(), - new HandlerExecutor(mHandler), flags); - - mBrightnessClamperController = mInjector.getBrightnessClamperController( - mHandler, modeChangeCallback::run, - new BrightnessClamperController.DisplayDeviceData( - mUniqueDisplayId, - mThermalBrightnessThrottlingDataId, - logicalDisplay.getPowerThrottlingDataIdLocked(), - mDisplayDeviceConfig), mContext, flags); - // Seed the cached brightness - saveBrightnessInfo(getScreenBrightnessSetting()); - mAutomaticBrightnessStrategy = - mDisplayBrightnessController.getAutomaticBrightnessStrategy(); - - DisplayWhiteBalanceSettings displayWhiteBalanceSettings = null; - DisplayWhiteBalanceController displayWhiteBalanceController = null; - if (mDisplayId == Display.DEFAULT_DISPLAY) { - try { - displayWhiteBalanceController = mInjector.getDisplayWhiteBalanceController( - mHandler, mSensorManager, resources); - displayWhiteBalanceSettings = new DisplayWhiteBalanceSettings(mContext, mHandler); - displayWhiteBalanceSettings.setCallbacks(this); - displayWhiteBalanceController.setCallbacks(this); - } catch (Exception e) { - Slog.e(mTag, "failed to set up display white-balance: " + e); - } - } - mDisplayWhiteBalanceSettings = displayWhiteBalanceSettings; - mDisplayWhiteBalanceController = displayWhiteBalanceController; - - loadNitsRange(resources); - - if (mDisplayId == Display.DEFAULT_DISPLAY) { - mCdsi = LocalServices.getService(ColorDisplayServiceInternal.class); - if (mCdsi != null) { - boolean active = mCdsi.setReduceBrightColorsListener( - new ReduceBrightColorsListener() { - @Override - public void onReduceBrightColorsActivationChanged(boolean activated, - boolean userInitiated) { - applyReduceBrightColorsSplineAdjustment(); - - } - - @Override - public void onReduceBrightColorsStrengthChanged(int strength) { - applyReduceBrightColorsSplineAdjustment(); - } - }); - if (active) { - applyReduceBrightColorsSplineAdjustment(); - } - } - } else { - mCdsi = null; - } - - setUpAutoBrightness(context, handler); - - mColorFadeEnabled = mInjector.isColorFadeEnabled() - && !resources.getBoolean( - com.android.internal.R.bool.config_displayColorFadeDisabled); - mColorFadeFadesConfig = resources.getBoolean( - R.bool.config_animateScreenLights); - - mDisplayBlanksAfterDozeConfig = resources.getBoolean( - R.bool.config_displayBlanksAfterDoze); - - mBrightnessBucketsInDozeConfig = resources.getBoolean( - R.bool.config_displayBrightnessBucketsInDoze); - - mBootCompleted = bootCompleted; - } - - private void applyReduceBrightColorsSplineAdjustment() { - mHandler.obtainMessage(MSG_UPDATE_RBC).sendToTarget(); - sendUpdatePowerState(); - } - - private void handleRbcChanged() { - if (mAutomaticBrightnessController == null) { - return; - } - - float[] adjustedNits = new float[mNitsRange.length]; - for (int i = 0; i < mNitsRange.length; i++) { - adjustedNits[i] = mCdsi.getReduceBrightColorsAdjustedBrightnessNits(mNitsRange[i]); - } - mIsRbcActive = mCdsi.isReduceBrightColorsActivated(); - mAutomaticBrightnessController.recalculateSplines(mIsRbcActive, adjustedNits); - } - - /** - * Returns true if the proximity sensor screen-off function is available. - */ - @Override - public boolean isProximitySensorAvailable() { - return mDisplayPowerProximityStateController.isProximitySensorAvailable(); - } - - /** - * Get the {@link BrightnessChangeEvent}s for the specified user. - * - * @param userId userId to fetch data for - * @param includePackage if false will null out the package name in events - */ - @Nullable - @Override - public ParceledListSlice<BrightnessChangeEvent> getBrightnessEvents( - @UserIdInt int userId, boolean includePackage) { - if (mBrightnessTracker == null) { - return null; - } - return mBrightnessTracker.getEvents(userId, includePackage); - } - - @Override - public void onSwitchUser(@UserIdInt int newUserId) { - Message msg = mHandler.obtainMessage(MSG_SWITCH_USER, newUserId); - mHandler.sendMessage(msg); - } - - private void handleOnSwitchUser(@UserIdInt int newUserId) { - handleSettingsChange(true /* userSwitch */); - handleBrightnessModeChange(); - if (mBrightnessTracker != null) { - mBrightnessTracker.onSwitchUser(newUserId); - } - } - - @Nullable - @Override - public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats( - @UserIdInt int userId) { - if (mBrightnessTracker == null) { - return null; - } - return mBrightnessTracker.getAmbientBrightnessStats(userId); - } - - /** - * Persist the brightness slider events and ambient brightness stats to disk. - */ - @Override - public void persistBrightnessTrackerState() { - if (mBrightnessTracker != null) { - mBrightnessTracker.persistBrightnessTrackerState(); - } - } - - /** - * Requests a new power state. - * The controller makes a copy of the provided object and then - * begins adjusting the power state to match what was requested. - * - * @param request The requested power state. - * @param waitForNegativeProximity If true, issues a request to wait for - * negative proximity before turning the screen back on, - * assuming the screen - * was turned off by the proximity sensor. - * @return True if display is ready, false if there are important changes that must - * be made asynchronously (such as turning the screen on), in which case the caller - * should grab a wake lock, watch for {@link DisplayPowerCallbacks#onStateChanged()} - * then try the request again later until the state converges. - */ - public boolean requestPowerState(DisplayPowerRequest request, - boolean waitForNegativeProximity) { - if (DEBUG) { - Slog.d(mTag, "requestPowerState: " - + request + ", waitForNegativeProximity=" + waitForNegativeProximity); - } - - synchronized (mLock) { - if (mStopped) { - return true; - } - - boolean changed = mDisplayPowerProximityStateController - .setPendingWaitForNegativeProximityLocked(waitForNegativeProximity); - - if (mPendingRequestLocked == null) { - mPendingRequestLocked = new DisplayPowerRequest(request); - changed = true; - } else if (!mPendingRequestLocked.equals(request)) { - mPendingRequestLocked.copyFrom(request); - changed = true; - } - - if (changed) { - mDisplayReadyLocked = false; - if (!mPendingRequestChangedLocked) { - mPendingRequestChangedLocked = true; - sendUpdatePowerStateLocked(); - } - } - - return mDisplayReadyLocked; - } - } - - @Override - public void overrideDozeScreenState(int displayState) { - mHandler.postAtTime(() -> { - if (mDisplayOffloadSession == null - || !(DisplayOffloadSession.isSupportedOffloadState(displayState) - || displayState == Display.STATE_UNKNOWN)) { - return; - } - mDisplayStateController.overrideDozeScreenState(displayState); - sendUpdatePowerState(); - }, mClock.uptimeMillis()); - } - - @Override - public void setDisplayOffloadSession(DisplayOffloadSession session) { - if (session == mDisplayOffloadSession) { - return; - } - unblockScreenOnByDisplayOffload(); - mDisplayOffloadSession = session; - } - - @Override - public BrightnessConfiguration getDefaultBrightnessConfiguration() { - if (mAutomaticBrightnessController == null) { - return null; - } - return mAutomaticBrightnessController.getDefaultConfig(); - } - - /** - * Notified when the display is changed. We use this to apply any changes that might be needed - * when displays get swapped on foldable devices. For example, different brightness properties - * of each display need to be properly reflected in AutomaticBrightnessController. - * - * Make sure DisplayManagerService.mSyncRoot lock is held when this is called - */ - @Override - public void onDisplayChanged(HighBrightnessModeMetadata hbmMetadata, int leadDisplayId) { - mLeadDisplayId = leadDisplayId; - final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked(); - if (device == null) { - Slog.wtf(mTag, "Display Device is null in DisplayPowerController2 for display: " - + mLogicalDisplay.getDisplayIdLocked()); - return; - } - - final String uniqueId = device.getUniqueId(); - final DisplayDeviceConfig config = device.getDisplayDeviceConfig(); - final IBinder token = device.getDisplayTokenLocked(); - final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); - final boolean isEnabled = mLogicalDisplay.isEnabledLocked(); - final boolean isInTransition = mLogicalDisplay.isInTransitionLocked(); - final boolean isDisplayInternal = mLogicalDisplay.getPrimaryDisplayDeviceLocked() != null - && mLogicalDisplay.getPrimaryDisplayDeviceLocked() - .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL; - final String thermalBrightnessThrottlingDataId = - mLogicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId; - final String powerThrottlingDataId = - mLogicalDisplay.getPowerThrottlingDataIdLocked(); - - mHandler.postAtTime(() -> { - boolean changed = false; - if (mDisplayDevice != device) { - changed = true; - mDisplayDevice = device; - mUniqueDisplayId = uniqueId; - mDisplayStatsId = mUniqueDisplayId.hashCode(); - mDisplayDeviceConfig = config; - mThermalBrightnessThrottlingDataId = thermalBrightnessThrottlingDataId; - loadFromDisplayDeviceConfig(token, info, hbmMetadata); - mDisplayPowerProximityStateController.notifyDisplayDeviceChanged(config); - - // Since the underlying display-device changed, we really don't know the - // last command that was sent to change it's state. Let's assume it is unknown so - // that we trigger a change immediately. - mPowerState.resetScreenState(); - } else if (!Objects.equals(mThermalBrightnessThrottlingDataId, - thermalBrightnessThrottlingDataId)) { - changed = true; - mThermalBrightnessThrottlingDataId = thermalBrightnessThrottlingDataId; - mBrightnessThrottler.loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig( - config.getThermalBrightnessThrottlingDataMapByThrottlingId(), - mThermalBrightnessThrottlingDataId, - mUniqueDisplayId); - } - if (mIsEnabled != isEnabled || mIsInTransition != isInTransition) { - changed = true; - mIsEnabled = isEnabled; - mIsInTransition = isInTransition; - } - - mIsDisplayInternal = isDisplayInternal; - // using local variables here, when mBrightnessThrottler is removed, - // mThermalBrightnessThrottlingDataId could be removed as well - // changed = true will be not needed - clampers are maintaining their state and - // will call updatePowerState if needed. - mBrightnessClamperController.onDisplayChanged( - new BrightnessClamperController.DisplayDeviceData(uniqueId, - thermalBrightnessThrottlingDataId, powerThrottlingDataId, config)); - - if (changed) { - updatePowerState(); - } - }, mClock.uptimeMillis()); - } - - /** - * Unregisters all listeners and interrupts all running threads; halting future work. - * - * This method should be called when the DisplayPowerController2 is no longer in use; i.e. when - * the {@link #mDisplayId display} has been removed. - */ - @Override - public void stop() { - synchronized (mLock) { - clearDisplayBrightnessFollowersLocked(); - - mStopped = true; - Message msg = mHandler.obtainMessage(MSG_STOP); - mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); - - if (mAutomaticBrightnessController != null) { - mAutomaticBrightnessController.stop(); - } - - mDisplayBrightnessController.stop(); - - mContext.getContentResolver().unregisterContentObserver(mSettingsObserver); - } - } - - private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info, - HighBrightnessModeMetadata hbmMetadata) { - // All properties that depend on the associated DisplayDevice and the DDC must be - // updated here. - loadBrightnessRampRates(); - loadNitsRange(mContext.getResources()); - setUpAutoBrightness(mContext, mHandler); - reloadReduceBrightColours(); - setAnimatorRampSpeeds(/* isIdleMode= */ false); - - mBrightnessRangeController.loadFromConfig(hbmMetadata, token, info, mDisplayDeviceConfig); - mBrightnessThrottler.loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig( - mDisplayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId(), - mThermalBrightnessThrottlingDataId, mUniqueDisplayId); - } - - private void sendUpdatePowerState() { - synchronized (mLock) { - sendUpdatePowerStateLocked(); - } - } - - @GuardedBy("mLock") - private void sendUpdatePowerStateLocked() { - if (!mStopped && !mPendingUpdatePowerStateLocked) { - mPendingUpdatePowerStateLocked = true; - Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE); - mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); - } - } - - private void initialize(int displayState) { - mPowerState = mInjector.getDisplayPowerState(mBlanker, - mColorFadeEnabled ? new ColorFade(mDisplayId) : null, mDisplayId, displayState); - - if (mColorFadeEnabled) { - mColorFadeOnAnimator = ObjectAnimator.ofFloat( - mPowerState, DisplayPowerState.COLOR_FADE_LEVEL, 0.0f, 1.0f); - mColorFadeOnAnimator.setDuration(COLOR_FADE_ON_ANIMATION_DURATION_MILLIS); - mColorFadeOnAnimator.addListener(mAnimatorListener); - - mColorFadeOffAnimator = ObjectAnimator.ofFloat( - mPowerState, DisplayPowerState.COLOR_FADE_LEVEL, 1.0f, 0.0f); - mColorFadeOffAnimator.setDuration(COLOR_FADE_OFF_ANIMATION_DURATION_MILLIS); - mColorFadeOffAnimator.addListener(mAnimatorListener); - } - - mScreenBrightnessRampAnimator = mInjector.getDualRampAnimator(mPowerState, - DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT, - DisplayPowerState.SCREEN_SDR_BRIGHTNESS_FLOAT); - setAnimatorRampSpeeds(mAutomaticBrightnessController != null - && mAutomaticBrightnessController.isInIdleMode()); - mScreenBrightnessRampAnimator.setListener(mRampAnimatorListener); - - noteScreenState(mPowerState.getScreenState()); - noteScreenBrightness(mPowerState.getScreenBrightness()); - - // Initialize all of the brightness tracking state - final float brightness = mDisplayBrightnessController.convertToAdjustedNits( - mPowerState.getScreenBrightness()); - if (mBrightnessTracker != null && brightness >= PowerManager.BRIGHTNESS_MIN) { - mBrightnessTracker.start(brightness); - } - - BrightnessSetting.BrightnessSettingListener brightnessSettingListener = brightnessValue -> { - Message msg = mHandler.obtainMessage(MSG_UPDATE_BRIGHTNESS, brightnessValue); - mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); - }; - mDisplayBrightnessController - .registerBrightnessSettingChangeListener(brightnessSettingListener); - - mContext.getContentResolver().registerContentObserver( - Settings.System.getUriFor(Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ), - false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL); - mContext.getContentResolver().registerContentObserver( - Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE), - false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL); - if (mFlags.areAutoBrightnessModesEnabled()) { - mContext.getContentResolver().registerContentObserver( - Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_ALS), - /* notifyForDescendants= */ false, mSettingsObserver, UserHandle.USER_CURRENT); - } - handleBrightnessModeChange(); - } - - private void setUpAutoBrightness(Context context, Handler handler) { - mUseSoftwareAutoBrightnessConfig = mDisplayDeviceConfig.isAutoBrightnessAvailable(); - - if (!mUseSoftwareAutoBrightnessConfig) { - return; - } - - SparseArray<BrightnessMappingStrategy> brightnessMappers = new SparseArray<>(); - - BrightnessMappingStrategy defaultModeBrightnessMapper = - mInjector.getDefaultModeBrightnessMapper(context, mDisplayDeviceConfig, - mDisplayWhiteBalanceController); - brightnessMappers.append(AUTO_BRIGHTNESS_MODE_DEFAULT, - defaultModeBrightnessMapper); - - final boolean isIdleScreenBrightnessEnabled = context.getResources().getBoolean( - R.bool.config_enableIdleScreenBrightnessMode); - if (isIdleScreenBrightnessEnabled) { - BrightnessMappingStrategy idleModeBrightnessMapper = - BrightnessMappingStrategy.create(context, mDisplayDeviceConfig, - AUTO_BRIGHTNESS_MODE_IDLE, - mDisplayWhiteBalanceController); - if (idleModeBrightnessMapper != null) { - brightnessMappers.append(AUTO_BRIGHTNESS_MODE_IDLE, - idleModeBrightnessMapper); - } - } - - BrightnessMappingStrategy dozeModeBrightnessMapper = - BrightnessMappingStrategy.create(context, mDisplayDeviceConfig, - AUTO_BRIGHTNESS_MODE_DOZE, mDisplayWhiteBalanceController); - if (mFlags.areAutoBrightnessModesEnabled() && dozeModeBrightnessMapper != null) { - brightnessMappers.put(AUTO_BRIGHTNESS_MODE_DOZE, dozeModeBrightnessMapper); - } - - float userLux = BrightnessMappingStrategy.INVALID_LUX; - float userNits = BrightnessMappingStrategy.INVALID_NITS; - if (mAutomaticBrightnessController != null) { - userLux = mAutomaticBrightnessController.getUserLux(); - userNits = mAutomaticBrightnessController.getUserNits(); - } - - if (defaultModeBrightnessMapper != null) { - final float dozeScaleFactor = context.getResources().getFraction( - R.fraction.config_screenAutoBrightnessDozeScaleFactor, - 1, 1); - - // Ambient Lux - Active Mode Brightness Thresholds - float[] ambientBrighteningThresholds = - mDisplayDeviceConfig.getAmbientBrighteningPercentages(); - float[] ambientDarkeningThresholds = - mDisplayDeviceConfig.getAmbientDarkeningPercentages(); - float[] ambientBrighteningLevels = - mDisplayDeviceConfig.getAmbientBrighteningLevels(); - float[] ambientDarkeningLevels = - mDisplayDeviceConfig.getAmbientDarkeningLevels(); - float ambientDarkeningMinThreshold = - mDisplayDeviceConfig.getAmbientLuxDarkeningMinThreshold(); - float ambientBrighteningMinThreshold = - mDisplayDeviceConfig.getAmbientLuxBrighteningMinThreshold(); - HysteresisLevels ambientBrightnessThresholds = mInjector.getHysteresisLevels( - ambientBrighteningThresholds, ambientDarkeningThresholds, - ambientBrighteningLevels, ambientDarkeningLevels, ambientDarkeningMinThreshold, - ambientBrighteningMinThreshold); - - // Display - Active Mode Brightness Thresholds - float[] screenBrighteningThresholds = - mDisplayDeviceConfig.getScreenBrighteningPercentages(); - float[] screenDarkeningThresholds = - mDisplayDeviceConfig.getScreenDarkeningPercentages(); - float[] screenBrighteningLevels = - mDisplayDeviceConfig.getScreenBrighteningLevels(); - float[] screenDarkeningLevels = - mDisplayDeviceConfig.getScreenDarkeningLevels(); - float screenDarkeningMinThreshold = - mDisplayDeviceConfig.getScreenDarkeningMinThreshold(); - float screenBrighteningMinThreshold = - mDisplayDeviceConfig.getScreenBrighteningMinThreshold(); - HysteresisLevels screenBrightnessThresholds = mInjector.getHysteresisLevels( - screenBrighteningThresholds, screenDarkeningThresholds, - screenBrighteningLevels, screenDarkeningLevels, screenDarkeningMinThreshold, - screenBrighteningMinThreshold, true); - - // Ambient Lux - Idle Screen Brightness Thresholds - float ambientDarkeningMinThresholdIdle = - mDisplayDeviceConfig.getAmbientLuxDarkeningMinThresholdIdle(); - float ambientBrighteningMinThresholdIdle = - mDisplayDeviceConfig.getAmbientLuxBrighteningMinThresholdIdle(); - float[] ambientBrighteningThresholdsIdle = - mDisplayDeviceConfig.getAmbientBrighteningPercentagesIdle(); - float[] ambientDarkeningThresholdsIdle = - mDisplayDeviceConfig.getAmbientDarkeningPercentagesIdle(); - float[] ambientBrighteningLevelsIdle = - mDisplayDeviceConfig.getAmbientBrighteningLevelsIdle(); - float[] ambientDarkeningLevelsIdle = - mDisplayDeviceConfig.getAmbientDarkeningLevelsIdle(); - HysteresisLevels ambientBrightnessThresholdsIdle = mInjector.getHysteresisLevels( - ambientBrighteningThresholdsIdle, ambientDarkeningThresholdsIdle, - ambientBrighteningLevelsIdle, ambientDarkeningLevelsIdle, - ambientDarkeningMinThresholdIdle, ambientBrighteningMinThresholdIdle); - - // Display - Idle Screen Brightness Thresholds - float screenDarkeningMinThresholdIdle = - mDisplayDeviceConfig.getScreenDarkeningMinThresholdIdle(); - float screenBrighteningMinThresholdIdle = - mDisplayDeviceConfig.getScreenBrighteningMinThresholdIdle(); - float[] screenBrighteningThresholdsIdle = - mDisplayDeviceConfig.getScreenBrighteningPercentagesIdle(); - float[] screenDarkeningThresholdsIdle = - mDisplayDeviceConfig.getScreenDarkeningPercentagesIdle(); - float[] screenBrighteningLevelsIdle = - mDisplayDeviceConfig.getScreenBrighteningLevelsIdle(); - float[] screenDarkeningLevelsIdle = - mDisplayDeviceConfig.getScreenDarkeningLevelsIdle(); - HysteresisLevels screenBrightnessThresholdsIdle = mInjector.getHysteresisLevels( - screenBrighteningThresholdsIdle, screenDarkeningThresholdsIdle, - screenBrighteningLevelsIdle, screenDarkeningLevelsIdle, - screenDarkeningMinThresholdIdle, screenBrighteningMinThresholdIdle); - - long brighteningLightDebounce = mDisplayDeviceConfig - .getAutoBrightnessBrighteningLightDebounce(); - long darkeningLightDebounce = mDisplayDeviceConfig - .getAutoBrightnessDarkeningLightDebounce(); - long brighteningLightDebounceIdle = mDisplayDeviceConfig - .getAutoBrightnessBrighteningLightDebounceIdle(); - long darkeningLightDebounceIdle = mDisplayDeviceConfig - .getAutoBrightnessDarkeningLightDebounceIdle(); - boolean autoBrightnessResetAmbientLuxAfterWarmUp = context.getResources().getBoolean( - R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp); - - int lightSensorWarmUpTimeConfig = context.getResources().getInteger( - R.integer.config_lightSensorWarmupTime); - int lightSensorRate = context.getResources().getInteger( - R.integer.config_autoBrightnessLightSensorRate); - int initialLightSensorRate = context.getResources().getInteger( - R.integer.config_autoBrightnessInitialLightSensorRate); - if (initialLightSensorRate == -1) { - initialLightSensorRate = lightSensorRate; - } else if (initialLightSensorRate > lightSensorRate) { - Slog.w(mTag, "Expected config_autoBrightnessInitialLightSensorRate (" - + initialLightSensorRate + ") to be less than or equal to " - + "config_autoBrightnessLightSensorRate (" + lightSensorRate + ")."); - } - - loadAmbientLightSensor(); - // BrightnessTracker should only use one light sensor, we want to use the light sensor - // from the default display and not e.g. temporary displays when switching layouts. - if (mBrightnessTracker != null && mDisplayId == Display.DEFAULT_DISPLAY) { - mBrightnessTracker.setLightSensor(mLightSensor); - } - - if (mAutomaticBrightnessController != null) { - mAutomaticBrightnessController.stop(); - } - mAutomaticBrightnessController = mInjector.getAutomaticBrightnessController( - this, handler.getLooper(), mSensorManager, mLightSensor, - brightnessMappers, lightSensorWarmUpTimeConfig, PowerManager.BRIGHTNESS_MIN, - PowerManager.BRIGHTNESS_MAX, dozeScaleFactor, lightSensorRate, - initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce, - brighteningLightDebounceIdle, darkeningLightDebounceIdle, - autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds, - screenBrightnessThresholds, ambientBrightnessThresholdsIdle, - screenBrightnessThresholdsIdle, mContext, mBrightnessRangeController, - mBrightnessThrottler, mDisplayDeviceConfig.getAmbientHorizonShort(), - mDisplayDeviceConfig.getAmbientHorizonLong(), userLux, userNits); - mDisplayBrightnessController.setAutomaticBrightnessController( - mAutomaticBrightnessController); - - mAutomaticBrightnessStrategy - .setAutomaticBrightnessController(mAutomaticBrightnessController); - mBrightnessEventRingBuffer = - new RingBuffer<>(BrightnessEvent.class, RINGBUFFER_MAX); - - if (mScreenOffBrightnessSensorController != null) { - mScreenOffBrightnessSensorController.stop(); - mScreenOffBrightnessSensorController = null; - } - loadScreenOffBrightnessSensor(); - int[] sensorValueToLux = mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux(); - if (mScreenOffBrightnessSensor != null && sensorValueToLux != null) { - mScreenOffBrightnessSensorController = - mInjector.getScreenOffBrightnessSensorController( - mSensorManager, - mScreenOffBrightnessSensor, - mHandler, - SystemClock::uptimeMillis, - sensorValueToLux, - defaultModeBrightnessMapper); - } - } else { - mUseSoftwareAutoBrightnessConfig = false; - } - } - - private void loadBrightnessRampRates() { - mBrightnessRampRateFastDecrease = mDisplayDeviceConfig.getBrightnessRampFastDecrease(); - mBrightnessRampRateFastIncrease = mDisplayDeviceConfig.getBrightnessRampFastIncrease(); - mBrightnessRampRateSlowDecrease = mDisplayDeviceConfig.getBrightnessRampSlowDecrease(); - mBrightnessRampRateSlowIncrease = mDisplayDeviceConfig.getBrightnessRampSlowIncrease(); - mBrightnessRampRateSlowDecreaseIdle = - mDisplayDeviceConfig.getBrightnessRampSlowDecreaseIdle(); - mBrightnessRampRateSlowIncreaseIdle = - mDisplayDeviceConfig.getBrightnessRampSlowIncreaseIdle(); - mBrightnessRampDecreaseMaxTimeMillis = - mDisplayDeviceConfig.getBrightnessRampDecreaseMaxMillis(); - mBrightnessRampIncreaseMaxTimeMillis = - mDisplayDeviceConfig.getBrightnessRampIncreaseMaxMillis(); - mBrightnessRampDecreaseMaxTimeIdleMillis = - mDisplayDeviceConfig.getBrightnessRampDecreaseMaxIdleMillis(); - mBrightnessRampIncreaseMaxTimeIdleMillis = - mDisplayDeviceConfig.getBrightnessRampIncreaseMaxIdleMillis(); - } - - private void loadNitsRange(Resources resources) { - if (mDisplayDeviceConfig != null && mDisplayDeviceConfig.getNits() != null) { - mNitsRange = mDisplayDeviceConfig.getNits(); - } else { - Slog.w(mTag, "Screen brightness nits configuration is unavailable; falling back"); - mNitsRange = BrightnessMappingStrategy.getFloatArray(resources - .obtainTypedArray(R.array.config_screenBrightnessNits)); - } - } - - private void reloadReduceBrightColours() { - if (mCdsi != null && mCdsi.isReduceBrightColorsActivated()) { - applyReduceBrightColorsSplineAdjustment(); - } - } - - @Override - public void setAutomaticScreenBrightnessMode( - @AutomaticBrightnessController.AutomaticBrightnessMode int mode) { - boolean isIdle = mode == AUTO_BRIGHTNESS_MODE_IDLE; - if (mAutomaticBrightnessController != null) { - mAutomaticBrightnessController.switchMode(mode); - setAnimatorRampSpeeds(isIdle); - } - Message msg = mHandler.obtainMessage(); - msg.what = MSG_SET_DWBC_STRONG_MODE; - msg.arg1 = isIdle ? 1 : 0; - mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); - } - - private void setAnimatorRampSpeeds(boolean isIdle) { - if (mScreenBrightnessRampAnimator == null) { - return; - } - if (mFlags.isAdaptiveTone1Enabled() && isIdle) { - mScreenBrightnessRampAnimator.setAnimationTimeLimits( - mBrightnessRampIncreaseMaxTimeIdleMillis, - mBrightnessRampDecreaseMaxTimeIdleMillis); - } else { - mScreenBrightnessRampAnimator.setAnimationTimeLimits( - mBrightnessRampIncreaseMaxTimeMillis, - mBrightnessRampDecreaseMaxTimeMillis); - } - } - - private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() { - @Override - public void onAnimationStart(Animator animation) { - } - - @Override - public void onAnimationEnd(Animator animation) { - sendUpdatePowerState(); - } - - @Override - public void onAnimationRepeat(Animator animation) { - } - - @Override - public void onAnimationCancel(Animator animation) { - } - }; - - private final RampAnimator.Listener mRampAnimatorListener = new RampAnimator.Listener() { - @Override - public void onAnimationEnd() { - sendUpdatePowerState(); - Message msg = mHandler.obtainMessage(MSG_BRIGHTNESS_RAMP_DONE); - mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); - } - }; - - /** Clean up all resources that are accessed via the {@link #mHandler} thread. */ - private void cleanupHandlerThreadAfterStop() { - mDisplayPowerProximityStateController.cleanup(); - mBrightnessRangeController.stop(); - mBrightnessThrottler.stop(); - mBrightnessClamperController.stop(); - mHandler.removeCallbacksAndMessages(null); - - // Release any outstanding wakelocks we're still holding because of pending messages. - mWakelockController.releaseAll(); - - final float brightness = mPowerState != null - ? mPowerState.getScreenBrightness() - : PowerManager.BRIGHTNESS_MIN; - reportStats(brightness); - - if (mPowerState != null) { - mPowerState.stop(); - mPowerState = null; - } - - if (mScreenOffBrightnessSensorController != null) { - mScreenOffBrightnessSensorController.stop(); - } - - if (mDisplayWhiteBalanceController != null) { - mDisplayWhiteBalanceController.setEnabled(false); - } - } - - // Call from handler thread - private void updatePowerState() { - Trace.traceBegin(Trace.TRACE_TAG_POWER, - "DisplayPowerController#updatePowerState"); - updatePowerStateInternal(); - Trace.traceEnd(Trace.TRACE_TAG_POWER); - } - - private void updatePowerStateInternal() { - // Update the power state request. - final boolean mustNotify; - final int previousPolicy; - boolean mustInitialize = false; - mBrightnessReasonTemp.set(null); - mTempBrightnessEvent.reset(); - SparseArray<DisplayPowerControllerInterface> displayBrightnessFollowers; - synchronized (mLock) { - if (mStopped) { - return; - } - mPendingUpdatePowerStateLocked = false; - if (mPendingRequestLocked == null) { - return; // wait until first actual power request - } - - if (mPowerRequest == null) { - mPowerRequest = new DisplayPowerRequest(mPendingRequestLocked); - mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked(); - mPendingRequestChangedLocked = false; - mustInitialize = true; - // Assume we're on and bright until told otherwise, since that's the state we turn - // on in. - previousPolicy = DisplayPowerRequest.POLICY_BRIGHT; - } else if (mPendingRequestChangedLocked) { - previousPolicy = mPowerRequest.policy; - mPowerRequest.copyFrom(mPendingRequestLocked); - mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked(); - mPendingRequestChangedLocked = false; - mDisplayReadyLocked = false; - } else { - previousPolicy = mPowerRequest.policy; - } - - mustNotify = !mDisplayReadyLocked; - - displayBrightnessFollowers = mDisplayBrightnessFollowers.clone(); - } - - int state = mDisplayStateController - .updateDisplayState(mPowerRequest, mIsEnabled, mIsInTransition); - - // Initialize things the first time the power state is changed. - if (mustInitialize) { - initialize(readyToUpdateDisplayState() ? state : Display.STATE_UNKNOWN); - } - - // Animate the screen state change unless already animating. - // The transition may be deferred, so after this point we will use the - // actual state instead of the desired one. - animateScreenStateChange(state, mDisplayStateController.shouldPerformScreenOffTransition()); - state = mPowerState.getScreenState(); - - // Switch to doze auto-brightness mode if needed - if (mFlags.areAutoBrightnessModesEnabled() && mAutomaticBrightnessController != null - && !mAutomaticBrightnessController.isInIdleMode()) { - setAutomaticScreenBrightnessMode(Display.isDozeState(state) - ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT); - } - - final boolean userSetBrightnessChanged = mDisplayBrightnessController - .updateUserSetScreenBrightness(); - - DisplayBrightnessState displayBrightnessState = mDisplayBrightnessController - .updateBrightness(mPowerRequest, state); - float brightnessState = displayBrightnessState.getBrightness(); - float rawBrightnessState = displayBrightnessState.getBrightness(); - mBrightnessReasonTemp.set(displayBrightnessState.getBrightnessReason()); - boolean slowChange = displayBrightnessState.isSlowChange(); - // custom transition duration - float customAnimationRate = displayBrightnessState.getCustomAnimationRate(); - - // Set up the ScreenOff controller used when coming out of SCREEN_OFF and the ALS sensor - // doesn't yet have a valid lux value to use with auto-brightness. - if (mScreenOffBrightnessSensorController != null) { - mScreenOffBrightnessSensorController - .setLightSensorEnabled(displayBrightnessState.getShouldUseAutoBrightness() - && mIsEnabled && (state == Display.STATE_OFF - || (state == Display.STATE_DOZE - && !mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig())) - && mLeadDisplayId == Layout.NO_LEAD_DISPLAY); - } - - // Take note if the short term model was already active before applying the current - // request changes. - final boolean wasShortTermModelActive = - mAutomaticBrightnessStrategy.isShortTermModelActive(); - mAutomaticBrightnessStrategy.setAutoBrightnessState(state, - mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig(), - mBrightnessReasonTemp.getReason(), mPowerRequest.policy, - mDisplayBrightnessController.getLastUserSetScreenBrightness(), - userSetBrightnessChanged); - - // If the brightness is already set then it's been overridden by something other than the - // user, or is a temporary adjustment. - boolean userInitiatedChange = (Float.isNaN(brightnessState)) - && (mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentChanged() - || userSetBrightnessChanged); - - mBrightnessRangeController.setAutoBrightnessEnabled( - mAutomaticBrightnessStrategy.isAutoBrightnessEnabled() - ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED - : mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff() - ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE - : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED); - - boolean updateScreenBrightnessSetting = - displayBrightnessState.shouldUpdateScreenBrightnessSetting(); - float currentBrightnessSetting = mDisplayBrightnessController.getCurrentBrightness(); - // Apply auto-brightness. - int brightnessAdjustmentFlags = 0; - if (Float.isNaN(brightnessState)) { - if (mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()) { - brightnessState = mAutomaticBrightnessStrategy.getAutomaticScreenBrightness( - mTempBrightnessEvent); - if (BrightnessUtils.isValidBrightnessValue(brightnessState) - || brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT) { - rawBrightnessState = mAutomaticBrightnessController - .getRawAutomaticScreenBrightness(); - brightnessState = clampScreenBrightness(brightnessState); - // slowly adapt to auto-brightness - // TODO(b/253226419): slowChange should be decided by strategy.updateBrightness - slowChange = mAutomaticBrightnessStrategy.hasAppliedAutoBrightness() - && !mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentChanged(); - brightnessAdjustmentFlags = - mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentReasonsFlags(); - updateScreenBrightnessSetting = currentBrightnessSetting != brightnessState; - mAutomaticBrightnessStrategy.setAutoBrightnessApplied(true); - mBrightnessReasonTemp.setReason(BrightnessReason.REASON_AUTOMATIC); - if (mScreenOffBrightnessSensorController != null) { - mScreenOffBrightnessSensorController.setLightSensorEnabled(false); - } - } else { - mAutomaticBrightnessStrategy.setAutoBrightnessApplied(false); - } - } - } else { - // Any non-auto-brightness values such as override or temporary should still be subject - // to clamping so that they don't go beyond the current max as specified by HBM - // Controller. - brightnessState = clampScreenBrightness(brightnessState); - mAutomaticBrightnessStrategy.setAutoBrightnessApplied(false); - } - - // Use default brightness when dozing unless overridden. - if ((Float.isNaN(brightnessState)) - && Display.isDozeState(state)) { - rawBrightnessState = mScreenBrightnessDozeConfig; - brightnessState = clampScreenBrightness(rawBrightnessState); - mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT); - } - - // The ALS is not available yet - use the screen off sensor to determine the initial - // brightness - if (Float.isNaN(brightnessState) && mAutomaticBrightnessStrategy.isAutoBrightnessEnabled() - && mScreenOffBrightnessSensorController != null) { - rawBrightnessState = - mScreenOffBrightnessSensorController.getAutomaticScreenBrightness(); - brightnessState = rawBrightnessState; - if (BrightnessUtils.isValidBrightnessValue(brightnessState)) { - brightnessState = clampScreenBrightness(brightnessState); - updateScreenBrightnessSetting = mDisplayBrightnessController.getCurrentBrightness() - != brightnessState; - mBrightnessReasonTemp.setReason( - BrightnessReason.REASON_SCREEN_OFF_BRIGHTNESS_SENSOR); - } - } - - // Apply manual brightness. - if (Float.isNaN(brightnessState)) { - rawBrightnessState = currentBrightnessSetting; - brightnessState = clampScreenBrightness(rawBrightnessState); - if (brightnessState != currentBrightnessSetting) { - // The manually chosen screen brightness is outside of the currently allowed - // range (i.e., high-brightness-mode), make sure we tell the rest of the system - // by updating the setting. - updateScreenBrightnessSetting = true; - } - mBrightnessReasonTemp.setReason(BrightnessReason.REASON_MANUAL); - } - - float ambientLux = mAutomaticBrightnessController == null ? 0 - : mAutomaticBrightnessController.getAmbientLux(); - for (int i = 0; i < displayBrightnessFollowers.size(); i++) { - DisplayPowerControllerInterface follower = displayBrightnessFollowers.valueAt(i); - follower.setBrightnessToFollow(rawBrightnessState, - mDisplayBrightnessController.convertToNits(rawBrightnessState), - ambientLux, slowChange); - } - - // Now that a desired brightness has been calculated, apply brightness throttling. The - // dimming and low power transformations that follow can only dim brightness further. - // - // We didn't do this earlier through brightness clamping because we need to know both - // unthrottled (unclamped/ideal) and throttled brightness levels for subsequent operations. - // Note throttling effectively changes the allowed brightness range, so, similarly to HBM, - // we broadcast this change through setting. - final float unthrottledBrightnessState = brightnessState; - DisplayBrightnessState clampedState = mBrightnessClamperController.clamp(mPowerRequest, - brightnessState, slowChange); - - brightnessState = clampedState.getBrightness(); - slowChange = clampedState.isSlowChange(); - // faster rate wins, at this point customAnimationRate == -1, strategy does not control - // customAnimationRate. Should be revisited if strategy start setting this value - customAnimationRate = Math.max(customAnimationRate, clampedState.getCustomAnimationRate()); - mBrightnessReasonTemp.addModifier(clampedState.getBrightnessReason().getModifier()); - - if (updateScreenBrightnessSetting) { - // Tell the rest of the system about the new brightness in case we had to change it - // for things like auto-brightness or high-brightness-mode. Note that we do this - // only considering maxBrightness (ignoring brightness modifiers like low power or dim) - // so that the slider accurately represents the full possible range, - // even if they range changes what it means in absolute terms. - mDisplayBrightnessController.updateScreenBrightnessSetting( - MathUtils.constrain(unthrottledBrightnessState, - clampedState.getMinBrightness(), clampedState.getMaxBrightness())); - } - - // The current brightness to use has been calculated at this point, and HbmController should - // be notified so that it can accurately calculate HDR or HBM levels. We specifically do it - // here instead of having HbmController listen to the brightness setting because certain - // brightness sources (such as an app override) are not saved to the setting, but should be - // reflected in HBM calculations. - mBrightnessRangeController.onBrightnessChanged(brightnessState, unthrottledBrightnessState, - mBrightnessClamperController.getBrightnessMaxReason()); - - // Animate the screen brightness when the screen is on or dozing. - // Skip the animation when the screen is off or suspended. - boolean brightnessAdjusted = false; - final boolean brightnessIsTemporary = - (mBrightnessReasonTemp.getReason() == BrightnessReason.REASON_TEMPORARY) - || mAutomaticBrightnessStrategy - .isTemporaryAutoBrightnessAdjustmentApplied(); - if (!mPendingScreenOff) { - if (mSkipScreenOnBrightnessRamp) { - if (state == Display.STATE_ON) { - if (mSkipRampState == RAMP_STATE_SKIP_NONE && mDozing) { - mInitialAutoBrightness = brightnessState; - mSkipRampState = RAMP_STATE_SKIP_INITIAL; - } else if (mSkipRampState == RAMP_STATE_SKIP_INITIAL - && mUseSoftwareAutoBrightnessConfig - && !BrightnessSynchronizer.floatEquals(brightnessState, - mInitialAutoBrightness)) { - mSkipRampState = RAMP_STATE_SKIP_AUTOBRIGHT; - } else if (mSkipRampState == RAMP_STATE_SKIP_AUTOBRIGHT) { - mSkipRampState = RAMP_STATE_SKIP_NONE; - } - } else { - mSkipRampState = RAMP_STATE_SKIP_NONE; - } - } - - final boolean initialRampSkip = (state == Display.STATE_ON && mSkipRampState - != RAMP_STATE_SKIP_NONE) || mDisplayPowerProximityStateController - .shouldSkipRampBecauseOfProximityChangeToNegative(); - // While dozing, sometimes the brightness is split into buckets. Rather than animating - // through the buckets, which is unlikely to be smooth in the first place, just jump - // right to the suggested brightness. - final boolean hasBrightnessBuckets = - Display.isDozeState(state) && mBrightnessBucketsInDozeConfig; - // If the color fade is totally covering the screen then we can change the backlight - // level without it being a noticeable jump since any actual content isn't yet visible. - final boolean isDisplayContentVisible = - mColorFadeEnabled && mPowerState.getColorFadeLevel() == 1.0f; - // We only want to animate the brightness if it is between 0.0f and 1.0f. - // brightnessState can contain the values -1.0f and NaN, which we do not want to - // animate to. To avoid this, we check the value first. - // If the brightnessState is off (-1.0f) we still want to animate to the minimum - // brightness (0.0f) to accommodate for LED displays, which can appear bright to the - // user even when the display is all black. We also clamp here in case some - // transformations to the brightness have pushed it outside of the currently - // allowed range. - float animateValue = clampScreenBrightness(brightnessState); - - // If there are any HDR layers on the screen, we have a special brightness value that we - // use instead. We still preserve the calculated brightness for Standard Dynamic Range - // (SDR) layers, but the main brightness value will be the one for HDR. - float sdrAnimateValue = animateValue; - // TODO(b/216365040): The decision to prevent HBM for HDR in low power mode should be - // done in HighBrightnessModeController. - if (mBrightnessRangeController.getHighBrightnessMode() - == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR - && (mBrightnessReasonTemp.getModifier() & BrightnessReason.MODIFIER_DIMMED) == 0 - && (mBrightnessReasonTemp.getModifier() & BrightnessReason.MODIFIER_LOW_POWER) - == 0) { - // We want to scale HDR brightness level with the SDR level, we also need to restore - // SDR brightness immediately when entering dim or low power mode. - animateValue = mBrightnessRangeController.getHdrBrightnessValue(); - customAnimationRate = Math.max(customAnimationRate, - mBrightnessRangeController.getHdrTransitionRate()); - mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_HDR); - } - - // if doze or suspend state is requested, we want to finish brightnes animation fast - // to allow state animation to start - if (mPowerRequest.policy == DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE - && (mPowerRequest.dozeScreenState == Display.STATE_UNKNOWN // dozing - || mPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND - || mPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND)) { - customAnimationRate = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET; - slowChange = false; - } - - final float currentBrightness = mPowerState.getScreenBrightness(); - final float currentSdrBrightness = mPowerState.getSdrScreenBrightness(); - - if (BrightnessUtils.isValidBrightnessValue(animateValue) - && (animateValue != currentBrightness - || sdrAnimateValue != currentSdrBrightness)) { - boolean skipAnimation = initialRampSkip || hasBrightnessBuckets - || !isDisplayContentVisible || brightnessIsTemporary; - final boolean isHdrOnlyChange = BrightnessSynchronizer.floatEquals( - sdrAnimateValue, currentSdrBrightness); - if (mFlags.isFastHdrTransitionsEnabled() && !skipAnimation && isHdrOnlyChange) { - // SDR brightness is unchanged, so animate quickly as this is only impacting - // a likely minority amount of display content - // ie, the highlights of an HDR video or UltraHDR image - slowChange = false; - - // Going from HDR to no HDR; visually this should be a "no-op" anyway - // as the remaining SDR content's brightness should be holding steady - // due to the sdr brightness not shifting - if (BrightnessSynchronizer.floatEquals(sdrAnimateValue, animateValue)) { - skipAnimation = true; - } - - // Going from no HDR to HDR; visually this is a significant scene change - // and the animation just prevents advanced clients from doing their own - // handling of enter/exit animations if they would like to do such a thing - if (BrightnessSynchronizer.floatEquals(sdrAnimateValue, currentBrightness)) { - skipAnimation = true; - } - } - if (skipAnimation) { - animateScreenBrightness(animateValue, sdrAnimateValue, - SCREEN_ANIMATION_RATE_MINIMUM); - } else if (customAnimationRate > 0) { - animateScreenBrightness(animateValue, sdrAnimateValue, - customAnimationRate, /* ignoreAnimationLimits = */true); - } else { - boolean isIncreasing = animateValue > currentBrightness; - final float rampSpeed; - final boolean idle = mAutomaticBrightnessController != null - && mAutomaticBrightnessController.isInIdleMode(); - if (isIncreasing && slowChange) { - rampSpeed = idle ? mBrightnessRampRateSlowIncreaseIdle - : mBrightnessRampRateSlowIncrease; - } else if (isIncreasing && !slowChange) { - rampSpeed = mBrightnessRampRateFastIncrease; - } else if (!isIncreasing && slowChange) { - rampSpeed = idle ? mBrightnessRampRateSlowDecreaseIdle - : mBrightnessRampRateSlowDecrease; - } else { - rampSpeed = mBrightnessRampRateFastDecrease; - } - animateScreenBrightness(animateValue, sdrAnimateValue, rampSpeed); - } - } - - notifyBrightnessTrackerChanged(brightnessState, userInitiatedChange, - wasShortTermModelActive, mAutomaticBrightnessStrategy.isAutoBrightnessEnabled(), - brightnessIsTemporary, displayBrightnessState.getShouldUseAutoBrightness()); - - // We save the brightness info *after* the brightness setting has been changed and - // adjustments made so that the brightness info reflects the latest value. - brightnessAdjusted = saveBrightnessInfo(getScreenBrightnessSetting(), - animateValue, clampedState); - } else { - brightnessAdjusted = saveBrightnessInfo(getScreenBrightnessSetting(), clampedState); - } - - // Only notify if the brightness adjustment is not temporary (i.e. slider has been released) - if (brightnessAdjusted && !brightnessIsTemporary) { - postBrightnessChangeRunnable(); - } - - // Log any changes to what is currently driving the brightness setting. - if (!mBrightnessReasonTemp.equals(mBrightnessReason) || brightnessAdjustmentFlags != 0) { - Slog.v(mTag, "Brightness [" + brightnessState + "] reason changing to: '" - + mBrightnessReasonTemp.toString(brightnessAdjustmentFlags) - + "', previous reason: '" + mBrightnessReason + "'."); - mBrightnessReason.set(mBrightnessReasonTemp); - } else if (mBrightnessReasonTemp.getReason() == BrightnessReason.REASON_MANUAL - && userSetBrightnessChanged) { - Slog.v(mTag, "Brightness [" + brightnessState + "] manual adjustment."); - } - - - // Log brightness events when a detail of significance has changed. Generally this is the - // brightness itself changing, but also includes data like HBM cap, thermal throttling - // brightness cap, RBC state, etc. - mTempBrightnessEvent.setTime(System.currentTimeMillis()); - mTempBrightnessEvent.setBrightness(brightnessState); - mTempBrightnessEvent.setPhysicalDisplayId(mUniqueDisplayId); - mTempBrightnessEvent.setReason(mBrightnessReason); - mTempBrightnessEvent.setHbmMax(mBrightnessRangeController.getCurrentBrightnessMax()); - mTempBrightnessEvent.setHbmMode(mBrightnessRangeController.getHighBrightnessMode()); - mTempBrightnessEvent.setFlags(mTempBrightnessEvent.getFlags() - | (mIsRbcActive ? BrightnessEvent.FLAG_RBC : 0) - | (mPowerRequest.lowPowerMode ? BrightnessEvent.FLAG_LOW_POWER_MODE : 0)); - mTempBrightnessEvent.setRbcStrength(mCdsi != null - ? mCdsi.getReduceBrightColorsStrength() : -1); - mTempBrightnessEvent.setPowerFactor(mPowerRequest.screenLowPowerBrightnessFactor); - mTempBrightnessEvent.setWasShortTermModelActive(wasShortTermModelActive); - mTempBrightnessEvent.setDisplayBrightnessStrategyName(displayBrightnessState - .getDisplayBrightnessStrategyName()); - mTempBrightnessEvent.setAutomaticBrightnessEnabled( - displayBrightnessState.getShouldUseAutoBrightness()); - // Temporary is what we use during slider interactions. We avoid logging those so that - // we don't spam logcat when the slider is being used. - boolean tempToTempTransition = - mTempBrightnessEvent.getReason().getReason() == BrightnessReason.REASON_TEMPORARY - && mLastBrightnessEvent.getReason().getReason() - == BrightnessReason.REASON_TEMPORARY; - // Purely for dumpsys; - final boolean isRbcEvent = - mLastBrightnessEvent.isRbcEnabled() != mTempBrightnessEvent.isRbcEnabled(); - - if ((!mTempBrightnessEvent.equalsMainData(mLastBrightnessEvent) && !tempToTempTransition) - || brightnessAdjustmentFlags != 0) { - mTempBrightnessEvent.setInitialBrightness(mLastBrightnessEvent.getBrightness()); - mLastBrightnessEvent.copyFrom(mTempBrightnessEvent); - BrightnessEvent newEvent = new BrightnessEvent(mTempBrightnessEvent); - // Adjustment flags (and user-set flag) only get added after the equality checks since - // they are transient. - newEvent.setAdjustmentFlags(brightnessAdjustmentFlags); - newEvent.setFlags(newEvent.getFlags() | (userSetBrightnessChanged - ? BrightnessEvent.FLAG_USER_SET : 0)); - Slog.i(mTag, newEvent.toString(/* includeTime= */ false)); - - if (userSetBrightnessChanged - || newEvent.getReason().getReason() != BrightnessReason.REASON_TEMPORARY) { - logBrightnessEvent(newEvent, unthrottledBrightnessState); - } - if (mBrightnessEventRingBuffer != null) { - mBrightnessEventRingBuffer.append(newEvent); - } - if (isRbcEvent) { - mRbcEventRingBuffer.append(newEvent); - } - - } - - // Update display white-balance. - if (mDisplayWhiteBalanceController != null) { - if (state == Display.STATE_ON && mDisplayWhiteBalanceSettings.isEnabled()) { - mDisplayWhiteBalanceController.setEnabled(true); - mDisplayWhiteBalanceController.updateDisplayColorTemperature(); - } else { - mDisplayWhiteBalanceController.setEnabled(false); - } - } - - // Determine whether the display is ready for use in the newly requested state. - // Note that we do not wait for the brightness ramp animation to complete before - // reporting the display is ready because we only need to ensure the screen is in the - // right power state even as it continues to converge on the desired brightness. - final boolean ready = mPendingScreenOnUnblocker == null - && mPendingScreenOnUnblockerByDisplayOffload == null - && (!mColorFadeEnabled || (!mColorFadeOnAnimator.isStarted() - && !mColorFadeOffAnimator.isStarted())) - && mPowerState.waitUntilClean(mCleanListener); - final boolean finished = ready - && !mScreenBrightnessRampAnimator.isAnimating(); - - // Notify policy about screen turned on. - if (ready && state != Display.STATE_OFF - && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_TURNING_ON) { - setReportedScreenState(REPORTED_TO_POLICY_SCREEN_ON); - mWindowManagerPolicy.screenTurnedOn(mDisplayId); - } - - // Grab a wake lock if we have unfinished business. - if (!finished) { - mWakelockController.acquireWakelock(WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS); - } - - // Notify the power manager when ready. - if (ready && mustNotify) { - // Send state change. - synchronized (mLock) { - if (!mPendingRequestChangedLocked) { - mDisplayReadyLocked = true; - - if (DEBUG) { - Slog.d(mTag, "Display ready!"); - } - } - } - sendOnStateChangedWithWakelock(); - } - - // Release the wake lock when we have no unfinished business. - if (finished) { - mWakelockController.releaseWakelock(WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS); - } - - // Record if dozing for future comparison. - mDozing = state != Display.STATE_ON; - - if (previousPolicy != mPowerRequest.policy) { - logDisplayPolicyChanged(mPowerRequest.policy); - } - } - - private void setDwbcOverride(float cct) { - if (mDisplayWhiteBalanceController != null) { - mDisplayWhiteBalanceController.setAmbientColorTemperatureOverride(cct); - // The ambient color temperature override is only applied when the ambient color - // temperature changes or is updated, so it doesn't necessarily change the screen color - // temperature immediately. So, let's make it! - // We can call this directly, since we're already on the handler thread. - updatePowerState(); - } - } - - private void setDwbcStrongMode(int arg) { - if (mDisplayWhiteBalanceController != null) { - final boolean isIdle = (arg == 1); - mDisplayWhiteBalanceController.setStrongModeEnabled(isIdle); - } - } - - private void setDwbcLoggingEnabled(int arg) { - if (mDisplayWhiteBalanceController != null) { - final boolean enabled = (arg == 1); - mDisplayWhiteBalanceController.setLoggingEnabled(enabled); - mDisplayWhiteBalanceSettings.setLoggingEnabled(enabled); - } - } - - @Override - public void updateBrightness() { - sendUpdatePowerState(); - } - - /** - * Ignores the proximity sensor until the sensor state changes, but only if the sensor is - * currently enabled and forcing the screen to be dark. - */ - @Override - public void ignoreProximitySensorUntilChanged() { - mDisplayPowerProximityStateController.ignoreProximitySensorUntilChanged(); - } - - @Override - public void setBrightnessConfiguration(BrightnessConfiguration c, - boolean shouldResetShortTermModel) { - Message msg = mHandler.obtainMessage(MSG_CONFIGURE_BRIGHTNESS, - shouldResetShortTermModel ? 1 : 0, /* unused */ 0, c); - msg.sendToTarget(); - } - - @Override - public void setTemporaryBrightness(float brightness) { - Message msg = mHandler.obtainMessage(MSG_SET_TEMPORARY_BRIGHTNESS, - Float.floatToIntBits(brightness), 0 /*unused*/); - msg.sendToTarget(); - } - - @Override - public void setTemporaryAutoBrightnessAdjustment(float adjustment) { - Message msg = mHandler.obtainMessage(MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT, - Float.floatToIntBits(adjustment), 0 /*unused*/); - msg.sendToTarget(); - } - - @Override - public void setBrightnessFromOffload(float brightness) { - Message msg = mHandler.obtainMessage(MSG_SET_BRIGHTNESS_FROM_OFFLOAD, - Float.floatToIntBits(brightness), 0 /*unused*/); - mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); - } - - @Override - public float[] getAutoBrightnessLevels( - @AutomaticBrightnessController.AutomaticBrightnessMode int mode) { - int preset = Settings.System.getIntForUser(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_FOR_ALS, - Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL, UserHandle.USER_CURRENT); - return mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(mode, preset); - } - - @Override - public float[] getAutoBrightnessLuxLevels( - @AutomaticBrightnessController.AutomaticBrightnessMode int mode) { - int preset = Settings.System.getIntForUser(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_FOR_ALS, - Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL, UserHandle.USER_CURRENT); - return mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(mode, preset); - } - - @Override - public BrightnessInfo getBrightnessInfo() { - synchronized (mCachedBrightnessInfo) { - return new BrightnessInfo( - mCachedBrightnessInfo.brightness.value, - mCachedBrightnessInfo.adjustedBrightness.value, - mCachedBrightnessInfo.brightnessMin.value, - mCachedBrightnessInfo.brightnessMax.value, - mCachedBrightnessInfo.hbmMode.value, - mCachedBrightnessInfo.hbmTransitionPoint.value, - mCachedBrightnessInfo.brightnessMaxReason.value); - } - } - - @Override - public void onBootCompleted() { - Message msg = mHandler.obtainMessage(MSG_BOOT_COMPLETED); - mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); - } - - private boolean saveBrightnessInfo(float brightness) { - return saveBrightnessInfo(brightness, /* state= */ null); - } - - private boolean saveBrightnessInfo(float brightness, @Nullable DisplayBrightnessState state) { - return saveBrightnessInfo(brightness, brightness, state); - } - - private boolean saveBrightnessInfo(float brightness, float adjustedBrightness, - @Nullable DisplayBrightnessState state) { - synchronized (mCachedBrightnessInfo) { - float stateMax = state != null ? state.getMaxBrightness() : PowerManager.BRIGHTNESS_MAX; - float stateMin = state != null ? state.getMinBrightness() : PowerManager.BRIGHTNESS_MAX; - final float minBrightness = Math.max(stateMin, Math.min( - mBrightnessRangeController.getCurrentBrightnessMin(), stateMax)); - final float maxBrightness = Math.min( - mBrightnessRangeController.getCurrentBrightnessMax(), stateMax); - boolean changed = false; - - changed |= - mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightness, - brightness); - changed |= - mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.adjustedBrightness, - adjustedBrightness); - changed |= - mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMin, - minBrightness); - changed |= - mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMax, - maxBrightness); - changed |= - mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.hbmMode, - mBrightnessRangeController.getHighBrightnessMode()); - changed |= - mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.hbmTransitionPoint, - mBrightnessRangeController.getTransitionPoint()); - changed |= - mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason, - mBrightnessClamperController.getBrightnessMaxReason()); - return changed; - } - } - - void postBrightnessChangeRunnable() { - if (!mHandler.hasCallbacks(mOnBrightnessChangeRunnable)) { - mHandler.post(mOnBrightnessChangeRunnable); - } - } - - private HighBrightnessModeController createHbmControllerLocked( - HighBrightnessModeMetadata hbmMetadata, Runnable modeChangeCallback) { - final DisplayDeviceConfig ddConfig = mDisplayDevice.getDisplayDeviceConfig(); - final IBinder displayToken = mDisplayDevice.getDisplayTokenLocked(); - final String displayUniqueId = mDisplayDevice.getUniqueId(); - final DisplayDeviceConfig.HighBrightnessModeData hbmData = - ddConfig != null ? ddConfig.getHighBrightnessModeData() : null; - final DisplayDeviceInfo info = mDisplayDevice.getDisplayDeviceInfoLocked(); - return mInjector.getHighBrightnessModeController(mHandler, info.width, info.height, - displayToken, displayUniqueId, PowerManager.BRIGHTNESS_MIN, - PowerManager.BRIGHTNESS_MAX, hbmData, (sdrBrightness, maxDesiredHdrSdrRatio) -> - mDisplayDeviceConfig.getHdrBrightnessFromSdr(sdrBrightness, - maxDesiredHdrSdrRatio), modeChangeCallback, hbmMetadata, mContext); - } - - private BrightnessThrottler createBrightnessThrottlerLocked() { - final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked(); - final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig(); - return new BrightnessThrottler(mHandler, - () -> { - sendUpdatePowerState(); - postBrightnessChangeRunnable(); - }, mUniqueDisplayId, - mLogicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId, - ddConfig.getThermalBrightnessThrottlingDataMapByThrottlingId()); - } - - private void blockScreenOn() { - if (mPendingScreenOnUnblocker == null) { - Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0); - mPendingScreenOnUnblocker = new ScreenOnUnblocker(); - mScreenOnBlockStartRealTime = SystemClock.elapsedRealtime(); - Slog.i(mTag, "Blocking screen on until initial contents have been drawn."); - } - } - - private void unblockScreenOn() { - if (mPendingScreenOnUnblocker != null) { - mPendingScreenOnUnblocker = null; - long delay = SystemClock.elapsedRealtime() - mScreenOnBlockStartRealTime; - Slog.i(mTag, "Unblocked screen on after " + delay + " ms"); - Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0); - } - } - - private void blockScreenOff() { - if (mPendingScreenOffUnblocker == null) { - Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_OFF_BLOCKED_TRACE_NAME, 0); - mPendingScreenOffUnblocker = new ScreenOffUnblocker(); - mScreenOffBlockStartRealTime = SystemClock.elapsedRealtime(); - Slog.i(mTag, "Blocking screen off"); - } - } - - private void unblockScreenOff() { - if (mPendingScreenOffUnblocker != null) { - mPendingScreenOffUnblocker = null; - long delay = SystemClock.elapsedRealtime() - mScreenOffBlockStartRealTime; - Slog.i(mTag, "Unblocked screen off after " + delay + " ms"); - Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, SCREEN_OFF_BLOCKED_TRACE_NAME, 0); - } - } - - private void blockScreenOnByDisplayOffload(DisplayOffloadSession displayOffloadSession) { - if (mPendingScreenOnUnblockerByDisplayOffload != null || displayOffloadSession == null) { - return; - } - mScreenTurningOnWasBlockedByDisplayOffload = true; - - Trace.asyncTraceBegin( - Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME, 0); - mScreenOnBlockByDisplayOffloadStartRealTime = SystemClock.elapsedRealtime(); - - mPendingScreenOnUnblockerByDisplayOffload = - () -> onDisplayOffloadUnblockScreenOn(displayOffloadSession); - if (!displayOffloadSession.blockScreenOn(mPendingScreenOnUnblockerByDisplayOffload)) { - mPendingScreenOnUnblockerByDisplayOffload = null; - long delay = - SystemClock.elapsedRealtime() - mScreenOnBlockByDisplayOffloadStartRealTime; - Slog.w(mTag, "Tried blocking screen on for offloading but failed. So, end trace after " - + delay + " ms."); - Trace.asyncTraceEnd( - Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME, 0); - return; - } - Slog.i(mTag, "Blocking screen on for offloading."); - } - - private void onDisplayOffloadUnblockScreenOn(DisplayOffloadSession displayOffloadSession) { - Message msg = mHandler.obtainMessage(MSG_OFFLOADING_SCREEN_ON_UNBLOCKED, - displayOffloadSession); - mHandler.sendMessage(msg); - } - - private void unblockScreenOnByDisplayOffload() { - if (mPendingScreenOnUnblockerByDisplayOffload == null) { - return; - } - mPendingScreenOnUnblockerByDisplayOffload = null; - long delay = SystemClock.elapsedRealtime() - mScreenOnBlockByDisplayOffloadStartRealTime; - Slog.i(mTag, "Unblocked screen on for offloading after " + delay + " ms"); - Trace.asyncTraceEnd( - Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME, 0); - } - - private boolean setScreenState(int state) { - return setScreenState(state, false /*reportOnly*/); - } - - private boolean setScreenState(int state, boolean reportOnly) { - final boolean isOff = (state == Display.STATE_OFF); - final boolean isOn = (state == Display.STATE_ON); - final boolean changed = mPowerState.getScreenState() != state; - - // If the screen is turning on, give displayoffload a chance to do something before the - // screen actually turns on. - // TODO(b/316941732): add tests for this displayoffload screen-on blocker. - if (isOn && changed && !mScreenTurningOnWasBlockedByDisplayOffload) { - blockScreenOnByDisplayOffload(mDisplayOffloadSession); - } else if (!isOn && mScreenTurningOnWasBlockedByDisplayOffload) { - // No longer turning screen on, so unblock previous screen on blocking immediately. - unblockScreenOnByDisplayOffload(); - mScreenTurningOnWasBlockedByDisplayOffload = false; - } - - if (changed || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) { - // If we are trying to turn screen off, give policy a chance to do something before we - // actually turn the screen off. - if (isOff && !mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity()) { - if (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_ON - || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) { - setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_OFF); - blockScreenOff(); - mWindowManagerPolicy.screenTurningOff(mDisplayId, mPendingScreenOffUnblocker); - unblockScreenOff(); - } else if (mPendingScreenOffUnblocker != null) { - // Abort doing the state change until screen off is unblocked. - return false; - } - } - - if (!reportOnly && changed && readyToUpdateDisplayState() - && mPendingScreenOffUnblocker == null - && mPendingScreenOnUnblockerByDisplayOffload == null) { - Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenState", state); - - String propertyKey = "debug.tracing.screen_state"; - String propertyValue = String.valueOf(state); - try { - // TODO(b/153319140) remove when we can get this from the above trace invocation - SystemProperties.set(propertyKey, propertyValue); - } catch (RuntimeException e) { - Slog.e(mTag, "Failed to set a system property: key=" + propertyKey - + " value=" + propertyValue + " " + e.getMessage()); - } - - mPowerState.setScreenState(state); - // Tell battery stats about the transition. - noteScreenState(state); - } - } - - // Tell the window manager policy when the screen is turned off or on unless it's due - // to the proximity sensor. We temporarily block turning the screen on until the - // window manager is ready by leaving a black surface covering the screen. - // This surface is essentially the final state of the color fade animation and - // it is only removed once the window manager tells us that the activity has - // finished drawing underneath. - if (isOff && mReportedScreenStateToPolicy != REPORTED_TO_POLICY_SCREEN_OFF - && !mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity()) { - setReportedScreenState(REPORTED_TO_POLICY_SCREEN_OFF); - unblockScreenOn(); - mWindowManagerPolicy.screenTurnedOff(mDisplayId, mIsInTransition); - } else if (!isOff - && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_TURNING_OFF) { - - // We told policy already that screen was turning off, but now we changed our minds. - // Complete the full state transition on -> turningOff -> off. - unblockScreenOff(); - mWindowManagerPolicy.screenTurnedOff(mDisplayId, mIsInTransition); - setReportedScreenState(REPORTED_TO_POLICY_SCREEN_OFF); - } - if (!isOff - && (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_OFF - || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED)) { - setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_ON); - if (mPowerState.getColorFadeLevel() == 0.0f) { - blockScreenOn(); - } else { - unblockScreenOn(); - } - mWindowManagerPolicy.screenTurningOn(mDisplayId, mPendingScreenOnUnblocker); - } - - // Return true if the screen isn't blocked. - return mPendingScreenOnUnblocker == null - && mPendingScreenOnUnblockerByDisplayOffload == null; - } - - private void setReportedScreenState(int state) { - Trace.traceCounter(Trace.TRACE_TAG_POWER, "ReportedScreenStateToPolicy", state); - mReportedScreenStateToPolicy = state; - if (state == REPORTED_TO_POLICY_SCREEN_ON) { - mScreenTurningOnWasBlockedByDisplayOffload = false; - } - } - - private void loadAmbientLightSensor() { - final int fallbackType = mDisplayId == Display.DEFAULT_DISPLAY - ? Sensor.TYPE_LIGHT : SensorUtils.NO_FALLBACK; - mLightSensor = SensorUtils.findSensor(mSensorManager, - mDisplayDeviceConfig.getAmbientLightSensor(), fallbackType); - } - - private void loadScreenOffBrightnessSensor() { - mScreenOffBrightnessSensor = SensorUtils.findSensor(mSensorManager, - mDisplayDeviceConfig.getScreenOffBrightnessSensor(), SensorUtils.NO_FALLBACK); - } - - private float clampScreenBrightness(float value) { - if (Float.isNaN(value)) { - value = PowerManager.BRIGHTNESS_MIN; - } - return MathUtils.constrain(value, mBrightnessRangeController.getCurrentBrightnessMin(), - mBrightnessRangeController.getCurrentBrightnessMax()); - } - - private void animateScreenBrightness(float target, float sdrTarget, float rate) { - animateScreenBrightness(target, sdrTarget, rate, /* ignoreAnimationLimits = */false); - } - - private void animateScreenBrightness(float target, float sdrTarget, float rate, - boolean ignoreAnimationLimits) { - if (DEBUG) { - Slog.d(mTag, "Animating brightness: target=" + target + ", sdrTarget=" + sdrTarget - + ", rate=" + rate); - } - if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate, - ignoreAnimationLimits)) { - Trace.traceCounter(Trace.TRACE_TAG_POWER, "TargetScreenBrightness", (int) target); - - String propertyKey = "debug.tracing.screen_brightness"; - String propertyValue = String.valueOf(target); - try { - // TODO(b/153319140) remove when we can get this from the above trace invocation - SystemProperties.set(propertyKey, propertyValue); - } catch (RuntimeException e) { - Slog.e(mTag, "Failed to set a system property: key=" + propertyKey - + " value=" + propertyValue + " " + e.getMessage()); - } - - noteScreenBrightness(target); - } - } - - private void animateScreenStateChange(int target, boolean performScreenOffTransition) { - // If there is already an animation in progress, don't interfere with it. - if (mColorFadeEnabled - && (mColorFadeOnAnimator.isStarted() || mColorFadeOffAnimator.isStarted())) { - if (target != Display.STATE_ON) { - return; - } - // If display state changed to on, proceed and stop the color fade and turn screen on. - mPendingScreenOff = false; - } - - if (mDisplayBlanksAfterDozeConfig - && Display.isDozeState(mPowerState.getScreenState()) - && !Display.isDozeState(target)) { - // Skip the screen off animation and add a black surface to hide the - // contents of the screen. - mPowerState.prepareColorFade(mContext, - mColorFadeFadesConfig ? ColorFade.MODE_FADE : ColorFade.MODE_WARM_UP); - if (mColorFadeOffAnimator != null) { - mColorFadeOffAnimator.end(); - } - // Some display hardware will blank itself on the transition between doze and non-doze - // but still on display states. In this case we want to report to policy that the - // display has turned off so it can prepare the appropriate power on animation, but we - // don't want to actually transition to the fully off state since that takes - // significantly longer to transition from. - setScreenState(Display.STATE_OFF, target != Display.STATE_OFF /*reportOnly*/); - } - - // If we were in the process of turning off the screen but didn't quite - // finish. Then finish up now to prevent a jarring transition back - // to screen on if we skipped blocking screen on as usual. - if (mPendingScreenOff && target != Display.STATE_OFF) { - setScreenState(Display.STATE_OFF); - mPendingScreenOff = false; - mPowerState.dismissColorFadeResources(); - } - - if (target == Display.STATE_ON) { - // Want screen on. The contents of the screen may not yet - // be visible if the color fade has not been dismissed because - // its last frame of animation is solid black. - if (!setScreenState(Display.STATE_ON)) { - return; // screen on blocked - } - if (USE_COLOR_FADE_ON_ANIMATION && mColorFadeEnabled && mPowerRequest.isBrightOrDim()) { - // Perform screen on animation. - if (mPowerState.getColorFadeLevel() == 1.0f) { - mPowerState.dismissColorFade(); - } else if (mPowerState.prepareColorFade(mContext, - mColorFadeFadesConfig - ? ColorFade.MODE_FADE : ColorFade.MODE_WARM_UP)) { - mColorFadeOnAnimator.start(); - } else { - mColorFadeOnAnimator.end(); - } - } else { - // Skip screen on animation. - mPowerState.setColorFadeLevel(1.0f); - mPowerState.dismissColorFade(); - } - } else if (target == Display.STATE_DOZE) { - // Want screen dozing. - // Wait for brightness animation to complete beforehand when entering doze - // from screen on to prevent a perceptible jump because brightness may operate - // differently when the display is configured for dozing. - if (mScreenBrightnessRampAnimator.isAnimating() - && mPowerState.getScreenState() == Display.STATE_ON) { - return; - } - - // Set screen state. - if (!setScreenState(Display.STATE_DOZE)) { - return; // screen on blocked - } - - // Dismiss the black surface without fanfare. - mPowerState.setColorFadeLevel(1.0f); - mPowerState.dismissColorFade(); - } else if (target == Display.STATE_DOZE_SUSPEND) { - // Want screen dozing and suspended. - // Wait for brightness animation to complete beforehand unless already - // suspended because we may not be able to change it after suspension. - if (mScreenBrightnessRampAnimator.isAnimating() - && mPowerState.getScreenState() != Display.STATE_DOZE_SUSPEND) { - return; - } - - // If not already suspending, temporarily set the state to doze until the - // screen on is unblocked, then suspend. - if (mPowerState.getScreenState() != Display.STATE_DOZE_SUSPEND) { - if (!setScreenState(Display.STATE_DOZE)) { - return; // screen on blocked - } - setScreenState(Display.STATE_DOZE_SUSPEND); // already on so can't block - } - - // Dismiss the black surface without fanfare. - mPowerState.setColorFadeLevel(1.0f); - mPowerState.dismissColorFade(); - } else if (target == Display.STATE_ON_SUSPEND) { - // Want screen full-power and suspended. - // Wait for brightness animation to complete beforehand unless already - // suspended because we may not be able to change it after suspension. - if (mScreenBrightnessRampAnimator.isAnimating() - && mPowerState.getScreenState() != Display.STATE_ON_SUSPEND) { - return; - } - - // If not already suspending, temporarily set the state to on until the - // screen on is unblocked, then suspend. - if (mPowerState.getScreenState() != Display.STATE_ON_SUSPEND) { - if (!setScreenState(Display.STATE_ON)) { - return; - } - setScreenState(Display.STATE_ON_SUSPEND); - } - - // Dismiss the black surface without fanfare. - mPowerState.setColorFadeLevel(1.0f); - mPowerState.dismissColorFade(); - } else { - // Want screen off. - mPendingScreenOff = true; - if (!mColorFadeEnabled) { - mPowerState.setColorFadeLevel(0.0f); - } - - if (mPowerState.getColorFadeLevel() == 0.0f) { - // Turn the screen off. - // A black surface is already hiding the contents of the screen. - setScreenState(Display.STATE_OFF); - mPendingScreenOff = false; - mPowerState.dismissColorFadeResources(); - } else if (performScreenOffTransition - && mPowerState.prepareColorFade(mContext, - mColorFadeFadesConfig - ? ColorFade.MODE_FADE : ColorFade.MODE_COOL_DOWN) - && mPowerState.getScreenState() != Display.STATE_OFF) { - // Perform the screen off animation. - mColorFadeOffAnimator.start(); - } else { - // Skip the screen off animation and add a black surface to hide the - // contents of the screen. - mColorFadeOffAnimator.end(); - } - } - } - - private final Runnable mCleanListener = this::sendUpdatePowerState; - - private void sendOnStateChangedWithWakelock() { - boolean wakeLockAcquired = mWakelockController.acquireWakelock( - WakelockController.WAKE_LOCK_STATE_CHANGED); - if (wakeLockAcquired) { - mHandler.post(mWakelockController.getOnStateChangedRunnable()); - } - } - - private void logDisplayPolicyChanged(int newPolicy) { - LogMaker log = new LogMaker(MetricsEvent.DISPLAY_POLICY); - log.setType(MetricsEvent.TYPE_UPDATE); - log.setSubtype(newPolicy); - MetricsLogger.action(log); - } - - private void handleSettingsChange(boolean userSwitch) { - mDisplayBrightnessController - .setPendingScreenBrightness(mDisplayBrightnessController - .getScreenBrightnessSetting()); - mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments(userSwitch); - if (userSwitch) { - // Don't treat user switches as user initiated change. - mDisplayBrightnessController - .setAndNotifyCurrentScreenBrightness(mDisplayBrightnessController - .getPendingScreenBrightness()); - if (mAutomaticBrightnessController != null) { - mAutomaticBrightnessController.resetShortTermModel(); - } - } - sendUpdatePowerState(); - } - - private void handleBrightnessModeChange() { - final int screenBrightnessModeSetting = Settings.System.getIntForUser( - mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT); - mHandler.postAtTime(() -> { - mAutomaticBrightnessStrategy.setUseAutoBrightness(screenBrightnessModeSetting - == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - updatePowerState(); - }, mClock.uptimeMillis()); - } - - - @Override - public float getScreenBrightnessSetting() { - return mDisplayBrightnessController.getScreenBrightnessSetting(); - } - - @Override - public void setBrightness(float brightnessValue, int userSerial) { - mDisplayBrightnessController.setBrightness(clampScreenBrightness(brightnessValue), - userSerial); - } - - @Override - public int getDisplayId() { - return mDisplayId; - } - - @Override - public int getLeadDisplayId() { - return mLeadDisplayId; - } - - @Override - public void setBrightnessToFollow(float leadDisplayBrightness, float nits, float ambientLux, - boolean slowChange) { - mBrightnessRangeController.onAmbientLuxChange(ambientLux); - if (nits == BrightnessMappingStrategy.INVALID_NITS) { - mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness, slowChange); - } else { - float brightness = mDisplayBrightnessController.getBrightnessFromNits(nits); - if (BrightnessUtils.isValidBrightnessValue(brightness)) { - mDisplayBrightnessController.setBrightnessToFollow(brightness, slowChange); - } else { - // The device does not support nits - mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness, - slowChange); - } - } - sendUpdatePowerState(); - } - - private void notifyBrightnessTrackerChanged(float brightness, boolean userInitiated, - boolean wasShortTermModelActive, boolean autobrightnessEnabled, - boolean brightnessIsTemporary, boolean shouldUseAutoBrightness) { - - final float brightnessInNits = - mDisplayBrightnessController.convertToAdjustedNits(brightness); - // Don't report brightness to brightnessTracker: - // If brightness is temporary (ie the slider has not been released) - // or if we are in idle screen brightness mode. - // or display is not on - // or we shouldn't be using autobrightness - // or the nits is invalid. - if (brightnessIsTemporary - || mAutomaticBrightnessController == null - || mAutomaticBrightnessController.isInIdleMode() - || !autobrightnessEnabled - || mBrightnessTracker == null - || !shouldUseAutoBrightness - || brightnessInNits < 0.0f) { - return; - } - - if (userInitiated && (mAutomaticBrightnessController == null - || !mAutomaticBrightnessController.hasValidAmbientLux())) { - // If we don't have a valid lux reading we can't report a valid - // slider event so notify as if the system changed the brightness. - userInitiated = false; - } - - // We only want to track changes on devices that can actually map the display backlight - // values into a physical brightness unit since the value provided by the API is in - // nits and not using the arbitrary backlight units. - final float powerFactor = mPowerRequest.lowPowerMode - ? mPowerRequest.screenLowPowerBrightnessFactor - : 1.0f; - mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated, - powerFactor, wasShortTermModelActive, - mAutomaticBrightnessController.isDefaultConfig(), mUniqueDisplayId, - mAutomaticBrightnessController.getLastSensorValues(), - mAutomaticBrightnessController.getLastSensorTimestamps()); - } - - @Override - public void addDisplayBrightnessFollower(DisplayPowerControllerInterface follower) { - synchronized (mLock) { - mDisplayBrightnessFollowers.append(follower.getDisplayId(), follower); - sendUpdatePowerStateLocked(); - } - } - - @Override - public void removeDisplayBrightnessFollower(DisplayPowerControllerInterface follower) { - synchronized (mLock) { - mDisplayBrightnessFollowers.remove(follower.getDisplayId()); - mHandler.postAtTime(() -> follower.setBrightnessToFollow( - PowerManager.BRIGHTNESS_INVALID_FLOAT, BrightnessMappingStrategy.INVALID_NITS, - /* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis()); - } - } - - @GuardedBy("mLock") - private void clearDisplayBrightnessFollowersLocked() { - for (int i = 0; i < mDisplayBrightnessFollowers.size(); i++) { - DisplayPowerControllerInterface follower = mDisplayBrightnessFollowers.valueAt(i); - mHandler.postAtTime(() -> follower.setBrightnessToFollow( - PowerManager.BRIGHTNESS_INVALID_FLOAT, BrightnessMappingStrategy.INVALID_NITS, - /* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis()); - } - mDisplayBrightnessFollowers.clear(); - } - - @Override - public void dump(final PrintWriter pw) { - synchronized (mLock) { - pw.println(); - pw.println("Display Power Controller:"); - pw.println(" mDisplayId=" + mDisplayId); - pw.println(" mLeadDisplayId=" + mLeadDisplayId); - pw.println(" mLightSensor=" + mLightSensor); - pw.println(" mDisplayBrightnessFollowers=" + mDisplayBrightnessFollowers); - - pw.println(); - pw.println("Display Power Controller Locked State:"); - pw.println(" mDisplayReadyLocked=" + mDisplayReadyLocked); - pw.println(" mPendingRequestLocked=" + mPendingRequestLocked); - pw.println(" mPendingRequestChangedLocked=" + mPendingRequestChangedLocked); - pw.println(" mPendingUpdatePowerStateLocked=" + mPendingUpdatePowerStateLocked); - } - - pw.println(); - pw.println("Display Power Controller Configuration:"); - pw.println(" mScreenBrightnessDozeConfig=" + mScreenBrightnessDozeConfig); - pw.println(" mUseSoftwareAutoBrightnessConfig=" + mUseSoftwareAutoBrightnessConfig); - pw.println(" mSkipScreenOnBrightnessRamp=" + mSkipScreenOnBrightnessRamp); - pw.println(" mColorFadeFadesConfig=" + mColorFadeFadesConfig); - pw.println(" mColorFadeEnabled=" + mColorFadeEnabled); - pw.println(" mIsDisplayInternal=" + mIsDisplayInternal); - synchronized (mCachedBrightnessInfo) { - pw.println(" mCachedBrightnessInfo.brightness=" - + mCachedBrightnessInfo.brightness.value); - pw.println(" mCachedBrightnessInfo.adjustedBrightness=" - + mCachedBrightnessInfo.adjustedBrightness.value); - pw.println(" mCachedBrightnessInfo.brightnessMin=" - + mCachedBrightnessInfo.brightnessMin.value); - pw.println(" mCachedBrightnessInfo.brightnessMax=" - + mCachedBrightnessInfo.brightnessMax.value); - pw.println(" mCachedBrightnessInfo.hbmMode=" + mCachedBrightnessInfo.hbmMode.value); - pw.println(" mCachedBrightnessInfo.hbmTransitionPoint=" - + mCachedBrightnessInfo.hbmTransitionPoint.value); - pw.println(" mCachedBrightnessInfo.brightnessMaxReason =" - + mCachedBrightnessInfo.brightnessMaxReason.value); - } - pw.println(" mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig); - pw.println(" mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig); - mHandler.runWithScissors(() -> dumpLocal(pw), 1000); - } - - private void dumpLocal(PrintWriter pw) { - pw.println(); - pw.println("Display Power Controller Thread State:"); - pw.println(" mPowerRequest=" + mPowerRequest); - pw.println(" mBrightnessReason=" + mBrightnessReason); - pw.println(" mAppliedDimming=" + mAppliedDimming); - pw.println(" mAppliedThrottling=" + mAppliedThrottling); - pw.println(" mDozing=" + mDozing); - pw.println(" mSkipRampState=" + skipRampStateToString(mSkipRampState)); - pw.println(" mScreenOnBlockStartRealTime=" + mScreenOnBlockStartRealTime); - pw.println(" mScreenOffBlockStartRealTime=" + mScreenOffBlockStartRealTime); - pw.println(" mPendingScreenOnUnblocker=" + mPendingScreenOnUnblocker); - pw.println(" mPendingScreenOffUnblocker=" + mPendingScreenOffUnblocker); - pw.println(" mPendingScreenOff=" + mPendingScreenOff); - pw.println(" mReportedToPolicy=" - + reportedToPolicyToString(mReportedScreenStateToPolicy)); - pw.println(" mIsRbcActive=" + mIsRbcActive); - IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); - mAutomaticBrightnessStrategy.dump(ipw); - - if (mScreenBrightnessRampAnimator != null) { - pw.println(" mScreenBrightnessRampAnimator.isAnimating()=" - + mScreenBrightnessRampAnimator.isAnimating()); - } - - if (mColorFadeOnAnimator != null) { - pw.println(" mColorFadeOnAnimator.isStarted()=" - + mColorFadeOnAnimator.isStarted()); - } - if (mColorFadeOffAnimator != null) { - pw.println(" mColorFadeOffAnimator.isStarted()=" - + mColorFadeOffAnimator.isStarted()); - } - - if (mPowerState != null) { - mPowerState.dump(pw); - } - - if (mAutomaticBrightnessController != null) { - mAutomaticBrightnessController.dump(pw); - dumpBrightnessEvents(pw); - } - - dumpRbcEvents(pw); - - if (mScreenOffBrightnessSensorController != null) { - mScreenOffBrightnessSensorController.dump(pw); - } - - if (mBrightnessRangeController != null) { - mBrightnessRangeController.dump(pw); - } - - if (mBrightnessThrottler != null) { - mBrightnessThrottler.dump(pw); - } - - pw.println(); - if (mDisplayWhiteBalanceController != null) { - mDisplayWhiteBalanceController.dump(pw); - mDisplayWhiteBalanceSettings.dump(pw); - } - - pw.println(); - - if (mWakelockController != null) { - mWakelockController.dumpLocal(pw); - } - - pw.println(); - if (mDisplayBrightnessController != null) { - mDisplayBrightnessController.dump(pw); - } - - pw.println(); - if (mDisplayStateController != null) { - mDisplayStateController.dumpsys(pw); - } - - pw.println(); - if (mBrightnessClamperController != null) { - mBrightnessClamperController.dump(ipw); - } - } - - - private static String reportedToPolicyToString(int state) { - switch (state) { - case REPORTED_TO_POLICY_SCREEN_OFF: - return "REPORTED_TO_POLICY_SCREEN_OFF"; - case REPORTED_TO_POLICY_SCREEN_TURNING_ON: - return "REPORTED_TO_POLICY_SCREEN_TURNING_ON"; - case REPORTED_TO_POLICY_SCREEN_ON: - return "REPORTED_TO_POLICY_SCREEN_ON"; - default: - return Integer.toString(state); - } - } - - private static String skipRampStateToString(int state) { - switch (state) { - case RAMP_STATE_SKIP_NONE: - return "RAMP_STATE_SKIP_NONE"; - case RAMP_STATE_SKIP_INITIAL: - return "RAMP_STATE_SKIP_INITIAL"; - case RAMP_STATE_SKIP_AUTOBRIGHT: - return "RAMP_STATE_SKIP_AUTOBRIGHT"; - default: - return Integer.toString(state); - } - } - - private void dumpBrightnessEvents(PrintWriter pw) { - int size = mBrightnessEventRingBuffer.size(); - if (size < 1) { - pw.println("No Automatic Brightness Adjustments"); - return; - } - - pw.println("Automatic Brightness Adjustments Last " + size + " Events: "); - BrightnessEvent[] eventArray = mBrightnessEventRingBuffer.toArray(); - for (int i = 0; i < mBrightnessEventRingBuffer.size(); i++) { - pw.println(" " + eventArray[i].toString()); - } - } - - private void dumpRbcEvents(PrintWriter pw) { - int size = mRbcEventRingBuffer.size(); - if (size < 1) { - pw.println("No Reduce Bright Colors Adjustments"); - return; - } - - pw.println("Reduce Bright Colors Adjustments Last " + size + " Events: "); - BrightnessEvent[] eventArray = mRbcEventRingBuffer.toArray(); - for (int i = 0; i < mRbcEventRingBuffer.size(); i++) { - pw.println(" " + eventArray[i]); - } - } - - - private void noteScreenState(int screenState) { - // Log screen state change with display id - FrameworkStatsLog.write(FrameworkStatsLog.SCREEN_STATE_CHANGED_V2, - screenState, mDisplayStatsId); - if (mBatteryStats != null) { - try { - // TODO(multi-display): make this multi-display - mBatteryStats.noteScreenState(screenState); - } catch (RemoteException e) { - // same process - } - } - } - - @SuppressLint("AndroidFrameworkRequiresPermission") - private void noteScreenBrightness(float brightness) { - if (mBatteryStats != null) { - try { - // TODO(brightnessfloat): change BatteryStats to use float - int brightnessInt = mFlags.isBrightnessIntRangeUserPerceptionEnabled() - ? BrightnessSynchronizer.brightnessFloatToIntSetting(mContext, brightness) - : BrightnessSynchronizer.brightnessFloatToInt(brightness); - mBatteryStats.noteScreenBrightness(brightnessInt); - } catch (RemoteException e) { - // same process - } - } - } - - private void reportStats(float brightness) { - if (mLastStatsBrightness == brightness) { - return; - } - - float hbmTransitionPoint = PowerManager.BRIGHTNESS_MAX; - synchronized (mCachedBrightnessInfo) { - if (mCachedBrightnessInfo.hbmTransitionPoint == null) { - return; - } - hbmTransitionPoint = mCachedBrightnessInfo.hbmTransitionPoint.value; - } - - final boolean aboveTransition = brightness > hbmTransitionPoint; - final boolean oldAboveTransition = mLastStatsBrightness > hbmTransitionPoint; - - if (aboveTransition || oldAboveTransition) { - mLastStatsBrightness = brightness; - mHandler.removeMessages(MSG_STATSD_HBM_BRIGHTNESS); - if (aboveTransition != oldAboveTransition) { - // report immediately - logHbmBrightnessStats(brightness, mDisplayStatsId); - } else { - // delay for rate limiting - Message msg = mHandler.obtainMessage(); - msg.what = MSG_STATSD_HBM_BRIGHTNESS; - msg.arg1 = Float.floatToIntBits(brightness); - msg.arg2 = mDisplayStatsId; - mHandler.sendMessageAtTime(msg, mClock.uptimeMillis() - + BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS); - } - } - } - - private void logHbmBrightnessStats(float brightness, int displayStatsId) { - synchronized (mHandler) { - FrameworkStatsLog.write( - FrameworkStatsLog.DISPLAY_HBM_BRIGHTNESS_CHANGED, displayStatsId, brightness); - } - } - - // Return bucket index of range_[left]_[right] where - // left <= nits < right - private int nitsToRangeIndex(float nits) { - for (int i = 0; i < BRIGHTNESS_RANGE_BOUNDARIES.length; i++) { - if (nits < BRIGHTNESS_RANGE_BOUNDARIES[i]) { - return BRIGHTNESS_RANGE_INDEX[i]; - } - } - return FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_3000_INF; - } - - private int convertBrightnessReasonToStatsEnum(int brightnessReason) { - switch(brightnessReason) { - case BrightnessReason.REASON_UNKNOWN: - return FrameworkStatsLog - .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_UNKNOWN; - case BrightnessReason.REASON_MANUAL: - return FrameworkStatsLog - .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_MANUAL; - case BrightnessReason.REASON_DOZE: - return FrameworkStatsLog - .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_DOZE; - case BrightnessReason.REASON_DOZE_DEFAULT: - return FrameworkStatsLog - .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_DOZE_DEFAULT; - case BrightnessReason.REASON_AUTOMATIC: - return FrameworkStatsLog - .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_AUTOMATIC; - case BrightnessReason.REASON_SCREEN_OFF: - return FrameworkStatsLog - .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_SCREEN_OFF; - case BrightnessReason.REASON_OVERRIDE: - return FrameworkStatsLog - .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_OVERRIDE; - case BrightnessReason.REASON_TEMPORARY: - return FrameworkStatsLog - .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_TEMPORARY; - case BrightnessReason.REASON_BOOST: - return FrameworkStatsLog - .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_BOOST; - case BrightnessReason.REASON_SCREEN_OFF_BRIGHTNESS_SENSOR: - return FrameworkStatsLog - .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_SCREEN_OFF_BRIGHTNESS_SENSOR; - case BrightnessReason.REASON_FOLLOWER: - return FrameworkStatsLog - .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_FOLLOWER; - } - return FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_UNKNOWN; - } - - private void logBrightnessEvent(BrightnessEvent event, float unmodifiedBrightness) { - int modifier = event.getReason().getModifier(); - int flags = event.getFlags(); - // It's easier to check if the brightness is at maximum level using the brightness - // value untouched by any modifiers - boolean brightnessIsMax = unmodifiedBrightness == event.getHbmMax(); - float brightnessInNits = - mDisplayBrightnessController.convertToAdjustedNits(event.getBrightness()); - float appliedLowPowerMode = event.isLowPowerModeSet() ? event.getPowerFactor() : -1f; - int appliedRbcStrength = event.isRbcEnabled() ? event.getRbcStrength() : -1; - float appliedHbmMaxNits = - event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF - ? -1f : mDisplayBrightnessController.convertToAdjustedNits(event.getHbmMax()); - // thermalCapNits set to -1 if not currently capping max brightness - float appliedThermalCapNits = - event.getThermalMax() == PowerManager.BRIGHTNESS_MAX - ? -1f : mDisplayBrightnessController.convertToAdjustedNits(event.getThermalMax()); - if (mIsDisplayInternal) { - FrameworkStatsLog.write(FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED, - mDisplayBrightnessController - .convertToAdjustedNits(event.getInitialBrightness()), - brightnessInNits, - event.getLux(), - event.getPhysicalDisplayId(), - event.wasShortTermModelActive(), - appliedLowPowerMode, - appliedRbcStrength, - appliedHbmMaxNits, - appliedThermalCapNits, - event.isAutomaticBrightnessEnabled(), - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__REASON__REASON_MANUAL, - convertBrightnessReasonToStatsEnum(event.getReason().getReason()), - nitsToRangeIndex(brightnessInNits), - brightnessIsMax, - event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT, - event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, - (modifier & BrightnessReason.MODIFIER_LOW_POWER) > 0, - mBrightnessClamperController.getBrightnessMaxReason(), - // TODO: (flc) add brightnessMinReason here too. - (modifier & BrightnessReason.MODIFIER_DIMMED) > 0, - event.isRbcEnabled(), - (flags & BrightnessEvent.FLAG_INVALID_LUX) > 0, - (flags & BrightnessEvent.FLAG_DOZE_SCALE) > 0, - (flags & BrightnessEvent.FLAG_USER_SET) > 0, - (flags & BrightnessEvent.FLAG_IDLE_CURVE) > 0, - (flags & BrightnessEvent.FLAG_LOW_POWER_MODE) > 0); - } - } - - /** - * Indicates whether the display state is ready to update. If this is the default display, we - * want to update it right away so that we can draw the boot animation on it. If it is not - * the default display, drawing the boot animation on it would look incorrect, so we need - * to wait until boot is completed. - * @return True if the display state is ready to update - */ - private boolean readyToUpdateDisplayState() { - return mDisplayId == Display.DEFAULT_DISPLAY || mBootCompleted; - } - - private final class DisplayControllerHandler extends Handler { - DisplayControllerHandler(Looper looper) { - super(looper, null, true /*async*/); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_UPDATE_POWER_STATE: - updatePowerState(); - break; - - case MSG_SCREEN_ON_UNBLOCKED: - if (mPendingScreenOnUnblocker == msg.obj) { - unblockScreenOn(); - updatePowerState(); - } - break; - case MSG_SCREEN_OFF_UNBLOCKED: - if (mPendingScreenOffUnblocker == msg.obj) { - unblockScreenOff(); - updatePowerState(); - } - break; - case MSG_OFFLOADING_SCREEN_ON_UNBLOCKED: - if (mDisplayOffloadSession == msg.obj) { - unblockScreenOnByDisplayOffload(); - updatePowerState(); - } - break; - case MSG_CONFIGURE_BRIGHTNESS: - BrightnessConfiguration brightnessConfiguration = - (BrightnessConfiguration) msg.obj; - mAutomaticBrightnessStrategy.setBrightnessConfiguration(brightnessConfiguration, - msg.arg1 == 1); - if (mBrightnessTracker != null) { - mBrightnessTracker - .setShouldCollectColorSample(brightnessConfiguration != null - && brightnessConfiguration.shouldCollectColorSamples()); - } - updatePowerState(); - break; - - case MSG_SET_TEMPORARY_BRIGHTNESS: - // TODO: Should we have a a timeout for the temporary brightness? - mDisplayBrightnessController - .setTemporaryBrightness(Float.intBitsToFloat(msg.arg1)); - updatePowerState(); - break; - - case MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT: - mAutomaticBrightnessStrategy - .setTemporaryAutoBrightnessAdjustment(Float.intBitsToFloat(msg.arg1)); - updatePowerState(); - break; - - case MSG_STOP: - cleanupHandlerThreadAfterStop(); - break; - - case MSG_UPDATE_BRIGHTNESS: - if (mStopped) { - return; - } - handleSettingsChange(false /*userSwitch*/); - break; - - case MSG_UPDATE_RBC: - handleRbcChanged(); - break; - - case MSG_BRIGHTNESS_RAMP_DONE: - if (mPowerState != null) { - final float brightness = mPowerState.getScreenBrightness(); - reportStats(brightness); - } - break; - - case MSG_STATSD_HBM_BRIGHTNESS: - logHbmBrightnessStats(Float.intBitsToFloat(msg.arg1), msg.arg2); - break; - - case MSG_SWITCH_USER: - handleOnSwitchUser(msg.arg1); - break; - - case MSG_BOOT_COMPLETED: - mBootCompleted = true; - updatePowerState(); - break; - - case MSG_SET_DWBC_STRONG_MODE: - setDwbcStrongMode(msg.arg1); - break; - - case MSG_SET_DWBC_COLOR_OVERRIDE: - final float cct = Float.intBitsToFloat(msg.arg1); - setDwbcOverride(cct); - break; - - case MSG_SET_DWBC_LOGGING_ENABLED: - setDwbcLoggingEnabled(msg.arg1); - break; - case MSG_SET_BRIGHTNESS_FROM_OFFLOAD: - mDisplayBrightnessController.setBrightnessFromOffload( - Float.intBitsToFloat(msg.arg1)); - updatePowerState(); - break; - } - } - } - - - private final class SettingsObserver extends ContentObserver { - SettingsObserver(Handler handler) { - super(handler); - } - - @Override - public void onChange(boolean selfChange, Uri uri) { - if (uri.equals(Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE))) { - handleBrightnessModeChange(); - } else if (uri.equals(Settings.System.getUriFor( - Settings.System.SCREEN_BRIGHTNESS_FOR_ALS))) { - int preset = Settings.System.getIntForUser(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_FOR_ALS, - Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL, - UserHandle.USER_CURRENT); - Slog.i(mTag, "Setting up auto-brightness for preset " - + autoBrightnessPresetToString(preset)); - setUpAutoBrightness(mContext, mHandler); - sendUpdatePowerState(); - } else { - handleSettingsChange(false /* userSwitch */); - } - } - } - - private final class ScreenOnUnblocker implements WindowManagerPolicy.ScreenOnListener { - @Override - public void onScreenOn() { - Message msg = mHandler.obtainMessage(MSG_SCREEN_ON_UNBLOCKED, this); - mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); - } - } - - private final class ScreenOffUnblocker implements WindowManagerPolicy.ScreenOffListener { - @Override - public void onScreenOff() { - Message msg = mHandler.obtainMessage(MSG_SCREEN_OFF_UNBLOCKED, this); - mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); - } - } - - @Override - public void setAutoBrightnessLoggingEnabled(boolean enabled) { - if (mAutomaticBrightnessController != null) { - mAutomaticBrightnessController.setLoggingEnabled(enabled); - } - } - - @Override // DisplayWhiteBalanceController.Callbacks - public void updateWhiteBalance() { - sendUpdatePowerState(); - } - - @Override - public void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) { - Message msg = mHandler.obtainMessage(); - msg.what = MSG_SET_DWBC_LOGGING_ENABLED; - msg.arg1 = enabled ? 1 : 0; - msg.sendToTarget(); - } - - @Override - public void setAmbientColorTemperatureOverride(float cct) { - Message msg = mHandler.obtainMessage(); - msg.what = MSG_SET_DWBC_COLOR_OVERRIDE; - msg.arg1 = Float.floatToIntBits(cct); - msg.sendToTarget(); - } - - /** Functional interface for providing time. */ - @VisibleForTesting - interface Clock { - /** - * Returns current time in milliseconds since boot, not counting time spent in deep sleep. - */ - long uptimeMillis(); - } - - @VisibleForTesting - static class Injector { - Clock getClock() { - return SystemClock::uptimeMillis; - } - - DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade, - int displayId, int displayState) { - return new DisplayPowerState(blanker, colorFade, displayId, displayState); - } - - DualRampAnimator<DisplayPowerState> getDualRampAnimator(DisplayPowerState dps, - FloatProperty<DisplayPowerState> firstProperty, - FloatProperty<DisplayPowerState> secondProperty) { - return new DualRampAnimator(dps, firstProperty, secondProperty); - } - - WakelockController getWakelockController(int displayId, - DisplayPowerCallbacks displayPowerCallbacks) { - return new WakelockController(displayId, displayPowerCallbacks); - } - - DisplayPowerProximityStateController getDisplayPowerProximityStateController( - WakelockController wakelockController, DisplayDeviceConfig displayDeviceConfig, - Looper looper, Runnable nudgeUpdatePowerState, - int displayId, SensorManager sensorManager) { - return new DisplayPowerProximityStateController(wakelockController, displayDeviceConfig, - looper, nudgeUpdatePowerState, - displayId, sensorManager, /* injector= */ null); - } - - AutomaticBrightnessController getAutomaticBrightnessController( - AutomaticBrightnessController.Callbacks callbacks, Looper looper, - SensorManager sensorManager, Sensor lightSensor, - SparseArray<BrightnessMappingStrategy> brightnessMappingStrategyMap, - int lightSensorWarmUpTime, float brightnessMin, float brightnessMax, - float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, - long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, - long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle, - boolean resetAmbientLuxAfterWarmUpConfig, - HysteresisLevels ambientBrightnessThresholds, - HysteresisLevels screenBrightnessThresholds, - HysteresisLevels ambientBrightnessThresholdsIdle, - HysteresisLevels screenBrightnessThresholdsIdle, Context context, - BrightnessRangeController brightnessModeController, - BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort, - int ambientLightHorizonLong, float userLux, float userNits) { - return new AutomaticBrightnessController(callbacks, looper, sensorManager, lightSensor, - brightnessMappingStrategyMap, lightSensorWarmUpTime, brightnessMin, - brightnessMax, dozeScaleFactor, lightSensorRate, initialLightSensorRate, - brighteningLightDebounceConfig, darkeningLightDebounceConfig, - brighteningLightDebounceConfigIdle, darkeningLightDebounceConfigIdle, - resetAmbientLuxAfterWarmUpConfig, ambientBrightnessThresholds, - screenBrightnessThresholds, ambientBrightnessThresholdsIdle, - screenBrightnessThresholdsIdle, context, brightnessModeController, - brightnessThrottler, ambientLightHorizonShort, ambientLightHorizonLong, userLux, - userNits); - } - - BrightnessMappingStrategy getDefaultModeBrightnessMapper(Context context, - DisplayDeviceConfig displayDeviceConfig, - DisplayWhiteBalanceController displayWhiteBalanceController) { - return BrightnessMappingStrategy.create(context, displayDeviceConfig, - AUTO_BRIGHTNESS_MODE_DEFAULT, displayWhiteBalanceController); - } - - HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages, - float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels, - float[] darkeningThresholdLevels, float minDarkeningThreshold, - float minBrighteningThreshold) { - return new HysteresisLevels(brighteningThresholdsPercentages, - darkeningThresholdsPercentages, brighteningThresholdLevels, - darkeningThresholdLevels, minDarkeningThreshold, minBrighteningThreshold); - } - - HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages, - float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels, - float[] darkeningThresholdLevels, float minDarkeningThreshold, - float minBrighteningThreshold, boolean potentialOldBrightnessRange) { - return new HysteresisLevels(brighteningThresholdsPercentages, - darkeningThresholdsPercentages, brighteningThresholdLevels, - darkeningThresholdLevels, minDarkeningThreshold, minBrighteningThreshold, - potentialOldBrightnessRange); - } - - ScreenOffBrightnessSensorController getScreenOffBrightnessSensorController( - SensorManager sensorManager, - Sensor lightSensor, - Handler handler, - ScreenOffBrightnessSensorController.Clock clock, - int[] sensorValueToLux, - BrightnessMappingStrategy brightnessMapper) { - return new ScreenOffBrightnessSensorController( - sensorManager, - lightSensor, - handler, - clock, - sensorValueToLux, - brightnessMapper - ); - } - - HighBrightnessModeController getHighBrightnessModeController(Handler handler, int width, - int height, IBinder displayToken, String displayUniqueId, float brightnessMin, - float brightnessMax, DisplayDeviceConfig.HighBrightnessModeData hbmData, - HighBrightnessModeController.HdrBrightnessDeviceConfig hdrBrightnessCfg, - Runnable hbmChangeCallback, HighBrightnessModeMetadata hbmMetadata, - Context context) { - return new HighBrightnessModeController(handler, width, height, displayToken, - displayUniqueId, brightnessMin, brightnessMax, hbmData, hdrBrightnessCfg, - hbmChangeCallback, hbmMetadata, context); - } - - BrightnessRangeController getBrightnessRangeController( - HighBrightnessModeController hbmController, Runnable modeChangeCallback, - DisplayDeviceConfig displayDeviceConfig, Handler handler, - DisplayManagerFlags flags, IBinder displayToken, DisplayDeviceInfo info) { - return new BrightnessRangeController(hbmController, - modeChangeCallback, displayDeviceConfig, handler, flags, displayToken, info); - } - - BrightnessClamperController getBrightnessClamperController(Handler handler, - BrightnessClamperController.ClamperChangeListener clamperChangeListener, - BrightnessClamperController.DisplayDeviceData data, Context context, - DisplayManagerFlags flags) { - - return new BrightnessClamperController(handler, clamperChangeListener, data, context, - flags); - } - - DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler, - SensorManager sensorManager, Resources resources) { - return DisplayWhiteBalanceFactory.create(handler, - sensorManager, resources); - } - - boolean isColorFadeEnabled() { - return !ActivityManager.isLowRamDeviceStatic(); - } - } - - static class CachedBrightnessInfo { - public MutableFloat brightness = new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT); - public MutableFloat adjustedBrightness = - new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT); - public MutableFloat brightnessMin = - new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT); - public MutableFloat brightnessMax = - new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT); - public MutableInt hbmMode = new MutableInt(BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF); - public MutableFloat hbmTransitionPoint = - new MutableFloat(HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID); - public MutableInt brightnessMaxReason = - new MutableInt(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE); - - public boolean checkAndSetFloat(MutableFloat mf, float f) { - if (mf.value != f) { - mf.value = f; - return true; - } - return false; - } - - public boolean checkAndSetInt(MutableInt mi, int i) { - if (mi.value != i) { - mi.value = i; - return true; - } - return false; - } - } -} diff --git a/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java b/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java index 465584c3d90c..403dfbe920ee 100644 --- a/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java +++ b/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java @@ -41,13 +41,6 @@ public class DeviceConfigParameterProvider { mDeviceConfig = deviceConfig; } - // feature: revamping_display_power_controller_feature - // parameter: use_newly_structured_display_power_controller - public boolean isNewPowerControllerFeatureEnabled() { - return mDeviceConfig.getBoolean(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, - DisplayManager.DeviceConfig.KEY_NEW_POWER_CONTROLLER, true); - } - // feature: hdr_output_control // parameter: enable_hdr_output_control public boolean isHdrOutputControlFeatureEnabled() { diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java index 7e990c6c2f4a..380106ba486d 100644 --- a/services/core/java/com/android/server/input/InputManagerInternal.java +++ b/services/core/java/com/android/server/input/InputManagerInternal.java @@ -104,10 +104,13 @@ public abstract class InputManagerInternal { public abstract PointF getCursorPosition(); /** - * Sets the pointer acceleration. - * See {@code frameworks/native/include/input/VelocityControl.h#VelocityControlParameters}. + * Enables or disables pointer acceleration for mouse movements. + * + * Note that this only affects pointer movements from mice (that is, pointing devices which send + * relative motions, including trackballs and pointing sticks), not from other pointer devices + * such as touchpads and styluses. */ - public abstract void setPointerAcceleration(float acceleration, int displayId); + public abstract void setMousePointerAccelerationEnabled(boolean enabled, int displayId); /** * Sets the eligibility of windows on a given display for pointer capture. If a display is diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 36dac8316e54..bc7205ae2c07 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -64,7 +64,6 @@ import android.os.CombinedVibration; import android.os.Environment; import android.os.Handler; import android.os.IBinder; -import android.os.IInputConstants; import android.os.IVibratorStateListener; import android.os.InputEventInjectionResult; import android.os.InputEventInjectionSync; @@ -1372,9 +1371,9 @@ public class InputManagerService extends IInputManager.Stub mNative.setPointerSpeed(speed); } - private void setPointerAcceleration(float acceleration, int displayId) { + private void setMousePointerAccelerationEnabled(boolean enabled, int displayId) { updateAdditionalDisplayInputProperties(displayId, - properties -> properties.pointerAcceleration = acceleration); + properties -> properties.mousePointerAccelerationEnabled = enabled); } private void setPointerIconVisible(boolean visible, int displayId) { @@ -2261,7 +2260,8 @@ public class InputManagerService extends IInputManager.Stub + mAdditionalDisplayInputProperties.keyAt(i)); final AdditionalDisplayInputProperties properties = mAdditionalDisplayInputProperties.valueAt(i); - pw.println("pointerAcceleration: " + properties.pointerAcceleration); + pw.println("mousePointerAccelerationEnabled: " + + properties.mousePointerAccelerationEnabled); pw.println("pointerIconVisible: " + properties.pointerIconVisible); } pw.decreaseIndent(); @@ -3289,8 +3289,8 @@ public class InputManagerService extends IInputManager.Stub } @Override - public void setPointerAcceleration(float acceleration, int displayId) { - InputManagerService.this.setPointerAcceleration(acceleration, displayId); + public void setMousePointerAccelerationEnabled(boolean enabled, int displayId) { + InputManagerService.this.setMousePointerAccelerationEnabled(enabled, displayId); } @Override @@ -3382,11 +3382,15 @@ public class InputManagerService extends IInputManager.Stub private static class AdditionalDisplayInputProperties { static final boolean DEFAULT_POINTER_ICON_VISIBLE = true; - static final float DEFAULT_POINTER_ACCELERATION = - (float) IInputConstants.DEFAULT_POINTER_ACCELERATION; + static final boolean DEFAULT_MOUSE_POINTER_ACCELERATION_ENABLED = true; - // The pointer acceleration for this display. - public float pointerAcceleration; + /** + * Whether to enable mouse pointer acceleration on this display. Note that this only affects + * pointer movements from mice (that is, pointing devices which send relative motions, + * including trackballs and pointing sticks), not from other pointer devices such as + * touchpads and styluses. + */ + public boolean mousePointerAccelerationEnabled; // Whether the pointer icon should be visible or hidden on this display. public boolean pointerIconVisible; @@ -3396,12 +3400,12 @@ public class InputManagerService extends IInputManager.Stub } public boolean allDefaults() { - return Float.compare(pointerAcceleration, DEFAULT_POINTER_ACCELERATION) == 0 + return mousePointerAccelerationEnabled == DEFAULT_MOUSE_POINTER_ACCELERATION_ENABLED && pointerIconVisible == DEFAULT_POINTER_ICON_VISIBLE; } public void reset() { - pointerAcceleration = DEFAULT_POINTER_ACCELERATION; + mousePointerAccelerationEnabled = DEFAULT_MOUSE_POINTER_ACCELERATION_ENABLED; pointerIconVisible = DEFAULT_POINTER_ICON_VISIBLE; } } @@ -3433,9 +3437,11 @@ public class InputManagerService extends IInputManager.Stub } } - if (properties.pointerAcceleration != mCurrentDisplayProperties.pointerAcceleration) { - mCurrentDisplayProperties.pointerAcceleration = properties.pointerAcceleration; - mNative.setPointerAcceleration(properties.pointerAcceleration); + if (properties.mousePointerAccelerationEnabled + != mCurrentDisplayProperties.mousePointerAccelerationEnabled) { + mCurrentDisplayProperties.mousePointerAccelerationEnabled = + properties.mousePointerAccelerationEnabled; + mNative.setMousePointerAccelerationEnabled(properties.mousePointerAccelerationEnabled); } } diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java index 829b6607f113..dd9204cd1401 100644 --- a/services/core/java/com/android/server/input/NativeInputManagerService.java +++ b/services/core/java/com/android/server/input/NativeInputManagerService.java @@ -118,7 +118,7 @@ interface NativeInputManagerService { void setPointerSpeed(int speed); - void setPointerAcceleration(float acceleration); + void setMousePointerAccelerationEnabled(boolean enabled); void setTouchpadPointerSpeed(int speed); @@ -351,7 +351,7 @@ interface NativeInputManagerService { public native void setPointerSpeed(int speed); @Override - public native void setPointerAcceleration(float acceleration); + public native void setMousePointerAccelerationEnabled(boolean enabled); @Override public native void setTouchpadPointerSpeed(int speed); diff --git a/services/core/java/com/android/server/inputmethod/OWNERS b/services/core/java/com/android/server/inputmethod/OWNERS index aa638aa49fb3..e507c6ba40a1 100644 --- a/services/core/java/com/android/server/inputmethod/OWNERS +++ b/services/core/java/com/android/server/inputmethod/OWNERS @@ -6,5 +6,8 @@ tarandeep@google.com fstern@google.com cosminbaies@google.com +# Automotive +kanant@google.com + ogunwale@google.com #{LAST_RESORT_SUGGESTION} jjaggi@google.com #{LAST_RESORT_SUGGESTION} diff --git a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java index c772e08b5f9f..5df0de83b567 100644 --- a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java +++ b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java @@ -22,12 +22,14 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; import android.os.SystemClock; import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; import android.util.Log; import com.android.internal.telephony.TelephonyIntents; +import com.android.internal.telephony.flags.Flags; import com.android.server.FgThread; import java.util.Objects; @@ -104,10 +106,26 @@ public class SystemEmergencyHelper extends EmergencyHelper { boolean isInExtensionTime = mEmergencyCallEndRealtimeMs != Long.MIN_VALUE && (SystemClock.elapsedRealtime() - mEmergencyCallEndRealtimeMs) < extensionTimeMs; - return mIsInEmergencyCall - || isInExtensionTime - || mTelephonyManager.getEmergencyCallbackMode() - || mTelephonyManager.isInEmergencySmsMode(); + if (!Flags.enforceTelephonyFeatureMapping()) { + return mIsInEmergencyCall + || isInExtensionTime + || mTelephonyManager.getEmergencyCallbackMode() + || mTelephonyManager.isInEmergencySmsMode(); + } else { + boolean emergencyCallbackMode = false; + boolean emergencySmsMode = false; + PackageManager pm = mContext.getPackageManager(); + if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING)) { + emergencyCallbackMode = mTelephonyManager.getEmergencyCallbackMode(); + } + if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)) { + emergencySmsMode = mTelephonyManager.isInEmergencySmsMode(); + } + return mIsInEmergencyCall + || isInExtensionTime + || emergencyCallbackMode + || emergencySmsMode; + } } private class EmergencyCallTelephonyCallback extends TelephonyCallback implements diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 0c2eee5aebbe..ad090829a2f6 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -2118,11 +2118,10 @@ public class LockSettingsService extends ILockSettings.Stub { Slogf.d(TAG, "CE storage for user %d is already unlocked", userId); return; } - final UserInfo userInfo = mUserManager.getUserInfo(userId); final String userType = isUserSecure(userId) ? "secured" : "unsecured"; final byte[] secret = sp.deriveFileBasedEncryptionKey(); try { - mStorageManager.unlockCeStorage(userId, userInfo.serialNumber, secret); + mStorageManager.unlockCeStorage(userId, secret); Slogf.i(TAG, "Unlocked CE storage for %s user %d", userType, userId); } catch (RemoteException e) { Slogf.wtf(TAG, e, "Failed to unlock CE storage for %s user %d", userType, userId); diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java index 6deda468f9a2..f6571d94d554 100644 --- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java +++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java @@ -653,14 +653,14 @@ public final class MediaProjectionManagerService extends SystemService } @Override // Binder call - public boolean hasProjectionPermission(int uid, String packageName) { + public boolean hasProjectionPermission(int processUid, String packageName) { final long token = Binder.clearCallingIdentity(); boolean hasPermission = false; try { hasPermission |= checkPermission(packageName, android.Manifest.permission.CAPTURE_VIDEO_OUTPUT) || mAppOps.noteOpNoThrow( - AppOpsManager.OP_PROJECT_MEDIA, uid, packageName) + AppOpsManager.OP_PROJECT_MEDIA, processUid, packageName) == AppOpsManager.MODE_ALLOWED; } finally { Binder.restoreCallingIdentity(token); @@ -669,7 +669,7 @@ public final class MediaProjectionManagerService extends SystemService } @Override // Binder call - public IMediaProjection createProjection(int uid, String packageName, int type, + public IMediaProjection createProjection(int processUid, String packageName, int type, boolean isPermanentGrant) { if (mContext.checkCallingPermission(MANAGE_MEDIA_PROJECTION) != PackageManager.PERMISSION_GRANTED) { @@ -680,13 +680,13 @@ public final class MediaProjectionManagerService extends SystemService throw new IllegalArgumentException("package name must not be empty"); } final UserHandle callingUser = Binder.getCallingUserHandle(); - return createProjectionInternal(uid, packageName, type, isPermanentGrant, + return createProjectionInternal(processUid, packageName, type, isPermanentGrant, callingUser); } @Override // Binder call @EnforcePermission(MANAGE_MEDIA_PROJECTION) - public IMediaProjection getProjection(int uid, String packageName) { + public IMediaProjection getProjection(int processUid, String packageName) { getProjection_enforcePermission(); if (packageName == null || packageName.isEmpty()) { throw new IllegalArgumentException("package name must not be empty"); @@ -695,7 +695,7 @@ public final class MediaProjectionManagerService extends SystemService MediaProjection projection; final long callingToken = Binder.clearCallingIdentity(); try { - projection = getProjectionInternal(uid, packageName); + projection = getProjectionInternal(processUid, packageName); } finally { Binder.restoreCallingIdentity(callingToken); } @@ -869,12 +869,13 @@ public final class MediaProjectionManagerService extends SystemService @Override // Binder call @EnforcePermission(MANAGE_MEDIA_PROJECTION) - public void notifyPermissionRequestInitiated(int hostUid, int sessionCreationSource) { + public void notifyPermissionRequestInitiated( + int hostProcessUid, int sessionCreationSource) { notifyPermissionRequestInitiated_enforcePermission(); final long token = Binder.clearCallingIdentity(); try { MediaProjectionManagerService.this.notifyPermissionRequestInitiated( - hostUid, sessionCreationSource); + hostProcessUid, sessionCreationSource); } finally { Binder.restoreCallingIdentity(token); } @@ -882,11 +883,11 @@ public final class MediaProjectionManagerService extends SystemService @Override // Binder call @EnforcePermission(MANAGE_MEDIA_PROJECTION) - public void notifyPermissionRequestDisplayed(int hostUid) { + public void notifyPermissionRequestDisplayed(int hostProcessUid) { notifyPermissionRequestDisplayed_enforcePermission(); final long token = Binder.clearCallingIdentity(); try { - MediaProjectionManagerService.this.notifyPermissionRequestDisplayed(hostUid); + MediaProjectionManagerService.this.notifyPermissionRequestDisplayed(hostProcessUid); } finally { Binder.restoreCallingIdentity(token); } @@ -894,11 +895,11 @@ public final class MediaProjectionManagerService extends SystemService @Override // Binder call @EnforcePermission(MANAGE_MEDIA_PROJECTION) - public void notifyPermissionRequestCancelled(int hostUid) { + public void notifyPermissionRequestCancelled(int hostProcessUid) { notifyPermissionRequestCancelled_enforcePermission(); final long token = Binder.clearCallingIdentity(); try { - MediaProjectionManagerService.this.notifyPermissionRequestCancelled(hostUid); + MediaProjectionManagerService.this.notifyPermissionRequestCancelled(hostProcessUid); } finally { Binder.restoreCallingIdentity(token); } @@ -906,11 +907,11 @@ public final class MediaProjectionManagerService extends SystemService @Override // Binder call @EnforcePermission(MANAGE_MEDIA_PROJECTION) - public void notifyAppSelectorDisplayed(int hostUid) { + public void notifyAppSelectorDisplayed(int hostProcessUid) { notifyAppSelectorDisplayed_enforcePermission(); final long token = Binder.clearCallingIdentity(); try { - MediaProjectionManagerService.this.notifyAppSelectorDisplayed(hostUid); + MediaProjectionManagerService.this.notifyAppSelectorDisplayed(hostProcessUid); } finally { Binder.restoreCallingIdentity(token); } @@ -919,12 +920,12 @@ public final class MediaProjectionManagerService extends SystemService @Override // Binder call @EnforcePermission(MANAGE_MEDIA_PROJECTION) public void notifyWindowingModeChanged( - int contentToRecord, int targetUid, int windowingMode) { + int contentToRecord, int targetProcessUid, int windowingMode) { notifyWindowingModeChanged_enforcePermission(); final long token = Binder.clearCallingIdentity(); try { MediaProjectionManagerService.this.notifyWindowingModeChanged( - contentToRecord, targetUid, windowingMode); + contentToRecord, targetProcessUid, windowingMode); } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/notification/NotificationHistoryManager.java b/services/core/java/com/android/server/notification/NotificationHistoryManager.java index e3880c383632..deb95d8a392d 100644 --- a/services/core/java/com/android/server/notification/NotificationHistoryManager.java +++ b/services/core/java/com/android/server/notification/NotificationHistoryManager.java @@ -108,7 +108,7 @@ public class NotificationHistoryManager { for (int i = 0; i < pendingPackageRemovals.size(); i++) { userHistory.onPackageRemoved(pendingPackageRemovals.get(i)); } - mUserPendingPackageRemovals.put(userId, null); + mUserPendingPackageRemovals.remove(userId); } // delete history if it was disabled when the user was locked @@ -133,7 +133,7 @@ public class NotificationHistoryManager { synchronized (mLock) { // Actual data deletion is handled by other parts of the system (the entire directory is // removed) - we just need clean up our internal state for GC - mUserPendingPackageRemovals.put(userId, null); + mUserPendingPackageRemovals.remove(userId); mHistoryEnabled.put(userId, false); mUserPendingHistoryDisables.put(userId, false); onUserStopped(userId); diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index db64a750678b..911643b1a634 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -453,6 +453,8 @@ public class ZenModeHelper { ZenRule rule = new ZenRule(); populateZenRule(pkg, automaticZenRule, rule, origin, /* isNew= */ true); newConfig.automaticRules.put(rule.id, rule); + maybeReplaceDefaultRule(newConfig, automaticZenRule); + if (setConfigLocked(newConfig, origin, reason, rule.component, true, callingUid)) { return rule.id; } else { @@ -461,6 +463,25 @@ public class ZenModeHelper { } } + private static void maybeReplaceDefaultRule(ZenModeConfig config, AutomaticZenRule addedRule) { + if (!Flags.modesApi()) { + return; + } + if (addedRule.getType() == AutomaticZenRule.TYPE_BEDTIME) { + // Delete a built-in disabled "Sleeping" rule when a BEDTIME rule is added; it may have + // smarter triggers and it will prevent confusion about which one to use. + // Note: we must not verify canManageAutomaticZenRule here, since most likely they + // won't have the same owner (sleeping - system; bedtime - DWB). + ZenRule sleepingRule = config.automaticRules.get( + ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID); + if (sleepingRule != null + && !sleepingRule.enabled + && sleepingRule.canBeUpdatedByApp() /* meaning it's not user-customized */) { + config.automaticRules.remove(ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID); + } + } + } + public boolean updateAutomaticZenRule(String ruleId, AutomaticZenRule automaticZenRule, @ConfigChangeOrigin int origin, String reason, int callingUid) { ZenModeConfig newConfig; @@ -1377,6 +1398,10 @@ public class ZenModeHelper { // reset zen automatic rules to default on restore or upgrade if: // - doesn't already have default rules and // - all previous automatic rules were disabled + // + // Note: we don't need to check to avoid restoring the Sleeping rule if there is a + // TYPE_BEDTIME rule because the config is from an old version and thus by + // definition cannot have a rule with TYPE_BEDTIME (or any other type). config.automaticRules = new ArrayMap<>(); for (ZenRule rule : mDefaultConfig.automaticRules.values()) { config.automaticRules.put(rule.id, rule); diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java b/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java new file mode 100644 index 000000000000..4454601f2254 --- /dev/null +++ b/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static android.os.Process.THREAD_PRIORITY_BACKGROUND; + +import android.annotation.NonNull; +import android.app.BackgroundInstallControlManager; +import android.os.Bundle; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IRemoteCallback; +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.ServiceThread; + +public class BackgroundInstallControlCallbackHelper { + + @VisibleForTesting static final String FLAGGED_PACKAGE_NAME_KEY = "packageName"; + @VisibleForTesting static final String FLAGGED_USER_ID_KEY = "userId"; + private static final String TAG = "BackgroundInstallControlCallbackHelper"; + + private final Handler mHandler; + + BackgroundInstallControlCallbackHelper() { + HandlerThread backgroundThread = + new ServiceThread( + "BackgroundInstallControlCallbackHelperBg", + THREAD_PRIORITY_BACKGROUND, + true); + backgroundThread.start(); + mHandler = new Handler(backgroundThread.getLooper()); + } + + @NonNull @VisibleForTesting + final RemoteCallbackList<IRemoteCallback> mCallbacks = new RemoteCallbackList<>(); + + /** Registers callback that gets invoked upon detection of an MBA + * + * NOTE: The callback is user context agnostic and currently broadcasts to all users of other + * users app installs. This is fine because the API is for SystemServer use only. + */ + public void registerBackgroundInstallCallback(IRemoteCallback callback) { + synchronized (mCallbacks) { + mCallbacks.register(callback, null); + } + } + + /** Unregisters callback */ + public void unregisterBackgroundInstallCallback(IRemoteCallback callback) { + synchronized (mCallbacks) { + mCallbacks.unregister(callback); + } + } + + /** + * Invokes all registered callbacks Callbacks are processed through user provided-threads and + * parameters are passed in via {@link BackgroundInstallControlManager} InstallEvent + */ + public void notifyAllCallbacks(int userId, String packageName) { + Bundle extras = new Bundle(); + extras.putCharSequence(FLAGGED_PACKAGE_NAME_KEY, packageName); + extras.putInt(FLAGGED_USER_ID_KEY, userId); + synchronized (mCallbacks) { + mHandler.post( + () -> + mCallbacks.broadcast( + callback -> { + try { + callback.sendResult(extras); + } catch (RemoteException e) { + Slog.e( + TAG, + "error detected: " + e.getLocalizedMessage(), + e); + } + })); + } + } +} diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java index 7f0aadce3143..3a9dedcf2d7b 100644 --- a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java +++ b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java @@ -16,7 +16,12 @@ package com.android.server.pm; +import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; +import static android.Manifest.permission.QUERY_ALL_PACKAGES; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + import android.annotation.NonNull; +import android.annotation.RequiresPermission; import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManagerInternal; import android.content.Context; @@ -30,6 +35,7 @@ import android.content.pm.ParceledListSlice; import android.os.Build; import android.os.Environment; import android.os.Handler; +import android.os.IRemoteCallback; import android.os.Looper; import android.os.Message; import android.os.SystemClock; @@ -69,8 +75,10 @@ public class BackgroundInstallControlService extends SystemService { private static final String DISK_FILE_NAME = "states"; private static final String DISK_DIR_NAME = "bic"; - private static final int MAX_FOREGROUND_TIME_FRAMES_SIZE = 10; + private static final String ENFORCE_PERMISSION_ERROR_MSG = + "User is not permitted to call service: "; + private static final int MAX_FOREGROUND_TIME_FRAMES_SIZE = 10; private static final int MSG_USAGE_EVENT_RECEIVED = 0; private static final int MSG_PACKAGE_ADDED = 1; private static final int MSG_PACKAGE_REMOVED = 2; @@ -78,19 +86,20 @@ public class BackgroundInstallControlService extends SystemService { private final Context mContext; private final BinderService mBinderService; private final PackageManager mPackageManager; + // TODO migrate all internal PackageManager calls to PackageManagerInternal where possible. + // b/310983905 private final PackageManagerInternal mPackageManagerInternal; private final UsageStatsManagerInternal mUsageStatsManagerInternal; private final PermissionManagerServiceInternal mPermissionManager; private final Handler mHandler; private final File mDiskFile; - private SparseSetArray<String> mBackgroundInstalledPackages = null; + private final BackgroundInstallControlCallbackHelper mCallbackHelper; // User ID -> package name -> set of foreground time frame - private final SparseArrayMap<String, - TreeSet<ForegroundTimeFrame>> mInstallerForegroundTimeFrames = - new SparseArrayMap<>(); + private final SparseArrayMap<String, TreeSet<ForegroundTimeFrame>> + mInstallerForegroundTimeFrames = new SparseArrayMap<>(); public BackgroundInstallControlService(@NonNull Context context) { this(new InjectorImpl(context)); @@ -106,13 +115,11 @@ public class BackgroundInstallControlService extends SystemService { mHandler = new EventHandler(injector.getLooper(), this); mDiskFile = injector.getDiskFile(); mUsageStatsManagerInternal = injector.getUsageStatsManagerInternal(); + mCallbackHelper = injector.getBackgroundInstallControlCallbackHelper(); mUsageStatsManagerInternal.registerListener( (userId, event) -> - mHandler.obtainMessage(MSG_USAGE_EVENT_RECEIVED, - userId, - 0, - event).sendToTarget() - ); + mHandler.obtainMessage(MSG_USAGE_EVENT_RECEIVED, userId, 0, event) + .sendToTarget()); mBinderService = new BinderService(this); } @@ -126,12 +133,15 @@ public class BackgroundInstallControlService extends SystemService { @Override public ParceledListSlice<PackageInfo> getBackgroundInstalledPackages( @PackageManager.PackageInfoFlagsBits long flags, int userId) { + mService.enforceCallerQueryPackagesPermissions(); if (!Build.IS_DEBUGGABLE) { return mService.getBackgroundInstalledPackages(flags, userId); } // The debug.transparency.bg-install-apps (only works for debuggable builds) // is used to set mock list of background installed apps for testing. // The list of apps' names is delimited by ",". + // TODO: Remove after migrating test to new background install method using + // {@link BackgroundInstallControlCallbackHelperTest}.installPackage b/310983905 String propertyString = SystemProperties.get("debug.transparency.bg-install-apps"); if (TextUtils.isEmpty(propertyString)) { return mService.getBackgroundInstalledPackages(flags, userId); @@ -139,16 +149,41 @@ public class BackgroundInstallControlService extends SystemService { return mService.getMockBackgroundInstalledPackages(propertyString); } } + + @Override + public void registerBackgroundInstallCallback(IRemoteCallback callback) { + mService.enforceCallerQueryPackagesPermissions(); + mService.enforceCallerInteractCrossUserPermissions(); + mService.mCallbackHelper.registerBackgroundInstallCallback(callback); + } + + @Override + public void unregisterBackgroundInstallCallback(IRemoteCallback callback) { + mService.enforceCallerQueryPackagesPermissions(); + mService.enforceCallerInteractCrossUserPermissions(); + mService.mCallbackHelper.unregisterBackgroundInstallCallback(callback); + } + } + + @RequiresPermission(QUERY_ALL_PACKAGES) + void enforceCallerQueryPackagesPermissions() throws SecurityException { + mContext.enforceCallingPermission(QUERY_ALL_PACKAGES, + ENFORCE_PERMISSION_ERROR_MSG + QUERY_ALL_PACKAGES); + } + + @RequiresPermission(INTERACT_ACROSS_USERS_FULL) + void enforceCallerInteractCrossUserPermissions() throws SecurityException { + mContext.enforceCallingPermission(INTERACT_ACROSS_USERS_FULL, + ENFORCE_PERMISSION_ERROR_MSG + INTERACT_ACROSS_USERS_FULL); } @VisibleForTesting ParceledListSlice<PackageInfo> getBackgroundInstalledPackages( @PackageManager.PackageInfoFlagsBits long flags, int userId) { List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser( - PackageManager.PackageInfoFlags.of(flags), userId); + PackageManager.PackageInfoFlags.of(flags), userId); initBackgroundInstalledPackages(); - ListIterator<PackageInfo> iter = packages.listIterator(); while (iter.hasNext()) { String packageName = iter.next().packageName; @@ -170,8 +205,9 @@ public class BackgroundInstallControlService extends SystemService { List<PackageInfo> mockPackages = new ArrayList<>(); for (String name : mockPackageNames) { try { - PackageInfo packageInfo = mPackageManager.getPackageInfo(name, - PackageManager.PackageInfoFlags.of(PackageManager.MATCH_ALL)); + PackageInfo packageInfo = + mPackageManager.getPackageInfo( + name, PackageManager.PackageInfoFlags.of(PackageManager.MATCH_ALL)); mockPackages.add(packageInfo); } catch (PackageManager.NameNotFoundException e) { Slog.w(TAG, "Package's PackageInfo not found " + name); @@ -192,18 +228,16 @@ public class BackgroundInstallControlService extends SystemService { @Override public void handleMessage(Message msg) { switch (msg.what) { - case MSG_USAGE_EVENT_RECEIVED: { - mService.handleUsageEvent((UsageEvents.Event) msg.obj, msg.arg1 /* userId */); + case MSG_USAGE_EVENT_RECEIVED: + mService.handleUsageEvent( + (UsageEvents.Event) msg.obj, msg.arg1 /* userId */); break; - } - case MSG_PACKAGE_ADDED: { + case MSG_PACKAGE_ADDED: mService.handlePackageAdd((String) msg.obj, msg.arg1 /* userId */); break; - } - case MSG_PACKAGE_REMOVED: { + case MSG_PACKAGE_REMOVED: mService.handlePackageRemove((String) msg.obj, msg.arg1 /* userId */); break; - } default: Slog.w(TAG, "Unknown message: " + msg.what); } @@ -213,8 +247,9 @@ public class BackgroundInstallControlService extends SystemService { void handlePackageAdd(String packageName, int userId) { ApplicationInfo appInfo = null; try { - appInfo = mPackageManager.getApplicationInfoAsUser(packageName, - PackageManager.ApplicationInfoFlags.of(0), userId); + appInfo = + mPackageManager.getApplicationInfoAsUser( + packageName, PackageManager.ApplicationInfoFlags.of(0), userId); } catch (PackageManager.NameNotFoundException e) { Slog.w(TAG, "Package's appInfo not found " + packageName); return; @@ -233,15 +268,18 @@ public class BackgroundInstallControlService extends SystemService { // the installers without INSTALL_PACKAGES perm can't perform // the installation in background. So we can just filter out them. - if (mPermissionManager.checkPermission(installerPackageName, - android.Manifest.permission.INSTALL_PACKAGES, Context.DEVICE_ID_DEFAULT, - userId) != PackageManager.PERMISSION_GRANTED) { + if (mPermissionManager.checkPermission( + installerPackageName, + android.Manifest.permission.INSTALL_PACKAGES, + Context.DEVICE_ID_DEFAULT, + userId) + != PERMISSION_GRANTED) { return; } // convert up-time to current time. - final long installTimestamp = System.currentTimeMillis() - - (SystemClock.uptimeMillis() - appInfo.createTimestamp); + final long installTimestamp = + System.currentTimeMillis() - (SystemClock.uptimeMillis() - appInfo.createTimestamp); if (installedByAdb(initiatingPackageName) || wasForegroundInstallation(installerPackageName, userId, installTimestamp)) { @@ -250,6 +288,7 @@ public class BackgroundInstallControlService extends SystemService { initBackgroundInstalledPackages(); mBackgroundInstalledPackages.add(userId, packageName); + mCallbackHelper.notifyAllCallbacks(userId, packageName); writeBackgroundInstalledPackagesToDisk(); } @@ -259,8 +298,8 @@ public class BackgroundInstallControlService extends SystemService { return PackageManagerServiceUtils.isInstalledByAdb(initiatingPackageName); } - private boolean wasForegroundInstallation(String installerPackageName, - int userId, long installTimestamp) { + private boolean wasForegroundInstallation( + String installerPackageName, int userId, long installTimestamp) { TreeSet<BackgroundInstallControlService.ForegroundTimeFrame> foregroundTimeFrames = mInstallerForegroundTimeFrames.get(userId, installerPackageName); @@ -349,12 +388,12 @@ public class BackgroundInstallControlService extends SystemService { for (int i = 0; i < mBackgroundInstalledPackages.size(); i++) { int userId = mBackgroundInstalledPackages.keyAt(i); for (String packageName : mBackgroundInstalledPackages.get(userId)) { - long token = protoOutputStream.start( - BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); + long token = + protoOutputStream.start( + BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); protoOutputStream.write( BackgroundInstalledPackageProto.PACKAGE_NAME, packageName); - protoOutputStream.write( - BackgroundInstalledPackageProto.USER_ID, userId + 1); + protoOutputStream.write(BackgroundInstalledPackageProto.USER_ID, userId + 1); protoOutputStream.end(token); } } @@ -387,23 +426,28 @@ public class BackgroundInstallControlService extends SystemService { != (int) BackgroundInstalledPackagesProto.BG_INSTALLED_PKG) { continue; } - long token = protoInputStream.start( - BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); + long token = + protoInputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); String packageName = null; int userId = UserHandle.USER_NULL; while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { switch (protoInputStream.getFieldNumber()) { case (int) BackgroundInstalledPackageProto.PACKAGE_NAME: - packageName = protoInputStream.readString( - BackgroundInstalledPackageProto.PACKAGE_NAME); + packageName = + protoInputStream.readString( + BackgroundInstalledPackageProto.PACKAGE_NAME); break; case (int) BackgroundInstalledPackageProto.USER_ID: - userId = protoInputStream.readInt( - BackgroundInstalledPackageProto.USER_ID) - 1; + userId = + protoInputStream.readInt( + BackgroundInstalledPackageProto.USER_ID) + - 1; break; default: - Slog.w(TAG, "Undefined field in proto: " - + protoInputStream.getFieldNumber()); + Slog.w( + TAG, + "Undefined field in proto: " + + protoInputStream.getFieldNumber()); } } protoInputStream.end(token); @@ -432,9 +476,12 @@ public class BackgroundInstallControlService extends SystemService { if (mInstallerForegroundTimeFrames.contains(userId, pkgName)) { return true; } - return mPermissionManager.checkPermission(pkgName, - android.Manifest.permission.INSTALL_PACKAGES, Context.DEVICE_ID_DEFAULT, - userId) == PackageManager.PERMISSION_GRANTED; + return mPermissionManager.checkPermission( + pkgName, + android.Manifest.permission.INSTALL_PACKAGES, + Context.DEVICE_ID_DEFAULT, + userId) + == PERMISSION_GRANTED; } @Override @@ -448,21 +495,22 @@ public class BackgroundInstallControlService extends SystemService { publishBinderService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE, mBinderService); } - mPackageManagerInternal.getPackageList(new PackageManagerInternal.PackageListObserver() { - @Override - public void onPackageAdded(String packageName, int uid) { - final int userId = UserHandle.getUserId(uid); - mHandler.obtainMessage(MSG_PACKAGE_ADDED, - userId, 0, packageName).sendToTarget(); - } + mPackageManagerInternal.getPackageList( + new PackageManagerInternal.PackageListObserver() { + @Override + public void onPackageAdded(String packageName, int uid) { + final int userId = UserHandle.getUserId(uid); + mHandler.obtainMessage(MSG_PACKAGE_ADDED, userId, 0, packageName) + .sendToTarget(); + } - @Override - public void onPackageRemoved(String packageName, int uid) { - final int userId = UserHandle.getUserId(uid); - mHandler.obtainMessage(MSG_PACKAGE_REMOVED, - userId, 0, packageName).sendToTarget(); - } - }); + @Override + public void onPackageRemoved(String packageName, int uid) { + final int userId = UserHandle.getUserId(uid); + mHandler.obtainMessage(MSG_PACKAGE_REMOVED, userId, 0, packageName) + .sendToTarget(); + } + }); } // The foreground time frame (ForegroundTimeFrame) represents the period @@ -518,7 +566,7 @@ public class BackgroundInstallControlService extends SystemService { } /** - * Dependency injector for {@link #BackgroundInstallControlService)}. + * Dependency injector for {@link BackgroundInstallControlService}. */ interface Injector { Context getContext(); @@ -534,6 +582,8 @@ public class BackgroundInstallControlService extends SystemService { Looper getLooper(); File getDiskFile(); + + BackgroundInstallControlCallbackHelper getBackgroundInstallControlCallbackHelper(); } private static final class InjectorImpl implements Injector { @@ -570,11 +620,11 @@ public class BackgroundInstallControlService extends SystemService { @Override public Looper getLooper() { - ServiceThread serviceThread = new ServiceThread(TAG, - android.os.Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */); + ServiceThread serviceThread = + new ServiceThread( + TAG, android.os.Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */); serviceThread.start(); return serviceThread.getLooper(); - } @Override @@ -583,5 +633,10 @@ public class BackgroundInstallControlService extends SystemService { File file = new File(dir, DISK_FILE_NAME); return file; } + + @Override + public BackgroundInstallControlCallbackHelper getBackgroundInstallControlCallbackHelper() { + return new BackgroundInstallControlCallbackHelper(); + } } } diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index dd9541e5dda1..92be4ee3acd9 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -698,7 +698,7 @@ final class InstallPackageHelper { pkgSetting.setUninstallReason(PackageManager.UNINSTALL_REASON_UNKNOWN, userId); pkgSetting.setFirstInstallTime(System.currentTimeMillis(), userId); // Clear any existing archive state. - pkgSetting.setArchiveState(null, userId); + mPm.mInstallerService.mPackageArchiver.clearArchiveState(packageName, userId); mPm.mSettings.writePackageRestrictionsLPr(userId); mPm.mSettings.writeKernelMappingLPr(pkgSetting); installed = true; @@ -2281,7 +2281,7 @@ final class InstallPackageHelper { installerPackageName); } // Clear any existing archive state. - ps.setArchiveState(null, userId); + mPm.mInstallerService.mPackageArchiver.clearArchiveState(pkgName, userId); } else if (allUsers != null) { // The caller explicitly specified INSTALL_ALL_USERS flag. // Thus, updating the settings to install the app for all users. @@ -2305,7 +2305,8 @@ final class InstallPackageHelper { installerPackageName); } // Clear any existing archive state. - ps.setArchiveState(null, currentUserId); + mPm.mInstallerService.mPackageArchiver.clearArchiveState(pkgName, + currentUserId); } else { ps.setInstalled(false, currentUserId); } diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java index a4af5e71000c..3e5759a88213 100644 --- a/services/core/java/com/android/server/pm/PackageArchiver.java +++ b/services/core/java/com/android/server/pm/PackageArchiver.java @@ -71,6 +71,7 @@ import android.graphics.drawable.LayerDrawable; import android.os.Binder; import android.os.Bundle; import android.os.Environment; +import android.os.FileUtils; import android.os.IBinder; import android.os.ParcelableException; import android.os.Process; @@ -309,6 +310,26 @@ public class PackageArchiver { return false; } + void clearArchiveState(String packageName, int userId) { + synchronized (mPm.mLock) { + PackageSetting ps = mPm.mSettings.getPackageLPr(packageName); + if (ps != null) { + ps.setArchiveState(/* archiveState= */ null, userId); + } + } + mPm.mBackgroundHandler.post( + () -> { + File iconsDir = getIconsDir(packageName, userId); + if (!iconsDir.exists()) { + return; + } + // TODO(b/319238030) Move this into installd. + if (!FileUtils.deleteContentsAndDir(iconsDir)) { + Slog.e(TAG, "Failed to clean up archive files for " + packageName); + } + }); + } + @Nullable private String getCurrentLauncherPackageName(int userId) { ComponentName defaultLauncherComponent = mPm.snapshotComputer().getDefaultHomeActivity( @@ -437,8 +458,8 @@ public class PackageArchiver { if (mainActivity.iconBitmap == null) { return null; } - File iconsDir = createIconsDir(userId); - File iconFile = new File(iconsDir, packageName + "-" + index + ".png"); + File iconsDir = createIconsDir(packageName, userId); + File iconFile = new File(iconsDir, index + ".png"); try (FileOutputStream out = new FileOutputStream(iconFile)) { out.write(mainActivity.iconBitmap); out.flush(); @@ -454,14 +475,14 @@ public class PackageArchiver { // The app doesn't define an icon. No need to store anything. return null; } - File iconsDir = createIconsDir(userId); - File iconFile = new File(iconsDir, packageName + "-" + index + ".png"); + File iconsDir = createIconsDir(packageName, userId); + File iconFile = new File(iconsDir, index + ".png"); Bitmap icon = drawableToBitmap(mainActivity.getIcon(/* density= */ 0), iconSize); try (FileOutputStream out = new FileOutputStream(iconFile)) { // Note: Quality is ignored for PNGs. if (!icon.compress(Bitmap.CompressFormat.PNG, /* quality= */ 100, out)) { throw new IOException(TextUtils.formatSimple("Failure to store icon file %s", - iconFile.getName())); + iconFile.getAbsolutePath())); } out.flush(); } @@ -793,8 +814,20 @@ public class PackageArchiver { } @VisibleForTesting - Bitmap decodeIcon(ArchiveActivityInfo archiveActivityInfo) { - return BitmapFactory.decodeFile(archiveActivityInfo.getIconBitmap().toString()); + @Nullable + Bitmap decodeIcon(ArchiveActivityInfo activityInfo) { + Path iconBitmap = activityInfo.getIconBitmap(); + if (iconBitmap == null) { + return null; + } + Bitmap bitmap = BitmapFactory.decodeFile(iconBitmap.toString()); + // TODO(b/278553670) We should throw here after some time. Failing graciously now because + // we've just changed the place where we store icons. + if (bitmap == null) { + Slog.e(TAG, "Archived icon cannot be decoded " + iconBitmap.toAbsolutePath()); + return null; + } + return bitmap; } Bitmap includeCloudOverlay(Bitmap bitmap) { @@ -1075,8 +1108,9 @@ public class PackageArchiver { } } - private static File createIconsDir(@UserIdInt int userId) throws IOException { - File iconsDir = getIconsDir(userId); + private static File createIconsDir(String packageName, @UserIdInt int userId) + throws IOException { + File iconsDir = getIconsDir(packageName, userId); if (!iconsDir.isDirectory()) { iconsDir.delete(); iconsDir.mkdirs(); @@ -1088,8 +1122,10 @@ public class PackageArchiver { return iconsDir; } - private static File getIconsDir(int userId) { - return new File(Environment.getDataSystemCeDirectory(userId), ARCHIVE_ICONS_DIR); + private static File getIconsDir(String packageName, int userId) { + return new File( + new File(Environment.getDataSystemCeDirectory(userId), ARCHIVE_ICONS_DIR), + packageName); } private static byte[] bytesFromBitmapFile(Path path) throws IOException { @@ -1195,7 +1231,7 @@ public class PackageArchiver { UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle.class); if (extraIntent != null && user != null && mAppStateHelper.isAppTopVisible( - getCurrentLauncherPackageName(user.getIdentifier()))) { + getCurrentLauncherPackageName(user.getIdentifier()))) { extraIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivityAsUser(extraIntent, user); } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index a9118d46b4ba..fdcd28b0ed02 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -25,6 +25,7 @@ import static android.content.pm.PackageInstaller.UNARCHIVAL_ERROR_NO_CONNECTIVI import static android.content.pm.PackageInstaller.UNARCHIVAL_ERROR_USER_ACTION_NEEDED; import static android.content.pm.PackageInstaller.UNARCHIVAL_GENERIC_ERROR; import static android.content.pm.PackageInstaller.UNARCHIVAL_OK; +import static android.content.pm.PackageManager.DELETE_ARCHIVE; import static android.content.pm.PackageManager.INSTALL_UNARCHIVE_DRAFT; import static android.os.Process.INVALID_UID; import static android.os.Process.SYSTEM_UID; @@ -1405,7 +1406,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext, statusReceiver, versionedPackage.getPackageName(), - canSilentlyInstallPackage, userId); + canSilentlyInstallPackage, userId, mPackageArchiver, flags); final boolean shouldShowConfirmationDialog = (flags & PackageManager.DELETE_SHOW_DIALOG) != 0; if (!shouldShowConfirmationDialog @@ -1759,7 +1760,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements binderUid, unarchiveId)); } - session.reportUnarchivalStatus(unarchiveId, status, requiredStorageBytes, + session.reportUnarchivalStatus(status, unarchiveId, requiredStorageBytes, userActionIntent); } } @@ -1828,9 +1829,23 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements private final IntentSender mTarget; private final String mPackageName; private final Notification mNotification; + private final int mUserId; - public PackageDeleteObserverAdapter(Context context, IntentSender target, + @DeleteFlags + private final int mFlags; + + @Nullable + private final PackageArchiver mPackageArchiver; + + PackageDeleteObserverAdapter(Context context, IntentSender target, String packageName, boolean showNotification, int userId) { + this(context, target, packageName, showNotification, userId, + /* packageArchiver= */ null, /* flags= */ 0); + } + + PackageDeleteObserverAdapter(Context context, IntentSender target, + String packageName, boolean showNotification, int userId, + PackageArchiver packageArchiver, @DeleteFlags int flags) { mContext = context; mTarget = target; mPackageName = packageName; @@ -1842,6 +1857,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } else { mNotification = null; } + mUserId = userId; + mPackageArchiver = packageArchiver; + mFlags = flags; } private String getDeviceOwnerDeletedPackageMsg() { @@ -1883,6 +1901,11 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements SystemMessage.NOTE_PACKAGE_STATE, mNotification); } + if (mPackageArchiver != null + && PackageManager.DELETE_SUCCEEDED != returnCode + && (mFlags & DELETE_ARCHIVE) != 0) { + mPackageArchiver.clearArchiveState(mPackageName, mUserId); + } if (mTarget == null) { return; } diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java index 7d87d1b27da0..cef3244c9068 100644 --- a/services/core/java/com/android/server/pm/StorageEventHelper.java +++ b/services/core/java/com/android/server/pm/StorageEventHelper.java @@ -193,7 +193,7 @@ public final class StorageEventHelper extends StorageEventListener { } try { - sm.prepareUserStorage(volumeUuid, user.id, user.serialNumber, flags); + sm.prepareUserStorage(volumeUuid, user.id, flags); synchronized (mPm.mInstallLock) { appDataHelper.reconcileAppsDataLI(volumeUuid, user.id, flags, true /* migrateAppData */); diff --git a/services/core/java/com/android/server/pm/UserDataPreparer.java b/services/core/java/com/android/server/pm/UserDataPreparer.java index 8adb5661ad1d..4c42c2dd0850 100644 --- a/services/core/java/com/android/server/pm/UserDataPreparer.java +++ b/services/core/java/com/android/server/pm/UserDataPreparer.java @@ -92,7 +92,7 @@ class UserDataPreparer { volumeUuid, userId, flags, isNewUser); try { // Prepare CE and/or DE storage. - storage.prepareUserStorage(volumeUuid, userId, userSerial, flags); + storage.prepareUserStorage(volumeUuid, userId, flags); // Ensure that the data directories of a removed user with the same ID are not being // reused. New users must get fresh data directories, to avoid leaking data. diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 75b453184db8..c1b74898e5ae 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -2337,8 +2337,18 @@ public class UserManagerService extends IUserManager.Stub { final long identity = Binder.clearCallingIdentity(); try { final TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class); - if (telecomManager != null && telecomManager.isInCall()) { - flags |= UserManager.SWITCHABILITY_STATUS_USER_IN_CALL; + if (com.android.internal.telephony.flags + .Flags.enforceTelephonyFeatureMappingForPublicApis()) { + if (mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_TELECOM)) { + if (telecomManager != null && telecomManager.isInCall()) { + flags |= UserManager.SWITCHABILITY_STATUS_USER_IN_CALL; + } + } + } else { + if (telecomManager != null && telecomManager.isInCall()) { + flags |= UserManager.SWITCHABILITY_STATUS_USER_IN_CALL; + } } } finally { Binder.restoreCallingIdentity(identity); @@ -5136,7 +5146,7 @@ public class UserManagerService extends IUserManager.Stub { t.traceBegin("createUserStorageKeys"); final StorageManager storage = mContext.getSystemService(StorageManager.class); - storage.createUserStorageKeys(userId, userInfo.serialNumber, userInfo.isEphemeral()); + storage.createUserStorageKeys(userId, userInfo.isEphemeral()); t.traceEnd(); // Only prepare DE storage here. CE storage will be prepared later, when the user is diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java index 03f376358b77..09b19e6196a1 100644 --- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java +++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java @@ -190,15 +190,11 @@ public class BatteryStatsImpl extends BatteryStats { // The maximum number of names wakelocks we will keep track of // per uid; once the limit is reached, we batch the remaining wakelocks // in to one common name. - private static final int MAX_WAKELOCKS_PER_UID; + private static final int MAX_WAKELOCKS_PER_UID = isLowRamDevice() ? 40 : 200; - static { - if (ActivityManager.isLowRamDeviceStatic()) { - MAX_WAKELOCKS_PER_UID = 40; - } else { - MAX_WAKELOCKS_PER_UID = 200; - } - } + private static final int CELL_SIGNAL_STRENGTH_LEVEL_COUNT = getCellSignalStrengthLevelCount(); + + private static final int MODEM_TX_POWER_LEVEL_COUNT = getModemTxPowerLevelCount(); // Number of transmit power states the Wifi controller can be in. private static final int NUM_WIFI_TX_LEVELS = 1; @@ -278,11 +274,9 @@ public class BatteryStatsImpl extends BatteryStats { @VisibleForTesting protected KernelSingleUidTimeReader mKernelSingleUidTimeReader; @VisibleForTesting - protected SystemServerCpuThreadReader mSystemServerCpuThreadReader = - SystemServerCpuThreadReader.create(); + protected SystemServerCpuThreadReader mSystemServerCpuThreadReader; - private final KernelMemoryBandwidthStats mKernelMemoryBandwidthStats - = new KernelMemoryBandwidthStats(); + private KernelMemoryBandwidthStats mKernelMemoryBandwidthStats; private final LongSparseArray<SamplingTimer> mKernelMemoryStats = new LongSparseArray<>(); private int[] mCpuPowerBracketMap; private final CpuPowerStatsCollector mCpuPowerStatsCollector; @@ -323,7 +317,7 @@ public class BatteryStatsImpl extends BatteryStats { private long mLastRpmStatsUpdateTimeMs = -RPM_STATS_UPDATE_FREQ_MS; /** Container for Rail Energy Data stats. */ - private final RailStats mTmpRailStats = new RailStats(); + private RailStats mTmpRailStats; /** * Estimate UID modem power usage based on their estimated mobile radio active time. @@ -1031,7 +1025,7 @@ public class BatteryStatsImpl extends BatteryStats { int mPhoneSignalStrengthBin = -1; int mPhoneSignalStrengthBinRaw = -1; final StopwatchTimer[] mPhoneSignalStrengthsTimer = - new StopwatchTimer[CellSignalStrength.getNumSignalStrengthLevels()]; + new StopwatchTimer[CELL_SIGNAL_STRENGTH_LEVEL_COUNT]; StopwatchTimer mPhoneSignalScanningTimer; @@ -1723,7 +1717,8 @@ public class BatteryStatsImpl extends BatteryStats { @VisibleForTesting public BatteryStatsImpl(Clock clock, File historyDirectory, @NonNull Handler handler, @NonNull PowerStatsUidResolver powerStatsUidResolver) { - init(clock); + mClock = clock; + initKernelStatsReaders(); mBatteryStatsConfig = new BatteryStatsConfig.Builder().build(); mHandler = handler; mPowerStatsUidResolver = powerStatsUidResolver; @@ -1748,13 +1743,19 @@ public class BatteryStatsImpl extends BatteryStats { mCpuPowerStatsCollector = null; } - private void init(Clock clock) { - mClock = clock; - mCpuUidUserSysTimeReader = new KernelCpuUidUserSysTimeReader(true, clock); - mCpuUidFreqTimeReader = new KernelCpuUidFreqTimeReader(true, clock); - mCpuUidActiveTimeReader = new KernelCpuUidActiveTimeReader(true, clock); - mCpuUidClusterTimeReader = new KernelCpuUidClusterTimeReader(true, clock); + private void initKernelStatsReaders() { + if (!isKernelStatsAvailable()) { + return; + } + + mCpuUidUserSysTimeReader = new KernelCpuUidUserSysTimeReader(true, mClock); + mCpuUidFreqTimeReader = new KernelCpuUidFreqTimeReader(true, mClock); + mCpuUidActiveTimeReader = new KernelCpuUidActiveTimeReader(true, mClock); + mCpuUidClusterTimeReader = new KernelCpuUidClusterTimeReader(true, mClock); mKernelWakelockReader = new KernelWakelockReader(); + mSystemServerCpuThreadReader = SystemServerCpuThreadReader.create(); + mKernelMemoryBandwidthStats = new KernelMemoryBandwidthStats(); + mTmpRailStats = new RailStats(); } /** @@ -5878,7 +5879,7 @@ public class BatteryStatsImpl extends BatteryStats { @GuardedBy("this") void stopAllPhoneSignalStrengthTimersLocked(int except, long elapsedRealtimeMs) { - for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) { + for (int i = 0; i < CELL_SIGNAL_STRENGTH_LEVEL_COUNT; i++) { if (i == except) { continue; } @@ -8450,7 +8451,7 @@ public class BatteryStatsImpl extends BatteryStats { public ControllerActivityCounterImpl getOrCreateModemControllerActivityLocked() { if (mModemControllerActivity == null) { mModemControllerActivity = new ControllerActivityCounterImpl(mBsi.mClock, - mBsi.mOnBatteryTimeBase, ModemActivityInfo.getNumTxPowerLevels()); + mBsi.mOnBatteryTimeBase, mBsi.MODEM_TX_POWER_LEVEL_COUNT); } return mModemControllerActivity; } @@ -10896,7 +10897,8 @@ public class BatteryStatsImpl extends BatteryStats { @NonNull UserInfoProvider userInfoProvider, @NonNull PowerProfile powerProfile, @NonNull CpuScalingPolicies cpuScalingPolicies, @NonNull PowerStatsUidResolver powerStatsUidResolver) { - init(clock); + mClock = clock; + initKernelStatsReaders(); mBatteryStatsConfig = config; mMonotonicClock = monotonicClock; @@ -10992,7 +10994,7 @@ public class BatteryStatsImpl extends BatteryStats { mDeviceLightIdlingTimer = new StopwatchTimer(mClock, null, -15, null, mOnBatteryTimeBase); mDeviceIdlingTimer = new StopwatchTimer(mClock, null, -12, null, mOnBatteryTimeBase); mPhoneOnTimer = new StopwatchTimer(mClock, null, -3, null, mOnBatteryTimeBase); - for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) { + for (int i = 0; i < CELL_SIGNAL_STRENGTH_LEVEL_COUNT; i++) { mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(mClock, null, -200 - i, null, mOnBatteryTimeBase); } @@ -11012,7 +11014,7 @@ public class BatteryStatsImpl extends BatteryStats { mBluetoothActivity = new ControllerActivityCounterImpl(mClock, mOnBatteryTimeBase, NUM_BT_TX_LEVELS); mModemActivity = new ControllerActivityCounterImpl(mClock, mOnBatteryTimeBase, - ModemActivityInfo.getNumTxPowerLevels()); + MODEM_TX_POWER_LEVEL_COUNT); mMobileRadioActiveTimer = new StopwatchTimer(mClock, null, -400, null, mOnBatteryTimeBase); mMobileRadioActivePerAppTimer = new StopwatchTimer(mClock, null, -401, null, mOnBatteryTimeBase); @@ -11611,7 +11613,7 @@ public class BatteryStatsImpl extends BatteryStats { mFlashlightOnTimer.reset(false, elapsedRealtimeUs); mCameraOnTimer.reset(false, elapsedRealtimeUs); mBluetoothScanTimer.reset(false, elapsedRealtimeUs); - for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) { + for (int i = 0; i < CELL_SIGNAL_STRENGTH_LEVEL_COUNT; i++) { mPhoneSignalStrengthsTimer[i].reset(false, elapsedRealtimeUs); } mPhoneSignalScanningTimer.reset(false, elapsedRealtimeUs); @@ -11834,7 +11836,7 @@ public class BatteryStatsImpl extends BatteryStats { private String[] mWifiIfaces = EmptyArray.STRING; @GuardedBy("mWifiNetworkLock") - private NetworkStats mLastWifiNetworkStats = new NetworkStats(0, -1); + private NetworkStats mLastWifiNetworkStats; private final Object mModemNetworkLock = new Object(); @@ -11842,7 +11844,7 @@ public class BatteryStatsImpl extends BatteryStats { private String[] mModemIfaces = EmptyArray.STRING; @GuardedBy("mModemNetworkLock") - private NetworkStats mLastModemNetworkStats = new NetworkStats(0, -1); + private NetworkStats mLastModemNetworkStats; @VisibleForTesting protected NetworkStats readMobileNetworkStatsLocked( @@ -11875,7 +11877,9 @@ public class BatteryStatsImpl extends BatteryStats { synchronized (mWifiNetworkLock) { final NetworkStats latestStats = readWifiNetworkStatsLocked(networkStatsManager); if (latestStats != null) { - delta = latestStats.subtract(mLastWifiNetworkStats); + delta = mLastWifiNetworkStats != null + ? latestStats.subtract(mLastWifiNetworkStats) + : latestStats.subtract(new NetworkStats(0, -1)); mLastWifiNetworkStats = latestStats; } } @@ -12248,7 +12252,9 @@ public class BatteryStatsImpl extends BatteryStats { synchronized (mModemNetworkLock) { final NetworkStats latestStats = readMobileNetworkStatsLocked(networkStatsManager); if (latestStats != null) { - delta = latestStats.subtract(mLastModemNetworkStats); + delta = latestStats.subtract(mLastModemNetworkStats != null + ? mLastModemNetworkStats + : new NetworkStats(0, -1)); mLastModemNetworkStats = latestStats; } } @@ -12301,7 +12307,7 @@ public class BatteryStatsImpl extends BatteryStats { deltaInfo.getSleepTimeMillis()); mModemActivity.getOrCreateRxTimeCounter() .increment(deltaInfo.getReceiveTimeMillis(), elapsedRealtimeMs); - for (int lvl = 0; lvl < ModemActivityInfo.getNumTxPowerLevels(); lvl++) { + for (int lvl = 0; lvl < MODEM_TX_POWER_LEVEL_COUNT; lvl++) { mModemActivity.getOrCreateTxTimeCounters()[lvl] .increment(deltaInfo.getTransmitDurationMillisAtPowerLevel(lvl), elapsedRealtimeMs); @@ -12318,8 +12324,8 @@ public class BatteryStatsImpl extends BatteryStats { mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE) + deltaInfo.getReceiveTimeMillis() * mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX); - for (int i = 0; i < Math.min(ModemActivityInfo.getNumTxPowerLevels(), - CellSignalStrength.getNumSignalStrengthLevels()); i++) { + for (int i = 0; i < Math.min(MODEM_TX_POWER_LEVEL_COUNT, + CELL_SIGNAL_STRENGTH_LEVEL_COUNT); i++) { energyUsed += deltaInfo.getTransmitDurationMillisAtPowerLevel(i) * mPowerProfile.getAveragePower( PowerProfile.POWER_MODEM_CONTROLLER_TX, i); @@ -12441,7 +12447,7 @@ public class BatteryStatsImpl extends BatteryStats { } if (totalTxPackets > 0 && entry.getTxPackets() > 0) { - for (int lvl = 0; lvl < ModemActivityInfo.getNumTxPowerLevels(); + for (int lvl = 0; lvl < MODEM_TX_POWER_LEVEL_COUNT; lvl++) { long txMs = entry.getTxPackets() * deltaInfo.getTransmitDurationMillisAtPowerLevel(lvl); @@ -12550,7 +12556,7 @@ public class BatteryStatsImpl extends BatteryStats { && deltaInfo.getSpecificInfoFrequencyRange(0) == ServiceState.FREQUENCY_RANGE_UNKNOWN) { // Specific info data unavailable. Proportionally smear Rx and Tx times across each RAT. - final int levelCount = CellSignalStrength.getNumSignalStrengthLevels(); + final int levelCount = CELL_SIGNAL_STRENGTH_LEVEL_COUNT; long[] perSignalStrengthActiveTimeMs = new long[levelCount]; long totalActiveTimeMs = 0; @@ -12726,13 +12732,13 @@ public class BatteryStatsImpl extends BatteryStats { return; } int levelMaxTimeSpent = 0; - for (int i = 1; i < ModemActivityInfo.getNumTxPowerLevels(); i++) { + for (int i = 1; i < MODEM_TX_POWER_LEVEL_COUNT; i++) { if (activityInfo.getTransmitDurationMillisAtPowerLevel(i) > activityInfo.getTransmitDurationMillisAtPowerLevel(levelMaxTimeSpent)) { levelMaxTimeSpent = i; } } - if (levelMaxTimeSpent == ModemActivityInfo.getNumTxPowerLevels() - 1) { + if (levelMaxTimeSpent == MODEM_TX_POWER_LEVEL_COUNT - 1) { mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG); } @@ -14821,12 +14827,12 @@ public class BatteryStatsImpl extends BatteryStats { timeInRatMs[i] = getPhoneDataConnectionTime(i, rawRealTimeUs, which) / 1000; } long[] timeInRxSignalStrengthLevelMs = - new long[CellSignalStrength.getNumSignalStrengthLevels()]; + new long[CELL_SIGNAL_STRENGTH_LEVEL_COUNT]; for (int i = 0; i < timeInRxSignalStrengthLevelMs.length; i++) { timeInRxSignalStrengthLevelMs[i] = getPhoneSignalStrengthTime(i, rawRealTimeUs, which) / 1000; } - long[] txTimeMs = new long[Math.min(ModemActivityInfo.getNumTxPowerLevels(), + long[] txTimeMs = new long[Math.min(MODEM_TX_POWER_LEVEL_COUNT, counter.getTxTimeCounters().length)]; long totalTxTimeMs = 0; for (int i = 0; i < txTimeMs.length; i++) { @@ -15458,7 +15464,7 @@ public class BatteryStatsImpl extends BatteryStats { public Constants(Handler handler) { super(handler); - if (ActivityManager.isLowRamDeviceStatic()) { + if (isLowRamDevice()) { MAX_HISTORY_FILES = DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE; MAX_HISTORY_BUFFER = DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB * 1024; } else { @@ -15528,12 +15534,10 @@ public class BatteryStatsImpl extends BatteryStats { KEY_PROC_STATE_CHANGE_COLLECTION_DELAY_MS, DEFAULT_PROC_STATE_CHANGE_COLLECTION_DELAY_MS); MAX_HISTORY_FILES = mParser.getInt(KEY_MAX_HISTORY_FILES, - ActivityManager.isLowRamDeviceStatic() ? - DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE - : DEFAULT_MAX_HISTORY_FILES); + isLowRamDevice() ? DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE + : DEFAULT_MAX_HISTORY_FILES); MAX_HISTORY_BUFFER = mParser.getInt(KEY_MAX_HISTORY_BUFFER_KB, - ActivityManager.isLowRamDeviceStatic() ? - DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB + isLowRamDevice() ? DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB : DEFAULT_MAX_HISTORY_BUFFER_KB) * 1024; final String perUidModemModel = mParser.getString(KEY_PER_UID_MODEM_POWER_MODEL, @@ -16014,7 +16018,7 @@ public class BatteryStatsImpl extends BatteryStats { mDeviceLightIdlingTimer.readSummaryFromParcelLocked(in); mDeviceIdlingTimer.readSummaryFromParcelLocked(in); mPhoneOnTimer.readSummaryFromParcelLocked(in); - for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) { + for (int i = 0; i < CELL_SIGNAL_STRENGTH_LEVEL_COUNT; i++) { mPhoneSignalStrengthsTimer[i].readSummaryFromParcelLocked(in); } mPhoneSignalScanningTimer.readSummaryFromParcelLocked(in); @@ -16520,7 +16524,7 @@ public class BatteryStatsImpl extends BatteryStats { mDeviceLightIdlingTimer.writeSummaryFromParcelLocked(out, nowRealtime); mDeviceIdlingTimer.writeSummaryFromParcelLocked(out, nowRealtime); mPhoneOnTimer.writeSummaryFromParcelLocked(out, nowRealtime); - for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) { + for (int i = 0; i < CELL_SIGNAL_STRENGTH_LEVEL_COUNT; i++) { mPhoneSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, nowRealtime); } mPhoneSignalScanningTimer.writeSummaryFromParcelLocked(out, nowRealtime); @@ -17015,7 +17019,7 @@ public class BatteryStatsImpl extends BatteryStats { mDeviceIdlingTimer.logState(pr, " "); pr.println("*** Phone timer:"); mPhoneOnTimer.logState(pr, " "); - for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) { + for (int i = 0; i < CELL_SIGNAL_STRENGTH_LEVEL_COUNT; i++) { pr.println("*** Phone signal strength #" + i + ":"); mPhoneSignalStrengthsTimer[i].logState(pr, " "); } diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index 8d934089524c..13f114138261 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -1279,8 +1279,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba ipw.println(); } - PackageWatchdog.getInstance(mContext).dump(ipw); }); + PackageWatchdog.getInstance(mContext).dump(ipw); } @AnyThread diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index 58acbe08acf1..c0e33084f9c4 100755 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -1339,16 +1339,22 @@ class TvInputHardwareManager implements TvInputHal.Callback { String inputId = mHardwareInputIdMap.get(deviceId); if (inputId != null) { - if (connection.updateCableConnectionStatusLocked(cableConnectionStatus)) { - if (previousCableConnectionStatus != connection.getInputStateLocked()) { - mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, - connection.getInputStateLocked(), 0, inputId).sendToTarget(); - } - } else { - if ((previousConfigsLength == 0) - != (connection.getConfigsLengthLocked() == 0)) { - mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, - connection.getInputStateLocked(), 0, inputId).sendToTarget(); + synchronized (mLock) { + if (connection.updateCableConnectionStatusLocked( + cableConnectionStatus)) { + if (previousCableConnectionStatus + != connection.getInputStateLocked()) { + mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, + connection.getInputStateLocked(), 0, inputId) + .sendToTarget(); + } + } else { + if ((previousConfigsLength == 0) + != (connection.getConfigsLengthLocked() == 0)) { + mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, + connection.getInputStateLocked(), 0, inputId) + .sendToTarget(); + } } } } diff --git a/services/core/java/com/android/server/vcn/VcnContext.java b/services/core/java/com/android/server/vcn/VcnContext.java index 9213d96ad4ca..6ce868540070 100644 --- a/services/core/java/com/android/server/vcn/VcnContext.java +++ b/services/core/java/com/android/server/vcn/VcnContext.java @@ -74,6 +74,10 @@ public class VcnContext { return mFeatureFlags; } + public boolean isFlagSafeModeTimeoutConfigEnabled() { + return mFeatureFlags.safeModeTimeoutConfig(); + } + /** * Verifies that the caller is running on the VcnContext Thread. * diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java index 54c97dd37941..fcc0de1c2258 100644 --- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java +++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java @@ -915,9 +915,11 @@ public class VcnGatewayConnection extends StateMachine { // TODO(b/180132994): explore safely removing this Thread check mVcnContext.ensureRunningOnLooperThread(); - logInfo( - "Selected underlying network changed: " - + (underlying == null ? null : underlying.network)); + if (!UnderlyingNetworkRecord.isSameNetwork(mUnderlying, underlying)) { + logInfo( + "Selected underlying network changed: " + + (underlying == null ? null : underlying.network)); + } // TODO(b/179091925): Move the delayed-message handling to BaseState @@ -1242,9 +1244,28 @@ public class VcnGatewayConnection extends StateMachine { createScheduledAlarm( SAFEMODE_TIMEOUT_ALARM, delayedMessage, - mVcnContext.isInTestMode() - ? TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS_TEST_MODE) - : TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS)); + getSafeModeTimeoutMs(mVcnContext, mLastSnapshot, mSubscriptionGroup)); + } + + /** Gets the safe mode timeout */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + public static long getSafeModeTimeoutMs( + VcnContext vcnContext, TelephonySubscriptionSnapshot snapshot, ParcelUuid subGrp) { + final int defaultSeconds = + vcnContext.isInTestMode() + ? SAFEMODE_TIMEOUT_SECONDS_TEST_MODE + : SAFEMODE_TIMEOUT_SECONDS; + + final PersistableBundleWrapper carrierConfig = snapshot.getCarrierConfigForSubGrp(subGrp); + int resultSeconds = defaultSeconds; + + if (vcnContext.isFlagSafeModeTimeoutConfigEnabled() && carrierConfig != null) { + resultSeconds = + carrierConfig.getInt( + VcnManager.VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY, defaultSeconds); + } + + return TimeUnit.SECONDS.toMillis(resultSeconds); } private void cancelSafeModeAlarm() { diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java index 7f129ea3801c..d32e5cc8ef80 100644 --- a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java +++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java @@ -47,7 +47,6 @@ import com.android.server.vcn.VcnContext; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; /** @hide */ @@ -86,7 +85,6 @@ class NetworkPriorityClassifier { * <p>VCN MUST never select a non-INTERNET network that are unvalidated or fail to match any * template as the underlying network. */ - @VisibleForTesting(visibility = Visibility.PRIVATE) static final int PRIORITY_INVALID = -1; /** Gives networks a priority class, based on configured VcnGatewayConnectionConfig */ @@ -96,7 +94,7 @@ class NetworkPriorityClassifier { List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, - UnderlyingNetworkRecord currentlySelected, + boolean isSelected, PersistableBundleWrapper carrierConfig) { // mRouteSelectionNetworkRequest requires a network be both VALIDATED and NOT_SUSPENDED @@ -118,7 +116,7 @@ class NetworkPriorityClassifier { networkRecord, subscriptionGroup, snapshot, - currentlySelected, + isSelected, carrierConfig)) { return priorityIndex; } @@ -140,12 +138,9 @@ class NetworkPriorityClassifier { UnderlyingNetworkRecord networkRecord, ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, - UnderlyingNetworkRecord currentlySelected, + boolean isSelected, PersistableBundleWrapper carrierConfig) { final NetworkCapabilities caps = networkRecord.networkCapabilities; - final boolean isSelectedUnderlyingNetwork = - currentlySelected != null - && Objects.equals(currentlySelected.network, networkRecord.network); final int meteredMatch = networkPriority.getMetered(); final boolean isMetered = !caps.hasCapability(NET_CAPABILITY_NOT_METERED); @@ -159,7 +154,7 @@ class NetworkPriorityClassifier { if (caps.getLinkUpstreamBandwidthKbps() < networkPriority.getMinExitUpstreamBandwidthKbps() || (caps.getLinkUpstreamBandwidthKbps() < networkPriority.getMinEntryUpstreamBandwidthKbps() - && !isSelectedUnderlyingNetwork)) { + && !isSelected)) { return false; } @@ -167,7 +162,7 @@ class NetworkPriorityClassifier { < networkPriority.getMinExitDownstreamBandwidthKbps() || (caps.getLinkDownstreamBandwidthKbps() < networkPriority.getMinEntryDownstreamBandwidthKbps() - && !isSelectedUnderlyingNetwork)) { + && !isSelected)) { return false; } @@ -191,7 +186,7 @@ class NetworkPriorityClassifier { return checkMatchesWifiPriorityRule( (VcnWifiUnderlyingNetworkTemplate) networkPriority, networkRecord, - currentlySelected, + isSelected, carrierConfig); } @@ -214,7 +209,7 @@ class NetworkPriorityClassifier { public static boolean checkMatchesWifiPriorityRule( VcnWifiUnderlyingNetworkTemplate networkPriority, UnderlyingNetworkRecord networkRecord, - UnderlyingNetworkRecord currentlySelected, + boolean isSelected, PersistableBundleWrapper carrierConfig) { final NetworkCapabilities caps = networkRecord.networkCapabilities; @@ -223,7 +218,7 @@ class NetworkPriorityClassifier { } // TODO: Move the Network Quality check to the network metric monitor framework. - if (!isWifiRssiAcceptable(networkRecord, currentlySelected, carrierConfig)) { + if (!isWifiRssiAcceptable(networkRecord, isSelected, carrierConfig)) { return false; } @@ -237,15 +232,11 @@ class NetworkPriorityClassifier { private static boolean isWifiRssiAcceptable( UnderlyingNetworkRecord networkRecord, - UnderlyingNetworkRecord currentlySelected, + boolean isSelected, PersistableBundleWrapper carrierConfig) { final NetworkCapabilities caps = networkRecord.networkCapabilities; - final boolean isSelectedNetwork = - currentlySelected != null - && networkRecord.network.equals(currentlySelected.network); - if (isSelectedNetwork - && caps.getSignalStrength() >= getWifiExitRssiThreshold(carrierConfig)) { + if (isSelected && caps.getSignalStrength() >= getWifiExitRssiThreshold(carrierConfig)) { return true; } diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java index 6afa795e96fa..48df44b7c4ac 100644 --- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java @@ -48,6 +48,7 @@ import android.util.ArraySet; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.internal.util.IndentingPrintWriter; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.VcnContext; @@ -83,6 +84,9 @@ public class UnderlyingNetworkController { @NonNull private final TelephonyCallback mActiveDataSubIdListener = new VcnActiveDataSubscriptionIdListener(); + private final Map<Network, UnderlyingNetworkEvaluator> mUnderlyingNetworkRecords = + new ArrayMap<>(); + @NonNull private final List<NetworkCallback> mCellBringupCallbacks = new ArrayList<>(); @Nullable private NetworkCallback mWifiBringupCallback; @Nullable private NetworkCallback mWifiEntryRssiThresholdCallback; @@ -105,7 +109,8 @@ public class UnderlyingNetworkController { this(vcnContext, connectionConfig, subscriptionGroup, snapshot, cb, new Dependencies()); } - private UnderlyingNetworkController( + @VisibleForTesting(visibility = Visibility.PRIVATE) + UnderlyingNetworkController( @NonNull VcnContext vcnContext, @NonNull VcnGatewayConnectionConfig connectionConfig, @NonNull ParcelUuid subscriptionGroup, @@ -196,6 +201,7 @@ public class UnderlyingNetworkController { NetworkCallback oldWifiExitRssiThresholdCallback = mWifiExitRssiThresholdCallback; List<NetworkCallback> oldCellCallbacks = new ArrayList<>(mCellBringupCallbacks); mCellBringupCallbacks.clear(); + mUnderlyingNetworkRecords.clear(); // Register new callbacks. Make-before-break; always register new callbacks before removal // of old callbacks @@ -395,6 +401,18 @@ public class UnderlyingNetworkController { // Update carrier config mCarrierConfig = mLastSnapshot.getCarrierConfigForSubGrp(mSubscriptionGroup); + // Make sure all evaluators use the same updated TelephonySubscriptionSnapshot and carrier + // config to calculate their cached priority classes. For simplicity, the + // UnderlyingNetworkController does not listen for changes in VCN-related carrier config + // keys, and changes are applied at restart of the VcnGatewayConnection + for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) { + evaluator.reevaluate( + mConnectionConfig.getVcnUnderlyingNetworkPriorities(), + mSubscriptionGroup, + mLastSnapshot, + mCarrierConfig); + } + // Only trigger re-registration if subIds in this group have changed if (oldSnapshot .getAllSubIdsInGroup(mSubscriptionGroup) @@ -418,32 +436,62 @@ public class UnderlyingNetworkController { .unregisterTelephonyCallback(mActiveDataSubIdListener); } + private TreeSet<UnderlyingNetworkEvaluator> getSortedUnderlyingNetworks() { + TreeSet<UnderlyingNetworkEvaluator> sorted = + new TreeSet<>(UnderlyingNetworkEvaluator.getComparator()); + + for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) { + if (evaluator.getPriorityClass() != NetworkPriorityClassifier.PRIORITY_INVALID) { + sorted.add(evaluator); + } + } + + return sorted; + } + private void reevaluateNetworks() { if (mIsQuitting || mRouteSelectionCallback == null) { return; // UnderlyingNetworkController has quit. } - TreeSet<UnderlyingNetworkRecord> sorted = - mRouteSelectionCallback.getSortedUnderlyingNetworks(); - UnderlyingNetworkRecord candidate = sorted.isEmpty() ? null : sorted.first(); + TreeSet<UnderlyingNetworkEvaluator> sorted = getSortedUnderlyingNetworks(); + + UnderlyingNetworkEvaluator candidateEvaluator = sorted.isEmpty() ? null : sorted.first(); + UnderlyingNetworkRecord candidate = + candidateEvaluator == null ? null : candidateEvaluator.getNetworkRecord(); if (Objects.equals(mCurrentRecord, candidate)) { return; } String allNetworkPriorities = ""; - for (UnderlyingNetworkRecord record : sorted) { + for (UnderlyingNetworkEvaluator recordEvaluator : sorted) { if (!allNetworkPriorities.isEmpty()) { allNetworkPriorities += ", "; } - allNetworkPriorities += record.network + ": " + record.priorityClass; + allNetworkPriorities += + recordEvaluator.getNetwork() + ": " + recordEvaluator.getPriorityClass(); } - logInfo( - "Selected network changed to " - + (candidate == null ? null : candidate.network) - + ", selected from list: " - + allNetworkPriorities); + + if (!UnderlyingNetworkRecord.isSameNetwork(mCurrentRecord, candidate)) { + logInfo( + "Selected network changed to " + + (candidate == null ? null : candidate.network) + + ", selected from list: " + + allNetworkPriorities); + } + mCurrentRecord = candidate; mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord); + + // Need to update all evaluators to ensure the previously selected one is unselected + for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) { + evaluator.setIsSelected( + candidateEvaluator == evaluator, + mConnectionConfig.getVcnUnderlyingNetworkPriorities(), + mSubscriptionGroup, + mLastSnapshot, + mCarrierConfig); + } } /** @@ -463,46 +511,26 @@ public class UnderlyingNetworkController { */ @VisibleForTesting class UnderlyingNetworkListener extends NetworkCallback { - private final Map<Network, UnderlyingNetworkRecord.Builder> - mUnderlyingNetworkRecordBuilders = new ArrayMap<>(); - UnderlyingNetworkListener() { super(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO); } - private TreeSet<UnderlyingNetworkRecord> getSortedUnderlyingNetworks() { - TreeSet<UnderlyingNetworkRecord> sorted = - new TreeSet<>(UnderlyingNetworkRecord.getComparator()); - - for (UnderlyingNetworkRecord.Builder builder : - mUnderlyingNetworkRecordBuilders.values()) { - if (builder.isValid()) { - final UnderlyingNetworkRecord record = - builder.build( - mVcnContext, - mConnectionConfig.getVcnUnderlyingNetworkPriorities(), - mSubscriptionGroup, - mLastSnapshot, - mCurrentRecord, - mCarrierConfig); - if (record.priorityClass != NetworkPriorityClassifier.PRIORITY_INVALID) { - sorted.add(record); - } - } - } - - return sorted; - } - @Override public void onAvailable(@NonNull Network network) { - mUnderlyingNetworkRecordBuilders.put( - network, new UnderlyingNetworkRecord.Builder(network)); + mUnderlyingNetworkRecords.put( + network, + mDeps.newUnderlyingNetworkEvaluator( + mVcnContext, + network, + mConnectionConfig.getVcnUnderlyingNetworkPriorities(), + mSubscriptionGroup, + mLastSnapshot, + mCarrierConfig)); } @Override public void onLost(@NonNull Network network) { - mUnderlyingNetworkRecordBuilders.remove(network); + mUnderlyingNetworkRecords.remove(network); reevaluateNetworks(); } @@ -510,15 +538,20 @@ public class UnderlyingNetworkController { @Override public void onCapabilitiesChanged( @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) { - final UnderlyingNetworkRecord.Builder builder = - mUnderlyingNetworkRecordBuilders.get(network); - if (builder == null) { + final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network); + if (evaluator == null) { logWtf("Got capabilities change for unknown key: " + network); return; } - builder.setNetworkCapabilities(networkCapabilities); - if (builder.isValid()) { + evaluator.setNetworkCapabilities( + networkCapabilities, + mConnectionConfig.getVcnUnderlyingNetworkPriorities(), + mSubscriptionGroup, + mLastSnapshot, + mCarrierConfig); + + if (evaluator.isValid()) { reevaluateNetworks(); } } @@ -526,30 +559,40 @@ public class UnderlyingNetworkController { @Override public void onLinkPropertiesChanged( @NonNull Network network, @NonNull LinkProperties linkProperties) { - final UnderlyingNetworkRecord.Builder builder = - mUnderlyingNetworkRecordBuilders.get(network); - if (builder == null) { + final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network); + if (evaluator == null) { logWtf("Got link properties change for unknown key: " + network); return; } - builder.setLinkProperties(linkProperties); - if (builder.isValid()) { + evaluator.setLinkProperties( + linkProperties, + mConnectionConfig.getVcnUnderlyingNetworkPriorities(), + mSubscriptionGroup, + mLastSnapshot, + mCarrierConfig); + + if (evaluator.isValid()) { reevaluateNetworks(); } } @Override public void onBlockedStatusChanged(@NonNull Network network, boolean isBlocked) { - final UnderlyingNetworkRecord.Builder builder = - mUnderlyingNetworkRecordBuilders.get(network); - if (builder == null) { + final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network); + if (evaluator == null) { logWtf("Got blocked status change for unknown key: " + network); return; } - builder.setIsBlocked(isBlocked); - if (builder.isValid()) { + evaluator.setIsBlocked( + isBlocked, + mConnectionConfig.getVcnUnderlyingNetworkPriorities(), + mSubscriptionGroup, + mLastSnapshot, + mCarrierConfig); + + if (evaluator.isValid()) { reevaluateNetworks(); } } @@ -614,16 +657,8 @@ public class UnderlyingNetworkController { pw.println("Underlying networks:"); pw.increaseIndent(); if (mRouteSelectionCallback != null) { - for (UnderlyingNetworkRecord record : - mRouteSelectionCallback.getSortedUnderlyingNetworks()) { - record.dump( - mVcnContext, - pw, - mConnectionConfig.getVcnUnderlyingNetworkPriorities(), - mSubscriptionGroup, - mLastSnapshot, - mCurrentRecord, - mCarrierConfig); + for (UnderlyingNetworkEvaluator recordEvaluator : getSortedUnderlyingNetworks()) { + recordEvaluator.dump(pw); } } pw.decreaseIndent(); @@ -653,5 +688,23 @@ public class UnderlyingNetworkController { @Nullable UnderlyingNetworkRecord underlyingNetworkRecord); } - private static class Dependencies {} + @VisibleForTesting(visibility = Visibility.PRIVATE) + public static class Dependencies { + /** Construct a new UnderlyingNetworkEvaluator */ + public UnderlyingNetworkEvaluator newUnderlyingNetworkEvaluator( + @NonNull VcnContext vcnContext, + @NonNull Network network, + @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot lastSnapshot, + @Nullable PersistableBundleWrapper carrierConfig) { + return new UnderlyingNetworkEvaluator( + vcnContext, + network, + underlyingNetworkTemplates, + subscriptionGroup, + lastSnapshot, + carrierConfig); + } + } } diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java new file mode 100644 index 000000000000..c124a1976ac6 --- /dev/null +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.vcn.routeselection; + +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.vcn.VcnUnderlyingNetworkTemplate; +import android.os.ParcelUuid; + +import com.android.internal.util.IndentingPrintWriter; +import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; +import com.android.server.vcn.VcnContext; + +import java.util.Comparator; +import java.util.List; +import java.util.Objects; + +/** + * UnderlyingNetworkEvaluator evaluates the quality and priority class of a network candidate for + * route selection. + * + * @hide + */ +public class UnderlyingNetworkEvaluator { + private static final String TAG = UnderlyingNetworkEvaluator.class.getSimpleName(); + + @NonNull private final VcnContext mVcnContext; + @NonNull private final UnderlyingNetworkRecord.Builder mNetworkRecordBuilder; + + private boolean mIsSelected; + private int mPriorityClass = NetworkPriorityClassifier.PRIORITY_INVALID; + + public UnderlyingNetworkEvaluator( + @NonNull VcnContext vcnContext, + @NonNull Network network, + @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot lastSnapshot, + @Nullable PersistableBundleWrapper carrierConfig) { + mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext"); + + Objects.requireNonNull(underlyingNetworkTemplates, "Missing underlyingNetworkTemplates"); + Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup"); + Objects.requireNonNull(lastSnapshot, "Missing lastSnapshot"); + + mNetworkRecordBuilder = + new UnderlyingNetworkRecord.Builder( + Objects.requireNonNull(network, "Missing network")); + mIsSelected = false; + + updatePriorityClass( + underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig); + } + + private void updatePriorityClass( + @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot lastSnapshot, + @Nullable PersistableBundleWrapper carrierConfig) { + if (mNetworkRecordBuilder.isValid()) { + mPriorityClass = + NetworkPriorityClassifier.calculatePriorityClass( + mVcnContext, + mNetworkRecordBuilder.build(), + underlyingNetworkTemplates, + subscriptionGroup, + lastSnapshot, + mIsSelected, + carrierConfig); + } else { + mPriorityClass = NetworkPriorityClassifier.PRIORITY_INVALID; + } + } + + public static Comparator<UnderlyingNetworkEvaluator> getComparator() { + return (left, right) -> { + final int leftIndex = left.mPriorityClass; + final int rightIndex = right.mPriorityClass; + + // In the case of networks in the same priority class, prioritize based on other + // criteria (eg. actively selected network, link metrics, etc) + if (leftIndex == rightIndex) { + // TODO: Improve the strategy of network selection when both UnderlyingNetworkRecord + // fall into the same priority class. + if (left.mIsSelected) { + return -1; + } + if (right.mIsSelected) { + return 1; + } + } + return Integer.compare(leftIndex, rightIndex); + }; + } + + /** Set the NetworkCapabilities */ + public void setNetworkCapabilities( + @NonNull NetworkCapabilities nc, + @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot lastSnapshot, + @Nullable PersistableBundleWrapper carrierConfig) { + mNetworkRecordBuilder.setNetworkCapabilities(nc); + + updatePriorityClass( + underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig); + } + + /** Set the LinkProperties */ + public void setLinkProperties( + @NonNull LinkProperties lp, + @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot lastSnapshot, + @Nullable PersistableBundleWrapper carrierConfig) { + mNetworkRecordBuilder.setLinkProperties(lp); + + updatePriorityClass( + underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig); + } + + /** Set whether the network is blocked */ + public void setIsBlocked( + boolean isBlocked, + @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot lastSnapshot, + @Nullable PersistableBundleWrapper carrierConfig) { + mNetworkRecordBuilder.setIsBlocked(isBlocked); + + updatePriorityClass( + underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig); + } + + /** Set whether the network is selected as VCN's underlying network */ + public void setIsSelected( + boolean isSelected, + @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot lastSnapshot, + @Nullable PersistableBundleWrapper carrierConfig) { + mIsSelected = isSelected; + + updatePriorityClass( + underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig); + } + + /** + * Update the last TelephonySubscriptionSnapshot and carrier config to reevaluate the network + */ + public void reevaluate( + @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot lastSnapshot, + @Nullable PersistableBundleWrapper carrierConfig) { + updatePriorityClass( + underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig); + } + + /** Return whether this network evaluator is valid */ + public boolean isValid() { + return mNetworkRecordBuilder.isValid(); + } + + /** Return the network */ + public Network getNetwork() { + return mNetworkRecordBuilder.getNetwork(); + } + + /** Return the network record */ + public UnderlyingNetworkRecord getNetworkRecord() { + return mNetworkRecordBuilder.build(); + } + + /** Return the priority class for network selection */ + public int getPriorityClass() { + return mPriorityClass; + } + + /** Dump the information of this instance */ + public void dump(IndentingPrintWriter pw) { + pw.println("UnderlyingNetworkEvaluator:"); + pw.increaseIndent(); + + if (mNetworkRecordBuilder.isValid()) { + getNetworkRecord().dump(pw); + } else { + pw.println( + "UnderlyingNetworkRecord incomplete: mNetwork: " + + mNetworkRecordBuilder.getNetwork()); + } + + pw.println("mIsSelected: " + mIsSelected); + pw.println("mPriorityClass: " + mPriorityClass); + + pw.decreaseIndent(); + } +} diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java index aea9f4d2dbae..7ab8e552722a 100644 --- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java @@ -16,24 +16,17 @@ package com.android.server.vcn.routeselection; -import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import android.annotation.NonNull; import android.annotation.Nullable; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; -import android.net.vcn.VcnUnderlyingNetworkTemplate; -import android.os.ParcelUuid; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.internal.util.IndentingPrintWriter; -import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; -import com.android.server.vcn.VcnContext; -import java.util.Comparator; -import java.util.List; import java.util.Objects; /** @@ -46,54 +39,17 @@ public class UnderlyingNetworkRecord { @NonNull public final NetworkCapabilities networkCapabilities; @NonNull public final LinkProperties linkProperties; public final boolean isBlocked; - public final boolean isSelected; - public final int priorityClass; @VisibleForTesting(visibility = Visibility.PRIVATE) public UnderlyingNetworkRecord( @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities, @NonNull LinkProperties linkProperties, - boolean isBlocked, - VcnContext vcnContext, - List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, - ParcelUuid subscriptionGroup, - TelephonySubscriptionSnapshot snapshot, - UnderlyingNetworkRecord currentlySelected, - PersistableBundleWrapper carrierConfig) { + boolean isBlocked) { this.network = network; this.networkCapabilities = networkCapabilities; this.linkProperties = linkProperties; this.isBlocked = isBlocked; - - this.isSelected = isSelected(this.network, currentlySelected); - - priorityClass = - NetworkPriorityClassifier.calculatePriorityClass( - vcnContext, - this, - underlyingNetworkTemplates, - subscriptionGroup, - snapshot, - currentlySelected, - carrierConfig); - } - - @VisibleForTesting(visibility = Visibility.PRIVATE) - public UnderlyingNetworkRecord( - @NonNull Network network, - @NonNull NetworkCapabilities networkCapabilities, - @NonNull LinkProperties linkProperties, - boolean isBlocked, - boolean isSelected, - int priorityClass) { - this.network = network; - this.networkCapabilities = networkCapabilities; - this.linkProperties = linkProperties; - this.isBlocked = isBlocked; - this.isSelected = isSelected; - - this.priorityClass = priorityClass; } @Override @@ -113,64 +69,20 @@ public class UnderlyingNetworkRecord { return Objects.hash(network, networkCapabilities, linkProperties, isBlocked); } - /** Returns if two records are equal including their priority classes. */ - public static boolean isEqualIncludingPriorities( - UnderlyingNetworkRecord left, UnderlyingNetworkRecord right) { - if (left != null && right != null) { - return left.equals(right) - && left.isSelected == right.isSelected - && left.priorityClass == right.priorityClass; - } - - return left == right; - } - - static Comparator<UnderlyingNetworkRecord> getComparator() { - return (left, right) -> { - final int leftIndex = left.priorityClass; - final int rightIndex = right.priorityClass; - - // In the case of networks in the same priority class, prioritize based on other - // criteria (eg. actively selected network, link metrics, etc) - if (leftIndex == rightIndex) { - // TODO: Improve the strategy of network selection when both UnderlyingNetworkRecord - // fall into the same priority class. - if (left.isSelected) { - return -1; - } - if (right.isSelected) { - return 1; - } - } - return Integer.compare(leftIndex, rightIndex); - }; - } - - private static boolean isSelected( - Network networkToCheck, UnderlyingNetworkRecord currentlySelected) { - if (currentlySelected == null) { - return false; - } - if (currentlySelected.network.equals(networkToCheck)) { - return true; - } - return false; + /** Return whether two records represent the same network */ + public static boolean isSameNetwork( + @Nullable UnderlyingNetworkRecord leftRecord, + @Nullable UnderlyingNetworkRecord rightRecord) { + final Network left = leftRecord == null ? null : leftRecord.network; + final Network right = rightRecord == null ? null : rightRecord.network; + return Objects.equals(left, right); } /** Dumps the state of this record for logging and debugging purposes. */ - void dump( - VcnContext vcnContext, - IndentingPrintWriter pw, - List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, - ParcelUuid subscriptionGroup, - TelephonySubscriptionSnapshot snapshot, - UnderlyingNetworkRecord currentlySelected, - PersistableBundleWrapper carrierConfig) { + void dump(IndentingPrintWriter pw) { pw.println("UnderlyingNetworkRecord:"); pw.increaseIndent(); - pw.println("priorityClass: " + priorityClass); - pw.println("isSelected: " + isSelected); pw.println("mNetwork: " + network); pw.println("mNetworkCapabilities: " + networkCapabilities); pw.println("mLinkProperties: " + linkProperties); @@ -218,29 +130,14 @@ public class UnderlyingNetworkRecord { return mNetworkCapabilities != null && mLinkProperties != null && mWasIsBlockedSet; } - UnderlyingNetworkRecord build( - VcnContext vcnContext, - List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, - ParcelUuid subscriptionGroup, - TelephonySubscriptionSnapshot snapshot, - UnderlyingNetworkRecord currentlySelected, - PersistableBundleWrapper carrierConfig) { + UnderlyingNetworkRecord build() { if (!isValid()) { throw new IllegalArgumentException( "Called build before UnderlyingNetworkRecord was valid"); } return new UnderlyingNetworkRecord( - mNetwork, - mNetworkCapabilities, - mLinkProperties, - mIsBlocked, - vcnContext, - underlyingNetworkTemplates, - subscriptionGroup, - snapshot, - currentlySelected, - carrierConfig); + mNetwork, mNetworkCapabilities, mLinkProperties, mIsBlocked); } } } diff --git a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java index 05da9dfe7921..e5c743cc69e4 100644 --- a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java +++ b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java @@ -18,6 +18,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; + import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -180,16 +181,8 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, if (snapshot == null) { return null; } - final HardwareBuffer buffer = snapshot.getHardwareBuffer(); - if (buffer.getWidth() == 0 || buffer.getHeight() == 0) { - buffer.close(); - Slog.e(TAG, "Invalid snapshot dimensions " + buffer.getWidth() + "x" - + buffer.getHeight()); - return null; - } else { - mCache.putSnapshot(source, snapshot); - return snapshot; - } + mCache.putSnapshot(source, snapshot); + return snapshot; } @VisibleForTesting @@ -210,6 +203,11 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, @Nullable TaskSnapshot snapshot(TYPE source) { + return snapshot(source, mHighResSnapshotScale); + } + + @Nullable + TaskSnapshot snapshot(TYPE source, float scale) { TaskSnapshot.Builder builder = new TaskSnapshot.Builder(); final Rect crop = prepareTaskSnapshot(source, builder); if (crop == null) { @@ -218,7 +216,7 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, } Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "createSnapshot"); final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer = createSnapshot(source, - mHighResSnapshotScale, crop, builder); + scale, crop, builder); Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); if (screenshotBuffer == null) { // Failed to acquire image. Has been logged. @@ -227,7 +225,19 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, builder.setCaptureTime(SystemClock.elapsedRealtimeNanos()); builder.setSnapshot(screenshotBuffer.getHardwareBuffer()); builder.setColorSpace(screenshotBuffer.getColorSpace()); - return builder.build(); + final TaskSnapshot snapshot = builder.build(); + return validateSnapshot(snapshot); + } + + private static TaskSnapshot validateSnapshot(@NonNull TaskSnapshot snapshot) { + final HardwareBuffer buffer = snapshot.getHardwareBuffer(); + if (buffer.getWidth() == 0 || buffer.getHeight() == 0) { + buffer.close(); + Slog.e(TAG, "Invalid snapshot dimensions " + buffer.getWidth() + "x" + + buffer.getHeight()); + return null; + } + return snapshot; } @Nullable @@ -432,7 +442,7 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, InsetUtils.addInsets(contentInsets, letterboxInsets); // Note, the app theme snapshot is never translucent because we enforce a non-translucent // color above - return new TaskSnapshot( + final TaskSnapshot taskSnapshot = new TaskSnapshot( System.currentTimeMillis() /* id */, SystemClock.elapsedRealtimeNanos() /* captureTime */, topActivity.mActivityComponent, hwBitmap.getHardwareBuffer(), @@ -441,6 +451,7 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, contentInsets, letterboxInsets, false /* isLowResolution */, false /* isRealSnapshot */, source.getWindowingMode(), getAppearance(source), false /* isTranslucent */, false /* hasImeSurface */); + return validateSnapshot(taskSnapshot); } static Rect getSystemBarInsets(Rect frame, InsetsState state) { diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index 4b55bec928c7..676203bc746a 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -1166,11 +1166,12 @@ class ActivityClientController extends IActivityClientController.Stub { transition.abort(); return; } - transition.collect(topFocusedRootTask); - executeMultiWindowFullscreenRequest(fullscreenRequest, topFocusedRootTask); - r.mTransitionController.requestStartTransition(transition, topFocusedRootTask, + final Task requestingTask = r.getTask(); + transition.collect(requestingTask); + executeMultiWindowFullscreenRequest(fullscreenRequest, requestingTask); + r.mTransitionController.requestStartTransition(transition, requestingTask, null /* remoteTransition */, null /* displayChange */); - transition.setReady(topFocusedRootTask, true); + transition.setReady(requestingTask, true); } private static void reportMultiwindowFullscreenRequestValidatingResult(IRemoteCallback callback, diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotController.java b/services/core/java/com/android/server/wm/ActivitySnapshotController.java index 7af494c296de..a692167bbbf9 100644 --- a/services/core/java/com/android/server/wm/ActivitySnapshotController.java +++ b/services/core/java/com/android/server/wm/ActivitySnapshotController.java @@ -25,6 +25,7 @@ import android.os.Environment; import android.os.SystemProperties; import android.os.Trace; import android.util.ArraySet; +import android.util.IntArray; import android.util.Slog; import android.util.SparseArray; import android.window.TaskSnapshot; @@ -36,6 +37,7 @@ import com.android.server.wm.BaseAppSnapshotPersister.PersistInfoProvider; import com.android.window.flags.Flags; import java.io.File; +import java.io.PrintWriter; import java.util.ArrayList; /** @@ -136,11 +138,26 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord false /* enableLowResSnapshots */, 0 /* lowResScaleFactor */, use16BitFormat); } - /** Retrieves a snapshot for an activity from cache. */ + /** + * Retrieves a snapshot for a set of activities from cache. + * This will only return the snapshot IFF input activities exist entirely in the snapshot. + * Sample: If the snapshot was captured with activity A and B, here will return null if the + * input activity is only [A] or [B], it must be [A, B] + */ @Nullable - TaskSnapshot getSnapshot(ActivityRecord ar) { - final int code = getSystemHashCode(ar); - return mCache.getSnapshot(code); + TaskSnapshot getSnapshot(@NonNull ActivityRecord[] activities) { + if (activities.length == 0) { + return null; + } + final UserSavedFile tmpUsf = findSavedFile(activities[0]); + if (tmpUsf == null || tmpUsf.mActivityIds.size() != activities.length) { + return null; + } + int fileId = 0; + for (int i = activities.length - 1; i >= 0; --i) { + fileId ^= getSystemHashCode(activities[i]); + } + return tmpUsf.mFileId == fileId ? mCache.getSnapshot(tmpUsf.mActivityIds.get(0)) : null; } private void cleanUpUserFiles(int userId) { @@ -229,33 +246,16 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord + " load " + mPendingLoadActivity); } // load snapshot to cache - for (int i = mPendingLoadActivity.size() - 1; i >= 0; i--) { - final ActivityRecord ar = mPendingLoadActivity.valueAt(i); - final int code = getSystemHashCode(ar); - final int userId = ar.mUserId; - if (mCache.getSnapshot(code) != null) { - // already in cache, skip - continue; - } - if (containsFile(code, userId)) { - synchronized (mSnapshotPersistQueue.getLock()) { - mSnapshotPersistQueue.insertQueueAtFirstLocked( - new LoadActivitySnapshotItem(ar, code, userId, mPersistInfoProvider)); - } - } - } + loadActivitySnapshot(); // clear mTmpRemoveActivity from cache for (int i = mPendingRemoveActivity.size() - 1; i >= 0; i--) { final ActivityRecord ar = mPendingRemoveActivity.valueAt(i); - final int code = getSystemHashCode(ar); - mCache.onIdRemoved(code); + removeCachedFiles(ar); } // clear snapshot on cache and delete files for (int i = mPendingDeleteActivity.size() - 1; i >= 0; i--) { final ActivityRecord ar = mPendingDeleteActivity.valueAt(i); - final int code = getSystemHashCode(ar); - mCache.onIdRemoved(code); - removeIfUserSavedFileExist(code, ar.mUserId); + removeIfUserSavedFileExist(ar); } // don't keep any reference resetTmpFields(); @@ -264,28 +264,38 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord class LoadActivitySnapshotItem extends SnapshotPersistQueue.WriteQueueItem { private final int mCode; private final int mUserId; - private final ActivityRecord mActivityRecord; + private final ActivityRecord[] mActivities; - LoadActivitySnapshotItem(@NonNull ActivityRecord ar, int code, int userId, + LoadActivitySnapshotItem(@NonNull ActivityRecord[] activities, int code, int userId, @NonNull PersistInfoProvider persistInfoProvider) { super(persistInfoProvider); - mActivityRecord = ar; + mActivities = activities; mCode = code; mUserId = userId; } @Override void write() { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, - "load_activity_snapshot"); - final TaskSnapshot snapshot = mSnapshotLoader.loadTask(mCode, - mUserId, false /* loadLowResolutionBitmap */); - synchronized (mService.getWindowManagerLock()) { - if (snapshot != null && !mActivityRecord.finishing) { - mCache.putSnapshot(mActivityRecord, snapshot); + try { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, + "load_activity_snapshot"); + final TaskSnapshot snapshot = mSnapshotLoader.loadTask(mCode, + mUserId, false /* loadLowResolutionBitmap */); + if (snapshot == null) { + return; + } + synchronized (mService.getWindowManagerLock()) { + // Verify the snapshot is still needed, and the activity is not finishing + if (!hasRecord(mActivities[0])) { + return; + } + for (ActivityRecord ar : mActivities) { + mCache.putSnapshot(ar, snapshot); + } } + } finally { + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } @Override @@ -297,18 +307,81 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord } } - void recordSnapshot(ActivityRecord activity) { - if (shouldDisableSnapshots()) { + void loadActivitySnapshot() { + if (mPendingLoadActivity.isEmpty()) { + return; + } + // Only load if saved file exists. + final ArraySet<UserSavedFile> loadingFiles = new ArraySet<>(); + for (int i = mPendingLoadActivity.size() - 1; i >= 0; i--) { + final ActivityRecord ar = mPendingLoadActivity.valueAt(i); + final UserSavedFile usf = findSavedFile(ar); + if (usf != null) { + loadingFiles.add(usf); + } + } + // Filter out the activity if the snapshot was removed. + for (int i = loadingFiles.size() - 1; i >= 0; i--) { + final UserSavedFile usf = loadingFiles.valueAt(i); + final ActivityRecord[] activities = usf.filterExistActivities(mPendingLoadActivity); + if (activities == null) { + continue; + } + if (getSnapshot(activities) != null) { + // Found the cache in memory, so skip loading from file. + continue; + } + loadSnapshotInner(activities, usf); + } + } + + @VisibleForTesting + void loadSnapshotInner(ActivityRecord[] activities, UserSavedFile usf) { + synchronized (mSnapshotPersistQueue.getLock()) { + mSnapshotPersistQueue.insertQueueAtFirstLocked(new LoadActivitySnapshotItem( + activities, usf.mFileId, usf.mUserId, mPersistInfoProvider)); + } + } + + /** + * Record one or multiple activities within a snapshot where those activities must belong to + * the same task. + * @param activity If the request activity is more than one, try to record those activities + * as a single snapshot, so those activities should belong to the same task. + */ + void recordSnapshot(@NonNull ArrayList<ActivityRecord> activity) { + if (shouldDisableSnapshots() || activity.isEmpty()) { return; } if (DEBUG) { Slog.d(TAG, "ActivitySnapshotController#recordSnapshot " + activity); } - final TaskSnapshot snapshot = recordSnapshotInner(activity); - if (snapshot != null) { - final int code = getSystemHashCode(activity); - addUserSavedFile(code, activity.mUserId, snapshot); + final int size = activity.size(); + final int[] mixedCode = new int[size]; + if (size == 1) { + final ActivityRecord singleActivity = activity.get(0); + final TaskSnapshot snapshot = recordSnapshotInner(singleActivity); + if (snapshot != null) { + mixedCode[0] = getSystemHashCode(singleActivity); + addUserSavedFile(singleActivity.mUserId, snapshot, mixedCode); + } + return; } + + final Task mainTask = activity.get(0).getTask(); + // Snapshot by task controller with activity's scale. + final TaskSnapshot snapshot = mService.mTaskSnapshotController + .snapshot(mainTask, mHighResSnapshotScale); + if (snapshot == null) { + return; + } + + for (int i = 0; i < activity.size(); ++i) { + final ActivityRecord next = activity.get(i); + mCache.putSnapshot(next, snapshot); + mixedCode[i] = getSystemHashCode(next); + } + addUserSavedFile(mainTask.mUserId, snapshot, mixedCode); } /** @@ -331,7 +404,8 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord } } - private static int getSystemHashCode(ActivityRecord activity) { + @VisibleForTesting + static int getSystemHashCode(ActivityRecord activity) { return System.identityHashCode(activity); } @@ -362,7 +436,13 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord if (ar.isVisibleRequested()) { mPendingDeleteActivity.add(ar); // load next one if exists. - addBelowActivityIfExist(ar, mPendingLoadActivity, true, "load-snapshot"); + // Note if this transition is happen between two TaskFragment, the next N - 1 activity + // may not participant in this transition. + // Sample: + // [TF1] close + // [TF2] open + // Bottom Activity <- Able to load this even it didn't participant the transition. + addBelowActivityIfExist(ar, mPendingLoadActivity, false, "load-snapshot"); } else { // remove the snapshot for the one below close addBelowActivityIfExist(ar, mPendingRemoveActivity, true, "remove-snapshot"); @@ -478,10 +558,8 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord } private void adjustSavedFileOrder(Task nextTopTask) { - final int userId = nextTopTask.mUserId; nextTopTask.forAllActivities(ar -> { - final int code = getSystemHashCode(ar); - final UserSavedFile usf = getUserFiles(userId).get(code); + final UserSavedFile usf = findSavedFile(ar); if (usf != null) { mSavedFilesInOrder.remove(usf); mSavedFilesInOrder.add(usf); @@ -494,9 +572,7 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord if (shouldDisableSnapshots()) { return; } - super.onAppRemoved(activity); - final int code = getSystemHashCode(activity); - removeIfUserSavedFileExist(code, activity.mUserId); + removeIfUserSavedFileExist(activity); if (DEBUG) { Slog.d(TAG, "ActivitySnapshotController#onAppRemoved delete snapshot " + activity); } @@ -507,9 +583,7 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord if (shouldDisableSnapshots()) { return; } - super.onAppDied(activity); - final int code = getSystemHashCode(activity); - removeIfUserSavedFileExist(code, activity.mUserId); + removeIfUserSavedFileExist(activity); if (DEBUG) { Slog.d(TAG, "ActivitySnapshotController#onAppDied delete snapshot " + activity); } @@ -558,55 +632,92 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord return mUserSavedFiles.get(userId); } - private void removeIfUserSavedFileExist(int code, int userId) { - final UserSavedFile usf = getUserFiles(userId).get(code); + UserSavedFile findSavedFile(@NonNull ActivityRecord ar) { + final int code = getSystemHashCode(ar); + return findSavedFile(ar.mUserId, code); + } + + UserSavedFile findSavedFile(int userId, int code) { + final SparseArray<UserSavedFile> usfs = getUserFiles(userId); + return usfs.get(code); + } + + private void removeCachedFiles(ActivityRecord ar) { + final UserSavedFile usf = findSavedFile(ar); + if (usf != null) { + for (int i = usf.mActivityIds.size() - 1; i >= 0; --i) { + final int activityId = usf.mActivityIds.get(i); + mCache.onIdRemoved(activityId); + } + } + } + + private void removeIfUserSavedFileExist(ActivityRecord ar) { + final UserSavedFile usf = findSavedFile(ar); if (usf != null) { - mUserSavedFiles.get(userId).remove(code); + final SparseArray<UserSavedFile> usfs = getUserFiles(ar.mUserId); + for (int i = usf.mActivityIds.size() - 1; i >= 0; --i) { + final int activityId = usf.mActivityIds.get(i); + usf.remove(activityId); + mCache.onIdRemoved(activityId); + usfs.remove(activityId); + } mSavedFilesInOrder.remove(usf); - mPersister.removeSnapshot(code, userId); + mPersister.removeSnapshot(usf.mFileId, ar.mUserId); } } - private boolean containsFile(int code, int userId) { - return getUserFiles(userId).get(code) != null; + @VisibleForTesting + boolean hasRecord(@NonNull ActivityRecord ar) { + return findSavedFile(ar) != null; } - private void addUserSavedFile(int code, int userId, TaskSnapshot snapshot) { - final SparseArray<UserSavedFile> savedFiles = getUserFiles(userId); - final UserSavedFile savedFile = savedFiles.get(code); - if (savedFile == null) { - final UserSavedFile usf = new UserSavedFile(code, userId); - savedFiles.put(code, usf); - mSavedFilesInOrder.add(usf); - mPersister.persistSnapshot(code, userId, snapshot); + @VisibleForTesting + void addUserSavedFile(int userId, TaskSnapshot snapshot, @NonNull int[] code) { + final UserSavedFile savedFile = findSavedFile(userId, code[0]); + if (savedFile != null) { + Slog.w(TAG, "Duplicate request for recording activity snapshot " + savedFile); + return; + } + int fileId = 0; + for (int i = code.length - 1; i >= 0; --i) { + fileId ^= code[i]; + } + final UserSavedFile usf = new UserSavedFile(fileId, userId); + SparseArray<UserSavedFile> usfs = getUserFiles(userId); + for (int i = code.length - 1; i >= 0; --i) { + usfs.put(code[i], usf); + } + usf.mActivityIds.addAll(code); + mSavedFilesInOrder.add(usf); + mPersister.persistSnapshot(fileId, userId, snapshot); - if (mSavedFilesInOrder.size() > MAX_PERSIST_SNAPSHOT_COUNT * 2) { - purgeSavedFile(); - } + if (mSavedFilesInOrder.size() > MAX_PERSIST_SNAPSHOT_COUNT * 2) { + purgeSavedFile(); } } private void purgeSavedFile() { final int savedFileCount = mSavedFilesInOrder.size(); final int removeCount = savedFileCount - MAX_PERSIST_SNAPSHOT_COUNT; - final ArrayList<UserSavedFile> usfs = new ArrayList<>(); - if (removeCount > 0) { - final int removeTillIndex = savedFileCount - removeCount; - for (int i = savedFileCount - 1; i > removeTillIndex; --i) { - final UserSavedFile usf = mSavedFilesInOrder.remove(i); - if (usf != null) { - final SparseArray<UserSavedFile> records = getUserFiles(usf.mUserId); - records.remove(usf.mFileId); - usfs.add(usf); - } - } + if (removeCount < 1) { + return; } - if (usfs.size() > 0) { - removeSnapshotFiles(usfs); + + final ArrayList<UserSavedFile> removeTargets = new ArrayList<>(); + for (int i = removeCount - 1; i >= 0; --i) { + final UserSavedFile usf = mSavedFilesInOrder.remove(i); + final SparseArray<UserSavedFile> files = mUserSavedFiles.get(usf.mUserId); + for (int j = usf.mActivityIds.size() - 1; j >= 0; --j) { + mCache.removeRunningEntry(usf.mActivityIds.get(j)); + files.remove(usf.mActivityIds.get(j)); + } + removeTargets.add(usf); } + removeSnapshotFiles(removeTargets); } - private void removeSnapshotFiles(ArrayList<UserSavedFile> files) { + private void removeSnapshotFiles(@NonNull ArrayList<UserSavedFile> files) { synchronized (mSnapshotPersistQueue.getLock()) { mSnapshotPersistQueue.sendToQueueLocked( new SnapshotPersistQueue.WriteQueueItem(mPersistInfoProvider) { @@ -624,12 +735,85 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord } } + @Override + void dump(PrintWriter pw, String prefix) { + super.dump(pw, prefix); + final String doublePrefix = prefix + " "; + final String triplePrefix = doublePrefix + " "; + for (int i = mUserSavedFiles.size() - 1; i >= 0; --i) { + final SparseArray<UserSavedFile> usfs = mUserSavedFiles.valueAt(i); + pw.println(doublePrefix + "UserSavedFile userId=" + mUserSavedFiles.keyAt(i)); + final ArraySet<UserSavedFile> sets = new ArraySet<>(); + for (int j = usfs.size() - 1; j >= 0; --j) { + sets.add(usfs.valueAt(j)); + } + for (int j = sets.size() - 1; j >= 0; --j) { + pw.println(triplePrefix + "SavedFile=" + sets.valueAt(j)); + } + } + } + static class UserSavedFile { - int mFileId; - int mUserId; + // The unique id as filename. + final int mFileId; + final int mUserId; + + /** + * The Id of all activities which are includes in the snapshot. + */ + final IntArray mActivityIds = new IntArray(); + UserSavedFile(int fileId, int userId) { mFileId = fileId; mUserId = userId; } + + boolean contains(int code) { + return mActivityIds.contains(code); + } + + void remove(int code) { + final int index = mActivityIds.indexOf(code); + if (index >= 0) { + mActivityIds.remove(index); + } + } + + ActivityRecord[] filterExistActivities( + @NonNull ArraySet<ActivityRecord> pendingLoadActivity) { + ArrayList<ActivityRecord> matchedActivities = null; + for (int i = pendingLoadActivity.size() - 1; i >= 0; --i) { + final ActivityRecord ar = pendingLoadActivity.valueAt(i); + if (contains(getSystemHashCode(ar))) { + if (matchedActivities == null) { + matchedActivities = new ArrayList<>(); + } + matchedActivities.add(ar); + } + } + if (matchedActivities == null || matchedActivities.size() != mActivityIds.size()) { + return null; + } + return matchedActivities.toArray(new ActivityRecord[0]); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(128); + sb.append("UserSavedFile{"); + sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(" fileId="); + sb.append(Integer.toHexString(mFileId)); + sb.append(", activityIds=["); + for (int i = mActivityIds.size() - 1; i >= 0; --i) { + sb.append(Integer.toHexString(mActivityIds.get(i))); + if (i > 0) { + sb.append(','); + } + } + sb.append("]"); + sb.append("}"); + return sb.toString(); + } } } diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index 22d17b596c4c..9e56d7a9d785 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -1311,7 +1311,7 @@ class BackNavigationController { Rect insets; if (mainWindow != null) { insets = mainWindow.getInsetsStateWithVisibilityOverride().calculateInsets( - mBounds, WindowInsets.Type.systemBars(), + mBounds, WindowInsets.Type.tappableElement(), false /* ignoreVisibility */).toRect(); InsetUtils.addInsets(insets, mainWindow.mActivityRecord.getLetterboxInsets()); } else { @@ -1327,7 +1327,8 @@ class BackNavigationController { return mAnimationTarget; } - void createStartingSurface(@NonNull WindowContainer closeWindow) { + void createStartingSurface(@NonNull WindowContainer closeWindow, + @NonNull ActivityRecord[] visibleOpenActivities) { if (!mIsOpen) { return; } @@ -1346,7 +1347,7 @@ class BackNavigationController { if (mainActivity == null) { return; } - final TaskSnapshot snapshot = getSnapshot(mTarget); + final TaskSnapshot snapshot = getSnapshot(mTarget, visibleOpenActivities); mRequestedStartingSurfaceId = openTask.mAtmService.mTaskOrganizerController .addWindowlessStartingSurface(openTask, mainActivity, // Choose configuration from closeWindow, because the configuration @@ -1489,7 +1490,8 @@ class BackNavigationController { // Try to draw two snapshot within a WindowlessStartingWindow, or find // another key for StartingWindowRecordManager. && openAnimationAdaptor.length == 1) { - openAnimationAdaptor[0].createStartingSurface(closeWindow); + openAnimationAdaptor[0].createStartingSurface(closeWindow, + visibleOpenActivities); } else { for (int i = visibleOpenActivities.length - 1; i >= 0; --i) { setLaunchBehind(visibleOpenActivities[i]); @@ -1671,7 +1673,8 @@ class BackNavigationController { mPendingAnimationBuilder = null; } - static TaskSnapshot getSnapshot(@NonNull WindowContainer w) { + static TaskSnapshot getSnapshot(@NonNull WindowContainer w, + ActivityRecord[] visibleOpenActivities) { if (w.asTask() != null) { final Task task = w.asTask(); return task.mRootWindowContainer.mWindowManager.mTaskSnapshotController.getSnapshot( @@ -1681,7 +1684,8 @@ class BackNavigationController { if (w.asActivityRecord() != null) { final ActivityRecord ar = w.asActivityRecord(); - return ar.mWmService.mSnapshotController.mActivitySnapshotController.getSnapshot(ar); + return ar.mWmService.mSnapshotController.mActivitySnapshotController + .getSnapshot(visibleOpenActivities); } return null; } diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index fc3a33883de6..9c9cf04de3cc 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -120,38 +120,56 @@ public class BackgroundActivityStartController { static final int BAL_BLOCK = 0; - static final int BAL_ALLOW_DEFAULT = 1; + static final int BAL_ALLOW_DEFAULT = + FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_DEFAULT; // Following codes are in order of precedence /** Important UIDs which should be always allowed to launch activities */ - static final int BAL_ALLOW_ALLOWLISTED_UID = 2; + static final int BAL_ALLOW_ALLOWLISTED_UID = + FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_ALLOWLISTED_UID; /** Apps that fulfill a certain role that can can always launch new tasks */ - static final int BAL_ALLOW_ALLOWLISTED_COMPONENT = 3; + static final int BAL_ALLOW_ALLOWLISTED_COMPONENT = + FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_ALLOWLISTED_COMPONENT; - /** Apps which currently have a visible window or are bound by a service with a visible - * window */ - static final int BAL_ALLOW_VISIBLE_WINDOW = 4; + /** + * Apps which currently have a visible window or are bound by a service with a visible + * window + */ + static final int BAL_ALLOW_VISIBLE_WINDOW = + FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_VISIBLE_WINDOW; /** Allowed due to the PendingIntent sender */ - static final int BAL_ALLOW_PENDING_INTENT = 5; + static final int BAL_ALLOW_PENDING_INTENT = + FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_PENDING_INTENT; - /** App has START_ACTIVITIES_FROM_BACKGROUND permission or BAL instrumentation privileges - * granted to it */ - static final int BAL_ALLOW_PERMISSION = 6; + /** + * App has START_ACTIVITIES_FROM_BACKGROUND permission or BAL instrumentation privileges + * granted to it + */ + static final int BAL_ALLOW_PERMISSION = + FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_BAL_PERMISSION; /** Process has SYSTEM_ALERT_WINDOW permission granted to it */ - static final int BAL_ALLOW_SAW_PERMISSION = 7; + static final int BAL_ALLOW_SAW_PERMISSION = + FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_SAW_PERMISSION; /** App is in grace period after an activity was started or finished */ - static final int BAL_ALLOW_GRACE_PERIOD = 8; + static final int BAL_ALLOW_GRACE_PERIOD = + FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_GRACE_PERIOD; /** App is in a foreground task or bound to a foreground service (but not itself visible) */ - static final int BAL_ALLOW_FOREGROUND = 9; + static final int BAL_ALLOW_FOREGROUND = + FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_FOREGROUND; /** Process belongs to a SDK sandbox */ - static final int BAL_ALLOW_SDK_SANDBOX = 10; + static final int BAL_ALLOW_SDK_SANDBOX = + FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_SDK_SANDBOX; + + /** Process belongs to a SDK sandbox */ + static final int BAL_ALLOW_NON_APP_VISIBLE_WINDOW = + FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_NON_APP_VISIBLE_WINDOW; static String balCodeToString(@BalCode int balCode) { return switch (balCode) { @@ -160,6 +178,7 @@ public class BackgroundActivityStartController { case BAL_ALLOW_DEFAULT -> "BAL_ALLOW_DEFAULT"; case BAL_ALLOW_FOREGROUND -> "BAL_ALLOW_FOREGROUND"; case BAL_ALLOW_GRACE_PERIOD -> "BAL_ALLOW_GRACE_PERIOD"; + case BAL_ALLOW_NON_APP_VISIBLE_WINDOW -> "BAL_ALLOW_NON_APP_VISIBLE_WINDOW"; case BAL_ALLOW_PENDING_INTENT -> "BAL_ALLOW_PENDING_INTENT"; case BAL_ALLOW_PERMISSION -> "BAL_ALLOW_PERMISSION"; case BAL_ALLOW_SAW_PERMISSION -> "BAL_ALLOW_SAW_PERMISSION"; @@ -788,7 +807,7 @@ public class BackgroundActivityStartController { /*background*/ false, "callingUid has visible window"); } if (mService.mActiveUids.hasNonAppVisibleWindow(callingUid)) { - return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, + return new BalVerdict(BAL_ALLOW_NON_APP_VISIBLE_WINDOW, /*background*/ false, "callingUid has non-app visible window"); } @@ -884,7 +903,7 @@ public class BackgroundActivityStartController { /*background*/ false, "realCallingUid has visible window"); } if (mService.mActiveUids.hasNonAppVisibleWindow(state.mRealCallingUid)) { - return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, + return new BalVerdict(BAL_ALLOW_NON_APP_VISIBLE_WINDOW, /*background*/ false, "realCallingUid has non-app visible window"); } } else { @@ -989,7 +1008,8 @@ public class BackgroundActivityStartController { || balCode == BAL_ALLOW_PERMISSION || balCode == BAL_ALLOW_PENDING_INTENT || balCode == BAL_ALLOW_SAW_PERMISSION - || balCode == BAL_ALLOW_VISIBLE_WINDOW) { + || balCode == BAL_ALLOW_VISIBLE_WINDOW + || balCode == BAL_ALLOW_NON_APP_VISIBLE_WINDOW) { return true; } } @@ -1501,7 +1521,8 @@ public class BackgroundActivityStartController { Intent intent = state.mIntent; if (code == BAL_ALLOW_PENDING_INTENT - && (callingUid == Process.SYSTEM_UID || realCallingUid == Process.SYSTEM_UID)) { + && (callingUid < Process.FIRST_APPLICATION_UID + || realCallingUid < Process.FIRST_APPLICATION_UID)) { String activityName = intent != null ? requireNonNull(intent.getComponent()).flattenToShortString() : ""; writeBalAllowedLog(activityName, BAL_ALLOW_PENDING_INTENT, diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index bf30af3e8596..8035a298e45a 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -112,7 +112,7 @@ final class InputMonitor { * z-layering reference so that we can place the recents input consumer above it. */ private WeakReference<ActivityRecord> mActiveRecentsActivity = null; - private WeakReference<ActivityRecord> mActiveRecentsLayerRef = null; + private WeakReference<Task> mActiveRecentsLayerRef = null; private class UpdateInputWindows implements Runnable { @Override @@ -389,9 +389,9 @@ final class InputMonitor { /** * Inform InputMonitor when recents is active so it can enable the recents input consumer. * @param activity The active recents activity. {@code null} means recents is not active. - * @param layer An activity whose Z-layer is used as a reference for how to sort the consumer. + * @param layer A task whose Z-layer is used as a reference for how to sort the consumer. */ - void setActiveRecents(@Nullable ActivityRecord activity, @Nullable ActivityRecord layer) { + void setActiveRecents(@Nullable ActivityRecord activity, @Nullable Task layer) { final boolean clear = activity == null; final boolean wasActive = mActiveRecentsActivity != null && mActiveRecentsLayerRef != null; mActiveRecentsActivity = clear ? null : new WeakReference<>(activity); diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index ed54ea8229fe..f10a733040ed 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -75,6 +75,7 @@ import android.view.InsetsState; import android.view.SurfaceControl; import android.view.SurfaceSession; import android.view.View; +import android.view.View.FocusDirection; import android.view.WindowInsets; import android.view.WindowInsets.Type.InsetsType; import android.view.WindowManager; @@ -1000,6 +1001,17 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { } return didTransfer; } + + @Override + public boolean moveFocusToAdjacentWindow(IWindow fromWindow, @FocusDirection int direction) { + final long identity = Binder.clearCallingIdentity(); + try { + return mService.moveFocusToAdjacentWindow(this, fromWindow, direction); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + @Override public void generateDisplayHash(IWindow window, Rect boundsInWindow, String hashAlgorithm, RemoteCallback callback) { diff --git a/services/core/java/com/android/server/wm/SnapshotController.java b/services/core/java/com/android/server/wm/SnapshotController.java index b6f040a6cb56..3014f979aa70 100644 --- a/services/core/java/com/android/server/wm/SnapshotController.java +++ b/services/core/java/com/android/server/wm/SnapshotController.java @@ -160,9 +160,7 @@ class SnapshotController { if (!allOpensOptInOnBackInvoked() || mCloseActivities.isEmpty()) { return; } - for (int i = mCloseActivities.size() - 1; i >= 0; --i) { - controller.recordSnapshot(mCloseActivities.get(i)); - } + controller.recordSnapshot(mCloseActivities); } } } diff --git a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java index bffdf54e17ce..e4379b5343f3 100644 --- a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java +++ b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java @@ -274,7 +274,9 @@ class SnapshotPersistQueue { @Override void write() { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "StoreWriteQueueItem"); + if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "StoreWriteQueueItem#" + mId); + } if (!mPersistInfoProvider.createDirectory(mUserId)) { Slog.e(TAG, "Unable to create snapshot directory for user dir=" + mPersistInfoProvider.getDirectory(mUserId)); diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index e1bf8f8aa34a..f620a9743eb4 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -1933,29 +1933,29 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { dc.getInputMonitor().getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION); ActivityRecord recentsActivity = null; if (recentsAnimationInputConsumer != null) { - // find the top-most going-away activity and the recents activity. The top-most + // Find the top-most going-away task and the recents activity. The top-most // is used as layer reference while the recents is used for registering the consumer // override. - ActivityRecord topActivity = null; + Task topNonRecentsTask = null; for (int i = 0; i < info.getChanges().size(); ++i) { - final TransitionInfo.Change change = info.getChanges().get(i); - if (change.getTaskInfo() == null) continue; - final Task task = Task.fromWindowContainerToken( - info.getChanges().get(i).getTaskInfo().token); + final ActivityManager.RunningTaskInfo taskInfo = + info.getChanges().get(i).getTaskInfo(); + if (taskInfo == null) continue; + final Task task = Task.fromWindowContainerToken(taskInfo.token); if (task == null) continue; - final int activityType = change.getTaskInfo().topActivityType; + final int activityType = taskInfo.topActivityType; final boolean isRecents = activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS; if (isRecents && recentsActivity == null) { recentsActivity = task.getTopVisibleActivity(); - } else if (!isRecents && topActivity == null) { - topActivity = task.getTopNonFinishingActivity(); + } else if (!isRecents && topNonRecentsTask == null) { + topNonRecentsTask = task; } } - if (recentsActivity != null && topActivity != null) { + if (recentsActivity != null && topNonRecentsTask != null) { recentsAnimationInputConsumer.mWindowHandle.touchableRegion.set( - topActivity.getBounds()); - dc.getInputMonitor().setActiveRecents(recentsActivity, topActivity); + topNonRecentsTask.getBounds()); + dc.getInputMonitor().setActiveRecents(recentsActivity, topNonRecentsTask); } } @@ -2020,16 +2020,17 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } mController.mNavigationBarAttachedToApp = false; - if (mRecentsDisplayId == INVALID_DISPLAY) { - Slog.e(TAG, "Reparented navigation bar without a valid display"); - mRecentsDisplayId = DEFAULT_DISPLAY; + int recentsDisplayId = mRecentsDisplayId; + if (recentsDisplayId == INVALID_DISPLAY) { + Slog.i(TAG, "Restore parent surface of navigation bar by another transition"); + recentsDisplayId = DEFAULT_DISPLAY; } final DisplayContent dc = - mController.mAtm.mRootWindowContainer.getDisplayContent(mRecentsDisplayId); + mController.mAtm.mRootWindowContainer.getDisplayContent(recentsDisplayId); final StatusBarManagerInternal bar = dc.getDisplayPolicy().getStatusBarManagerInternal(); if (bar != null) { - bar.setNavigationBarLumaSamplingEnabled(mRecentsDisplayId, true); + bar.setNavigationBarLumaSamplingEnabled(recentsDisplayId, true); } final WindowState navWindow = dc.getDisplayPolicy().getNavigationBar(); if (navWindow == null) return; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index c63cc4373472..95448352736f 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -287,6 +287,7 @@ import android.view.SurfaceControlViewHost; import android.view.SurfaceSession; import android.view.TaskTransitionSpec; import android.view.View; +import android.view.View.FocusDirection; import android.view.ViewDebug; import android.view.WindowContentFrameStats; import android.view.WindowInsets; @@ -9104,6 +9105,66 @@ public class WindowManagerService extends IWindowManager.Stub win.mClient); } + boolean moveFocusToAdjacentWindow(Session session, IWindow fromWindow, + @FocusDirection int direction) { + synchronized (mGlobalLock) { + final WindowState fromWin = windowForClientLocked(session, fromWindow, false); + if (fromWin == null || !fromWin.isFocused()) { + return false; + } + final TaskFragment fromFragment = fromWin.getTaskFragment(); + if (fromFragment == null) { + return false; + } + final TaskFragment adjacentFragment = fromFragment.getAdjacentTaskFragment(); + if (adjacentFragment == null || adjacentFragment.asTask() != null) { + // Don't move the focus to another task. + return false; + } + final Rect fromBounds = fromFragment.getBounds(); + final Rect adjacentBounds = adjacentFragment.getBounds(); + switch (direction) { + case View.FOCUS_LEFT: + if (adjacentBounds.left >= fromBounds.left) { + return false; + } + break; + case View.FOCUS_UP: + if (adjacentBounds.top >= fromBounds.top) { + return false; + } + break; + case View.FOCUS_RIGHT: + if (adjacentBounds.right <= fromBounds.right) { + return false; + } + break; + case View.FOCUS_DOWN: + if (adjacentBounds.bottom <= fromBounds.bottom) { + return false; + } + break; + case View.FOCUS_BACKWARD: + case View.FOCUS_FORWARD: + // These are not absolute directions. Skip checking the bounds. + break; + default: + return false; + } + final ActivityRecord topRunningActivity = adjacentFragment.topRunningActivity( + true /* focusableOnly */); + if (topRunningActivity == null) { + return false; + } + moveDisplayToTopInternal(topRunningActivity.getDisplayId()); + handleTaskFocusChange(topRunningActivity.getTask(), topRunningActivity); + if (fromWin.isFocused()) { + return false; + } + } + return true; + } + /** Return whether layer tracing is enabled */ public boolean isLayerTracing() { if (!checkCallingPermission( diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index afb0b20650f8..414339d3f349 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -299,7 +299,7 @@ public: void setSystemUiLightsOut(bool lightsOut); void setPointerDisplayId(int32_t displayId); void setPointerSpeed(int32_t speed); - void setPointerAcceleration(float acceleration); + void setMousePointerAccelerationEnabled(bool enabled); void setTouchpadPointerSpeed(int32_t speed); void setTouchpadNaturalScrollingEnabled(bool enabled); void setTouchpadTapToClickEnabled(bool enabled); @@ -411,8 +411,8 @@ private: // Pointer speed. int32_t pointerSpeed{0}; - // Pointer acceleration. - float pointerAcceleration{android::os::IInputConstants::DEFAULT_POINTER_ACCELERATION}; + // True if pointer acceleration is enabled for mice. + bool mousePointerAccelerationEnabled{true}; // True if pointer gestures are enabled. bool pointerGesturesEnabled{true}; @@ -502,7 +502,8 @@ void NativeInputManager::dump(std::string& dump) { dump += StringPrintf(INDENT "System UI Lights Out: %s\n", toString(mLocked.systemUiLightsOut)); dump += StringPrintf(INDENT "Pointer Speed: %" PRId32 "\n", mLocked.pointerSpeed); - dump += StringPrintf(INDENT "Pointer Acceleration: %0.3f\n", mLocked.pointerAcceleration); + dump += StringPrintf(INDENT "Mouse Pointer Acceleration: %s\n", + mLocked.mousePointerAccelerationEnabled ? "Enabled" : "Disabled"); dump += StringPrintf(INDENT "Pointer Gestures Enabled: %s\n", toString(mLocked.pointerGesturesEnabled)); dump += StringPrintf(INDENT "Show Touches: %s\n", toString(mLocked.showTouches)); @@ -686,7 +687,10 @@ void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outCon outConfig->pointerVelocityControlParameters.scale = exp2f(mLocked.pointerSpeed * POINTER_SPEED_EXPONENT); - outConfig->pointerVelocityControlParameters.acceleration = mLocked.pointerAcceleration; + outConfig->pointerVelocityControlParameters.acceleration = + mLocked.mousePointerAccelerationEnabled + ? android::os::IInputConstants::DEFAULT_POINTER_ACCELERATION + : 1; outConfig->pointerGesturesEnabled = mLocked.pointerGesturesEnabled; outConfig->showTouches = mLocked.showTouches; @@ -1207,16 +1211,16 @@ void NativeInputManager::setPointerSpeed(int32_t speed) { InputReaderConfiguration::Change::POINTER_SPEED); } -void NativeInputManager::setPointerAcceleration(float acceleration) { +void NativeInputManager::setMousePointerAccelerationEnabled(bool enabled) { { // acquire lock std::scoped_lock _l(mLock); - if (mLocked.pointerAcceleration == acceleration) { + if (mLocked.mousePointerAccelerationEnabled == enabled) { return; } - ALOGI("Setting pointer acceleration to %0.3f", acceleration); - mLocked.pointerAcceleration = acceleration; + ALOGI("Setting mouse pointer acceleration to %s", toString(enabled)); + mLocked.mousePointerAccelerationEnabled = enabled; } // release lock mInputManager->getReader().requestRefreshConfiguration( @@ -2174,10 +2178,11 @@ static void nativeSetPointerSpeed(JNIEnv* env, jobject nativeImplObj, jint speed im->setPointerSpeed(speed); } -static void nativeSetPointerAcceleration(JNIEnv* env, jobject nativeImplObj, jfloat acceleration) { +static void nativeSetMousePointerAccelerationEnabled(JNIEnv* env, jobject nativeImplObj, + jboolean enabled) { NativeInputManager* im = getNativeInputManager(env, nativeImplObj); - im->setPointerAcceleration(acceleration); + im->setMousePointerAccelerationEnabled(enabled); } static void nativeSetTouchpadPointerSpeed(JNIEnv* env, jobject nativeImplObj, jint speed) { @@ -2812,7 +2817,8 @@ static const JNINativeMethod gInputManagerMethods[] = { (void*)nativeTransferTouchFocus}, {"transferTouch", "(Landroid/os/IBinder;I)Z", (void*)nativeTransferTouch}, {"setPointerSpeed", "(I)V", (void*)nativeSetPointerSpeed}, - {"setPointerAcceleration", "(F)V", (void*)nativeSetPointerAcceleration}, + {"setMousePointerAccelerationEnabled", "(Z)V", + (void*)nativeSetMousePointerAccelerationEnabled}, {"setTouchpadPointerSpeed", "(I)V", (void*)nativeSetTouchpadPointerSpeed}, {"setTouchpadNaturalScrollingEnabled", "(Z)V", (void*)nativeSetTouchpadNaturalScrollingEnabled}, diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java index dfb5a5758448..fb0729f806b1 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java @@ -517,6 +517,8 @@ public final class CredentialManagerService .map(CredentialOption::getType) .collect(Collectors.toList())); + finalizeAndEmitInitialPhaseMetric(session); + if (providerSessions.isEmpty()) { try { callback.onError( @@ -776,6 +778,13 @@ public final class CredentialManagerService providerSessions.forEach(ProviderSession::invokeSession); } + private void finalizeAndEmitInitialPhaseMetric(GetCandidateRequestSession session) { + var initMetric = session.mRequestSessionMetric.getInitialPhaseMetric(); + initMetric.setAutofillSessionId(session.getAutofillSessionId()); + initMetric.setAutofillRequestId(session.getAutofillRequestId()); + finalizeAndEmitInitialPhaseMetric((RequestSession) session); + } + private void finalizeAndEmitInitialPhaseMetric(RequestSession session) { try { var initMetric = session.mRequestSessionMetric.getInitialPhaseMetric(); diff --git a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java index d1651713fe03..0f914c32346d 100644 --- a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java @@ -49,7 +49,12 @@ public class GetCandidateRequestSession extends RequestSession<GetCredentialRequ implements ProviderSession.ProviderInternalCallback<GetCredentialResponse> { private static final String TAG = "GetCandidateRequestSession"; + private static final String SESSION_ID_KEY = "autofill_session_id"; + private static final String REQUEST_ID_KEY = "autofill_request_id"; + private final IAutoFillManagerClient mAutoFillCallback; + private final int mAutofillSessionId; + private final int mAutofillRequestId; public GetCandidateRequestSession( Context context, SessionLifetime sessionCallback, @@ -62,6 +67,8 @@ public class GetCandidateRequestSession extends RequestSession<GetCredentialRequ RequestInfo.TYPE_GET, callingAppInfo, enabledProviders, cancellationSignal, 0L); mAutoFillCallback = autoFillCallback; + mAutofillSessionId = request.getData().getInt(SESSION_ID_KEY, -1); + mAutofillRequestId = request.getData().getInt(REQUEST_ID_KEY, -1); } /** @@ -177,4 +184,19 @@ public class GetCandidateRequestSession extends RequestSession<GetCredentialRequ Slog.d(TAG, "onFinalResponseReceived"); respondToClientWithResponseAndFinish(new GetCandidateCredentialsResponse(response)); } + + /** + * Returns autofill session id. Returns -1 if unavailable. + */ + public int getAutofillSessionId() { + return mAutofillSessionId; + } + + /** + * Returns autofill request id. Returns -1 if unavailable. + */ + public int getAutofillRequestId() { + return mAutofillRequestId; + + } } diff --git a/services/credentials/java/com/android/server/credentials/MetricUtilities.java b/services/credentials/java/com/android/server/credentials/MetricUtilities.java index b36de0b03fa8..23aa3742175a 100644 --- a/services/credentials/java/com/android/server/credentials/MetricUtilities.java +++ b/services/credentials/java/com/android/server/credentials/MetricUtilities.java @@ -426,7 +426,11 @@ public class MetricUtilities { /* per_classtype_counts */ initialPhaseMetric.getUniqueRequestCounts(), /* origin_specified */ - initialPhaseMetric.isOriginSpecified() + initialPhaseMetric.isOriginSpecified(), + /* autofill_session_id */ + initialPhaseMetric.getAutofillSessionId(), + /* autofill_request_id */ + initialPhaseMetric.getAutofillRequestId() ); } catch (Exception e) { Slog.w(TAG, "Unexpected error during initial metric emit: " + e); diff --git a/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java index 8e965e3e5ba5..8a4e86c440b3 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java @@ -49,6 +49,12 @@ public class InitialPhaseMetric { // Stores the deduped request information, particularly {"req":5} private Map<String, Integer> mRequestCounts = new LinkedHashMap<>(); + // The session id of autofill if the request is from autofill, defaults to -1 + private int mAutofillSessionId = -1; + + // The request id of autofill if the request is from autofill, defaults to -1 + private int mAutofillRequestId = -1; + public InitialPhaseMetric(int sessionIdTrackOne) { mSessionIdCaller = sessionIdTrackOne; @@ -126,6 +132,24 @@ public class InitialPhaseMetric { return mOriginSpecified; } + /* ------ Autofill Integration ------ */ + + public void setAutofillSessionId(int autofillSessionId) { + mAutofillSessionId = autofillSessionId; + } + + public int getAutofillSessionId() { + return mAutofillSessionId; + } + + public void setAutofillRequestId(int autofillRequestId) { + mAutofillRequestId = autofillRequestId; + } + + public int getAutofillRequestId() { + return mAutofillRequestId; + } + /* ------ Unique Request Counts Map Information ------ */ public void setRequestCounts(Map<String, Integer> requestCounts) { diff --git a/services/tests/BackgroundInstallControlServiceTests/host/Android.bp b/services/tests/BackgroundInstallControlServiceTests/host/Android.bp index 4fcdbfc21f6c..e3954355491d 100644 --- a/services/tests/BackgroundInstallControlServiceTests/host/Android.bp +++ b/services/tests/BackgroundInstallControlServiceTests/host/Android.bp @@ -33,6 +33,7 @@ java_test_host { ":BackgroundInstallControlServiceTestApp", ":BackgroundInstallControlMockApp1", ":BackgroundInstallControlMockApp2", + ":BackgroundInstallControlMockApp3", ], test_suites: [ "general-tests", diff --git a/services/tests/BackgroundInstallControlServiceTests/host/AndroidTest.xml b/services/tests/BackgroundInstallControlServiceTests/host/AndroidTest.xml index 1e7a78aa6f93..031d57fbe182 100644 --- a/services/tests/BackgroundInstallControlServiceTests/host/AndroidTest.xml +++ b/services/tests/BackgroundInstallControlServiceTests/host/AndroidTest.xml @@ -29,11 +29,14 @@ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> <option name="cleanup" value="true" /> <option name="push-file" - key="BackgroundInstallControlMockApp1.apk" - value="/data/local/tmp/BackgroundInstallControlMockApp1.apk" /> + key="BackgroundInstallControlMockApp1.apk" + value="/data/local/tmp/BackgroundInstallControlMockApp1.apk" /> <option name="push-file" - key="BackgroundInstallControlMockApp2.apk" - value="/data/local/tmp/BackgroundInstallControlMockApp2.apk" /> + key="BackgroundInstallControlMockApp2.apk" + value="/data/local/tmp/BackgroundInstallControlMockApp2.apk" /> + <option name="push-file" + key="BackgroundInstallControlMockApp3.apk" + value="/data/local/tmp/BackgroundInstallControlMockApp3.apk" /> </target_preparer> <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" > diff --git a/services/tests/BackgroundInstallControlServiceTests/host/src/com/android/server/pm/test/BackgroundInstallControlServiceHostTest.java b/services/tests/BackgroundInstallControlServiceTests/host/src/com/android/server/pm/test/BackgroundInstallControlServiceHostTest.java index 74506076d82f..5092a4659eb9 100644 --- a/services/tests/BackgroundInstallControlServiceTests/host/src/com/android/server/pm/test/BackgroundInstallControlServiceHostTest.java +++ b/services/tests/BackgroundInstallControlServiceTests/host/src/com/android/server/pm/test/BackgroundInstallControlServiceHostTest.java @@ -41,17 +41,26 @@ public final class BackgroundInstallControlServiceHostTest extends BaseHostJUnit private static final String MOCK_APK_FILE_1 = "BackgroundInstallControlMockApp1.apk"; private static final String MOCK_APK_FILE_2 = "BackgroundInstallControlMockApp2.apk"; + // TODO: Move the silent installs to test-app using {@link + // BackgroundInstallControlServiceTest#installPackage(String, String)} and remove deviceConfig + // branch in BICS. + // b/310983905 @Test public void testGetMockBackgroundInstalledPackages() throws Exception { - installPackage(TEST_DATA_DIR + MOCK_APK_FILE_1); + installPackage(TEST_DATA_DIR + MOCK_APK_FILE_1); installPackage(TEST_DATA_DIR + MOCK_APK_FILE_2); assertThat(getDevice().getAppPackageInfo(MOCK_PACKAGE_NAME_1)).isNotNull(); assertThat(getDevice().getAppPackageInfo(MOCK_PACKAGE_NAME_2)).isNotNull(); - assertThat(getDevice().setProperty("debug.transparency.bg-install-apps", - MOCK_PACKAGE_NAME_1 + "," + MOCK_PACKAGE_NAME_2)).isTrue(); - runDeviceTest("testGetMockBackgroundInstalledPackages"); + assertThat( + getDevice() + .setProperty( + "debug.transparency.bg-install-apps", + MOCK_PACKAGE_NAME_1 + "," + MOCK_PACKAGE_NAME_2)) + .isTrue(); + runDeviceTest( + "BackgroundInstallControlServiceTest", "testGetMockBackgroundInstalledPackages"); assertThat(getDevice().uninstallPackage(MOCK_PACKAGE_NAME_1)).isNull(); assertThat(getDevice().uninstallPackage(MOCK_PACKAGE_NAME_2)).isNull(); @@ -59,16 +68,30 @@ public final class BackgroundInstallControlServiceHostTest extends BaseHostJUnit assertThat(getDevice().getAppPackageInfo(MOCK_PACKAGE_NAME_2)).isNull(); } + @Test + public void testRegisterCallback() throws Exception { + runDeviceTest( + "BackgroundInstallControlServiceTest", + "testRegisterBackgroundInstallControlCallback"); + } + + @Test + public void testUnregisterCallback() throws Exception { + runDeviceTest( + "BackgroundInstallControlServiceTest", + "testUnregisterBackgroundInstallControlCallback"); + } + private void installPackage(String path) throws DeviceNotAvailableException { String cmd = "pm install -t --force-queryable " + path; CommandResult result = getDevice().executeShellV2Command(cmd); assertThat(result.getStatus() == CommandStatus.SUCCESS).isTrue(); } - private void runDeviceTest(String method) throws DeviceNotAvailableException { + private void runDeviceTest(String testName, String method) throws DeviceNotAvailableException { var options = new DeviceTestRunOptions(PACKAGE_NAME); - options.setTestClassName(PACKAGE_NAME + ".BackgroundInstallControlServiceTest"); + options.setTestClassName(PACKAGE_NAME + "." + testName); options.setTestMethodName(method); runDeviceTests(options); } -} +}
\ No newline at end of file diff --git a/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/AndroidManifest.xml b/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/AndroidManifest.xml index 1fa1f84cd04e..b5b8ea0f40c7 100644 --- a/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/AndroidManifest.xml +++ b/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/AndroidManifest.xml @@ -21,6 +21,9 @@ <uses-library android:name="android.test.runner" /> </application> + <uses-permission android:name="android.permission.INSTALL_PACKAGES" /> + <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" android:label="APCT tests for background install control service" android:targetPackage="com.android.server.pm.test.app" /> diff --git a/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/src/com/android/server/pm/test/app/BackgroundInstallControlServiceTest.java b/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/src/com/android/server/pm/test/app/BackgroundInstallControlServiceTest.java index b74e5619fd0c..f033fed73b27 100644 --- a/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/src/com/android/server/pm/test/app/BackgroundInstallControlServiceTest.java +++ b/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/src/com/android/server/pm/test/app/BackgroundInstallControlServiceTest.java @@ -16,54 +16,256 @@ package com.android.server.pm.test.app; +import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; +import static android.Manifest.permission.QUERY_ALL_PACKAGES; + +import static com.android.compatibility.common.util.SystemUtil.runShellCommand; + import static com.google.common.truth.Truth.assertThat; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.IBackgroundInstallControlService; import android.content.pm.PackageInfo; +import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; +import android.os.Bundle; +import android.os.IRemoteCallback; +import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.UserHandle; +import android.util.Pair; +import androidx.annotation.NonNull; +import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; +import com.android.compatibility.common.util.ShellIdentityUtils; +import com.android.compatibility.common.util.ThrowingRunnable; + +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.io.FileInputStream; +import java.io.OutputStream; +import java.util.ArrayList; import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; import java.util.stream.Collectors; @RunWith(AndroidJUnit4.class) public class BackgroundInstallControlServiceTest { private static final String TAG = "BackgroundInstallControlServiceTest"; + private static final String ACTION_INSTALL_COMMIT = + "com.android.server.pm.test.app.BackgroundInstallControlServiceTest" + + ".ACTION_INSTALL_COMMIT"; + private static final String MOCK_PACKAGE_NAME = "com.android.servicestests.apps.bicmockapp3"; + + private static final String TEST_DATA_DIR = "/data/local/tmp/"; + private static final String MOCK_APK_FILE = "BackgroundInstallControlMockApp3.apk"; private IBackgroundInstallControlService mIBics; @Before public void setUp() { - mIBics = IBackgroundInstallControlService.Stub.asInterface( - ServiceManager.getService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE)); + mIBics = + IBackgroundInstallControlService.Stub.asInterface( + ServiceManager.getService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE)); assertThat(mIBics).isNotNull(); } + @After + public void tearDown() { + runShellCommand("pm uninstall " + MOCK_PACKAGE_NAME); + } + @Test public void testGetMockBackgroundInstalledPackages() throws RemoteException { - ParceledListSlice<PackageInfo> slice = mIBics.getBackgroundInstalledPackages( - PackageManager.MATCH_ALL, - UserHandle.USER_ALL); + ParceledListSlice<PackageInfo> slice = + ShellIdentityUtils.invokeMethodWithShellPermissions( + mIBics, + (bics) -> { + try { + return bics.getBackgroundInstalledPackages( + PackageManager.MATCH_ALL, Process.myUserHandle() + .getIdentifier()); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + }, + QUERY_ALL_PACKAGES); assertThat(slice).isNotNull(); var packageList = slice.getList(); assertThat(packageList).isNotNull(); assertThat(packageList).hasSize(2); - var expectedPackageNames = Set.of("com.android.servicestests.apps.bicmockapp1", - "com.android.servicestests.apps.bicmockapp2"); - var actualPackageNames = packageList.stream().map((packageInfo) -> packageInfo.packageName) - .collect(Collectors.toSet()); + var expectedPackageNames = + Set.of( + "com.android.servicestests.apps.bicmockapp1", + "com.android.servicestests.apps.bicmockapp2"); + var actualPackageNames = + packageList.stream() + .map((packageInfo) -> packageInfo.packageName) + .collect(Collectors.toSet()); assertThat(actualPackageNames).containsExactlyElementsIn(expectedPackageNames); } + + @Test + public void testRegisterBackgroundInstallControlCallback() + throws Exception { + String testPackageName = "test"; + int testUserId = 1; + ArrayList<Pair<String, Integer>> sharedResource = new ArrayList<>(); + IRemoteCallback testCallback = + new IRemoteCallback.Stub() { + private final ArrayList<Pair<String, Integer>> mArray = sharedResource; + + @Override + public void sendResult(Bundle data) throws RemoteException { + mArray.add(new Pair(testPackageName, testUserId)); + } + }; + ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( + mIBics, + (bics) -> { + try { + bics.registerBackgroundInstallCallback(testCallback); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + }, + QUERY_ALL_PACKAGES, + INTERACT_ACROSS_USERS_FULL); + installPackage(TEST_DATA_DIR + MOCK_APK_FILE, MOCK_PACKAGE_NAME); + + assertUntil(() -> sharedResource.size() == 1, 2000); + assertThat(sharedResource.get(0).first).isEqualTo(testPackageName); + assertThat(sharedResource.get(0).second).isEqualTo(testUserId); + } + + @Test + public void testUnregisterBackgroundInstallControlCallback() { + String testValue = "test"; + ArrayList<String> sharedResource = new ArrayList<>(); + IRemoteCallback testCallback = + new IRemoteCallback.Stub() { + private final ArrayList<String> mArray = sharedResource; + + @Override + public void sendResult(Bundle data) throws RemoteException { + mArray.add(testValue); + } + }; + ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( + mIBics, + (bics) -> { + try { + bics.registerBackgroundInstallCallback(testCallback); + bics.unregisterBackgroundInstallCallback(testCallback); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + }, + QUERY_ALL_PACKAGES, + INTERACT_ACROSS_USERS_FULL); + installPackage(TEST_DATA_DIR + MOCK_APK_FILE, MOCK_PACKAGE_NAME); + + assertUntil(() -> sharedResource.isEmpty(), 2000); + } + + private static boolean installPackage(String apkPath, String packageName) { + Context context = InstrumentationRegistry.getInstrumentation().getContext(); + final CountDownLatch installLatch = new CountDownLatch(1); + final BroadcastReceiver installReceiver = + new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + int packageInstallStatus = + intent.getIntExtra( + PackageInstaller.EXTRA_STATUS, + PackageInstaller.STATUS_FAILURE_INVALID); + if (packageInstallStatus == PackageInstaller.STATUS_SUCCESS) { + installLatch.countDown(); + } + } + }; + final IntentFilter intentFilter = new IntentFilter(ACTION_INSTALL_COMMIT); + context.registerReceiver(installReceiver, intentFilter, Context.RECEIVER_EXPORTED); + + PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller(); + PackageInstaller.SessionParams params = + new PackageInstaller.SessionParams( + PackageInstaller.SessionParams.MODE_FULL_INSTALL); + params.setRequireUserAction(PackageInstaller.SessionParams.USER_ACTION_NOT_REQUIRED); + try { + int sessionId = packageInstaller.createSession(params); + PackageInstaller.Session session = packageInstaller.openSession(sessionId); + OutputStream out = session.openWrite(packageName, 0, -1); + FileInputStream fis = new FileInputStream(apkPath); + byte[] buffer = new byte[65536]; + int size; + while ((size = fis.read(buffer)) != -1) { + out.write(buffer, 0, size); + } + session.fsync(out); + fis.close(); + out.close(); + + runWithShellPermissionIdentity( + () -> { + session.commit(createPendingIntent(context).getIntentSender()); + installLatch.await(5, TimeUnit.SECONDS); + }); + return true; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static PendingIntent createPendingIntent(Context context) { + PendingIntent pendingIntent = + PendingIntent.getBroadcast( + context, + 1, + new Intent(ACTION_INSTALL_COMMIT) + .setPackage( + BackgroundInstallControlServiceTest.class.getPackageName()), + PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE); + return pendingIntent; + } + + private static void runWithShellPermissionIdentity(@NonNull ThrowingRunnable command) + throws Exception { + InstrumentationRegistry.getInstrumentation() + .getUiAutomation() + .adoptShellPermissionIdentity(); + try { + command.run(); + } finally { + InstrumentationRegistry.getInstrumentation() + .getUiAutomation() + .dropShellPermissionIdentity(); + } + } + + private static void assertUntil(Supplier<Boolean> condition, int timeoutMs) { + long endTime = System.currentTimeMillis() + timeoutMs; + while (System.currentTimeMillis() <= endTime) { + if (condition.get()) return; + try { + Thread.sleep(10); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + assertThat(condition.get()).isTrue(); + } } diff --git a/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/Android.bp b/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/Android.bp index 7804f4ce9d02..39b0ff782b72 100644 --- a/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/Android.bp +++ b/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/Android.bp @@ -50,3 +50,11 @@ android_test_helper_app { "--rename-manifest-package com.android.servicestests.apps.bicmockapp2", ], } + +android_test_helper_app { + name: "BackgroundInstallControlMockApp3", + defaults: ["bic-mock-app-defaults"], + aaptflags: [ + "--rename-manifest-package com.android.servicestests.apps.bicmockapp3", + ], +} diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java index e5be4d9aa755..9e11fa2f0bdf 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java @@ -50,7 +50,7 @@ import java.nio.charset.Charset; import java.util.Arrays; import java.util.Collections; -// atest PackageManagerServiceTest:com.android.server.pm.UserDataPreparerTest +// atest PackageManagerServiceServerTests:com.android.server.pm.UserDataPreparerTest @RunWith(AndroidJUnit4.class) @Presubmit @SmallTest @@ -99,7 +99,7 @@ public class UserDataPreparerTest { systemDeDir.mkdirs(); mUserDataPreparer.prepareUserData(TEST_USER, StorageManager.FLAG_STORAGE_DE); verify(mStorageManagerMock).prepareUserStorage(isNull(String.class), eq(TEST_USER_ID), - eq(TEST_USER_SERIAL), eq(StorageManager.FLAG_STORAGE_DE)); + eq(StorageManager.FLAG_STORAGE_DE)); verify(mInstaller).createUserData(isNull(String.class), eq(TEST_USER_ID), eq(TEST_USER_SERIAL), eq(StorageManager.FLAG_STORAGE_DE)); int serialNumber = UserDataPreparer.getSerialNumber(userDeDir); @@ -116,7 +116,7 @@ public class UserDataPreparerTest { systemCeDir.mkdirs(); mUserDataPreparer.prepareUserData(TEST_USER, StorageManager.FLAG_STORAGE_CE); verify(mStorageManagerMock).prepareUserStorage(isNull(String.class), eq(TEST_USER_ID), - eq(TEST_USER_SERIAL), eq(StorageManager.FLAG_STORAGE_CE)); + eq(StorageManager.FLAG_STORAGE_CE)); verify(mInstaller).createUserData(isNull(String.class), eq(TEST_USER_ID), eq(TEST_USER_SERIAL), eq(StorageManager.FLAG_STORAGE_CE)); int serialNumber = UserDataPreparer.getSerialNumber(userCeDir); @@ -129,7 +129,7 @@ public class UserDataPreparerTest { public void testPrepareUserData_forNewUser_destroysOnFailure() throws Exception { TEST_USER.lastLoggedInTime = 0; doThrow(new IllegalStateException("expected exception for test")).when(mStorageManagerMock) - .prepareUserStorage(isNull(String.class), eq(TEST_USER_ID), eq(TEST_USER_SERIAL), + .prepareUserStorage(isNull(String.class), eq(TEST_USER_ID), eq(StorageManager.FLAG_STORAGE_CE)); mUserDataPreparer.prepareUserData(TEST_USER, StorageManager.FLAG_STORAGE_CE); verify(mStorageManagerMock).destroyUserStorage(isNull(String.class), eq(TEST_USER_ID), @@ -140,7 +140,7 @@ public class UserDataPreparerTest { public void testPrepareUserData_forExistingUser_doesNotDestroyOnFailure() throws Exception { TEST_USER.lastLoggedInTime = System.currentTimeMillis(); doThrow(new IllegalStateException("expected exception for test")).when(mStorageManagerMock) - .prepareUserStorage(isNull(String.class), eq(TEST_USER_ID), eq(TEST_USER_SERIAL), + .prepareUserStorage(isNull(String.class), eq(TEST_USER_ID), eq(StorageManager.FLAG_STORAGE_CE)); mUserDataPreparer.prepareUserData(TEST_USER, StorageManager.FLAG_STORAGE_CE); verify(mStorageManagerMock, never()).destroyUserStorage(isNull(String.class), diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java deleted file mode 100644 index 4cc68cf11e69..000000000000 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java +++ /dev/null @@ -1,1971 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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.display; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; -import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; -import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE; -import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE; - -import static org.junit.Assert.assertNotNull; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyFloat; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isA; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.when; - -import android.app.ActivityManager; -import android.content.Context; -import android.content.res.Resources; -import android.hardware.Sensor; -import android.hardware.SensorEventListener; -import android.hardware.SensorManager; -import android.hardware.display.BrightnessInfo; -import android.hardware.display.DisplayManagerInternal; -import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks; -import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.PowerManager; -import android.os.SystemProperties; -import android.os.UserHandle; -import android.os.test.TestLooper; -import android.platform.test.annotations.RequiresFlagsDisabled; -import android.platform.test.annotations.RequiresFlagsEnabled; -import android.platform.test.flag.junit.CheckFlagsRule; -import android.platform.test.flag.junit.DeviceFlagsValueProvider; -import android.provider.Settings; -import android.testing.TestableContext; -import android.util.FloatProperty; -import android.util.SparseArray; -import android.view.Display; -import android.view.DisplayInfo; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; -import androidx.test.platform.app.InstrumentationRegistry; - -import com.android.modules.utils.testing.ExtendedMockitoRule; -import com.android.server.LocalServices; -import com.android.server.am.BatteryStatsService; -import com.android.server.display.RampAnimator.DualRampAnimator; -import com.android.server.display.brightness.BrightnessEvent; -import com.android.server.display.brightness.clamper.BrightnessClamperController; -import com.android.server.display.brightness.clamper.HdrClamper; -import com.android.server.display.color.ColorDisplayService; -import com.android.server.display.config.SensorData; -import com.android.server.display.feature.DisplayManagerFlags; -import com.android.server.display.feature.flags.Flags; -import com.android.server.display.layout.Layout; -import com.android.server.display.whitebalance.DisplayWhiteBalanceController; -import com.android.server.policy.WindowManagerPolicy; -import com.android.server.testutils.OffsettableClock; - -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.quality.Strictness; -import org.mockito.stubbing.Answer; - -import java.util.List; - - -@SmallTest -@RunWith(AndroidJUnit4.class) -public final class DisplayPowerController2Test { - private static final int DISPLAY_ID = Display.DEFAULT_DISPLAY; - private static final String UNIQUE_ID = "unique_id_test123"; - private static final int FOLLOWER_DISPLAY_ID = DISPLAY_ID + 1; - private static final String FOLLOWER_UNIQUE_ID = "unique_id_456"; - private static final int SECOND_FOLLOWER_DISPLAY_ID = FOLLOWER_DISPLAY_ID + 1; - private static final String SECOND_FOLLOWER_UNIQUE_DISPLAY_ID = "unique_id_789"; - private static final float PROX_SENSOR_MAX_RANGE = 5; - private static final float BRIGHTNESS_RAMP_RATE_MINIMUM = 0.0f; - private static final float BRIGHTNESS_RAMP_RATE_FAST_DECREASE = 0.3f; - private static final float BRIGHTNESS_RAMP_RATE_FAST_INCREASE = 0.4f; - private static final float BRIGHTNESS_RAMP_RATE_SLOW_DECREASE = 0.1f; - private static final float BRIGHTNESS_RAMP_RATE_SLOW_INCREASE = 0.2f; - private static final float BRIGHTNESS_RAMP_RATE_SLOW_INCREASE_IDLE = 0.5f; - private static final float BRIGHTNESS_RAMP_RATE_SLOW_DECREASE_IDLE = 0.6f; - - private static final long BRIGHTNESS_RAMP_INCREASE_MAX = 1000; - private static final long BRIGHTNESS_RAMP_DECREASE_MAX = 2000; - private static final long BRIGHTNESS_RAMP_INCREASE_MAX_IDLE = 3000; - private static final long BRIGHTNESS_RAMP_DECREASE_MAX_IDLE = 4000; - - private OffsettableClock mClock; - private TestLooper mTestLooper; - private Handler mHandler; - private DisplayPowerControllerHolder mHolder; - private Sensor mProxSensor; - - @Mock - private DisplayPowerCallbacks mDisplayPowerCallbacksMock; - @Mock - private SensorManager mSensorManagerMock; - @Mock - private DisplayBlanker mDisplayBlankerMock; - @Mock - private BrightnessTracker mBrightnessTrackerMock; - @Mock - private WindowManagerPolicy mWindowManagerPolicyMock; - @Mock - private PowerManager mPowerManagerMock; - @Mock - private ColorDisplayService.ColorDisplayServiceInternal mCdsiMock; - @Mock - private DisplayWhiteBalanceController mDisplayWhiteBalanceControllerMock; - @Mock - private DisplayManagerFlags mDisplayManagerFlagsMock; - @Mock - private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession; - @Captor - private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor; - - @Rule - public final TestableContext mContext = new TestableContext( - InstrumentationRegistry.getInstrumentation().getContext()); - - @Rule - public final ExtendedMockitoRule mExtendedMockitoRule = - new ExtendedMockitoRule.Builder(this) - .setStrictness(Strictness.LENIENT) - .spyStatic(SystemProperties.class) - .spyStatic(BatteryStatsService.class) - .spyStatic(ActivityManager.class) - .build(); - - @Rule - public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); - - @Before - public void setUp() throws Exception { - mClock = new OffsettableClock.Stopped(); - mTestLooper = new TestLooper(mClock::now); - mHandler = new Handler(mTestLooper.getLooper()); - - // Set some settings to minimize unexpected events and have a consistent starting state - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL); - Settings.System.putFloatForUser(mContext.getContentResolver(), - Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0, UserHandle.USER_CURRENT); - - addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock); - addLocalServiceMock(ColorDisplayService.ColorDisplayServiceInternal.class, - mCdsiMock); - - mContext.addMockSystemService(PowerManager.class, mPowerManagerMock); - - mContext.getOrCreateTestableResources().addOverride( - com.android.internal.R.bool.config_displayColorFadeDisabled, false); - - doAnswer((Answer<Void>) invocationOnMock -> null).when(() -> - SystemProperties.set(anyString(), any())); - doAnswer((Answer<Void>) invocationOnMock -> null).when(BatteryStatsService::getService); - doAnswer((Answer<Boolean>) invocationOnMock -> false) - .when(ActivityManager::isLowRamDeviceStatic); - - setUpSensors(); - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); - } - - @After - public void tearDown() { - LocalServices.removeServiceForTest(WindowManagerPolicy.class); - LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class); - } - - @Test - public void testReleaseProxSuspendBlockersOnExit() throws Exception { - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - // Send a display power request - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - dpr.useProximitySensor = true; - mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */); - - // Run updatePowerState to start listener for the prox sensor - advanceTime(1); - - SensorEventListener listener = getSensorEventListener(mProxSensor); - assertNotNull(listener); - - listener.onSensorChanged(TestUtils.createSensorEvent(mProxSensor, /* value= */ 5)); - advanceTime(1); - - // two times, one for unfinished business and one for proximity - verify(mHolder.wakelockController, times(2)).acquireWakelock( - WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS); - verify(mHolder.wakelockController).acquireWakelock( - WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE); - - mHolder.dpc.stop(); - advanceTime(1); - // two times, one for unfinished business and one for proximity - verify(mHolder.wakelockController, times(2)).acquireWakelock( - WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS); - verify(mHolder.wakelockController).acquireWakelock( - WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE); - } - - @Test - public void testScreenOffBecauseOfProximity() throws Exception { - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - // Send a display power request - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - dpr.useProximitySensor = true; - mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */); - - // Run updatePowerState to start listener for the prox sensor - advanceTime(1); - - SensorEventListener listener = getSensorEventListener(mProxSensor); - assertNotNull(listener); - - // Send a positive proximity event - listener.onSensorChanged(TestUtils.createSensorEvent(mProxSensor, /* value= */ 1)); - advanceTime(1); - - // The display should have been turned off - verify(mHolder.displayPowerState).setScreenState(Display.STATE_OFF); - - clearInvocations(mHolder.displayPowerState); - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_OFF); - // Send a negative proximity event - listener.onSensorChanged(TestUtils.createSensorEvent(mProxSensor, - (int) PROX_SENSOR_MAX_RANGE + 1)); - // Advance time by less than PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY - advanceTime(1); - - // The prox sensor is debounced so the display should not have been turned back on yet - verify(mHolder.displayPowerState, never()).setScreenState(Display.STATE_ON); - - // Advance time by more than PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY - advanceTime(1000); - - // The display should have been turned back on - verify(mHolder.displayPowerState).setScreenState(Display.STATE_ON); - } - - @Test - public void testScreenOffBecauseOfProximity_ProxSensorGone() throws Exception { - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - // Send a display power request - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - dpr.useProximitySensor = true; - mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */); - - // Run updatePowerState to start listener for the prox sensor - advanceTime(1); - - SensorEventListener listener = getSensorEventListener(mProxSensor); - assertNotNull(listener); - - // Send a positive proximity event - listener.onSensorChanged(TestUtils.createSensorEvent(mProxSensor, /* value= */ 1)); - advanceTime(1); - - // The display should have been turned off - verify(mHolder.displayPowerState).setScreenState(Display.STATE_OFF); - - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_OFF); - // The display device changes and we no longer have a prox sensor - reset(mSensorManagerMock); - setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class), - mock(DisplayDeviceConfig.class), /* isEnabled= */ true); - mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, Layout.NO_LEAD_DISPLAY); - - advanceTime(1); // Run updatePowerState - - // The display should have been turned back on and the listener should have been - // unregistered - verify(mHolder.displayPowerState).setScreenState(Display.STATE_ON); - verify(mSensorManagerMock).unregisterListener(listener); - } - - @Test - public void testProximitySensorListenerNotRegisteredForNonDefaultDisplay() { - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - // send a display power request - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - dpr.useProximitySensor = true; - final DisplayPowerControllerHolder followerDpc = - createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); - followerDpc.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */); - - // Run updatePowerState - advanceTime(1); - - verify(mSensorManagerMock, never()).registerListener(any(SensorEventListener.class), - eq(mProxSensor), anyInt(), any(Handler.class)); - } - - @Test - public void testDisplayBrightnessFollowers_BothDpcsSupportNits() { - DisplayPowerControllerHolder followerDpc = - createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - followerDpc.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor = - ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); - verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture()); - BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue(); - - mHolder.dpc.addDisplayBrightnessFollower(followerDpc.dpc); - - // Test different float scale values - float leadBrightness = 0.3f; - float followerBrightness = 0.4f; - float nits = 300; - when(mHolder.automaticBrightnessController.convertToNits(leadBrightness)).thenReturn(nits); - when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) - .thenReturn(followerBrightness); - when(mHolder.brightnessSetting.getBrightness()).thenReturn(leadBrightness); - listener.onBrightnessChanged(leadBrightness); - advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(), anyFloat(), eq(false)); - verify(followerDpc.animator).animateTo(eq(followerBrightness), anyFloat(), - anyFloat(), eq(false)); - - clearInvocations(mHolder.animator, followerDpc.animator); - - // Test the same float scale value - float brightness = 0.6f; - nits = 600; - when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits); - when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) - .thenReturn(brightness); - when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); - listener.onBrightnessChanged(brightness); - advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - } - - @Test - public void testDisplayBrightnessFollowers_FollowerDoesNotSupportNits() { - DisplayPowerControllerHolder followerDpc = - createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - followerDpc.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor = - ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); - verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture()); - BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue(); - - mHolder.dpc.addDisplayBrightnessFollower(followerDpc.dpc); - - float brightness = 0.3f; - when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(300f); - when(followerDpc.automaticBrightnessController.getBrightnessFromNits(anyFloat())) - .thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT); - when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); - listener.onBrightnessChanged(brightness); - advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - } - - @Test - public void testDisplayBrightnessFollowers_LeadDpcDoesNotSupportNits() { - DisplayPowerControllerHolder followerDpc = - createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - followerDpc.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor = - ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); - verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture()); - BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue(); - - mHolder.dpc.addDisplayBrightnessFollower(followerDpc.dpc); - - float brightness = 0.3f; - when(mHolder.automaticBrightnessController.convertToNits(anyFloat())).thenReturn(-1f); - when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); - listener.onBrightnessChanged(brightness); - advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - } - - @Test - public void testDisplayBrightnessFollowers_NeitherDpcSupportsNits() { - DisplayPowerControllerHolder followerDpc = - createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - followerDpc.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor = - ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); - verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture()); - BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue(); - - mHolder.dpc.addDisplayBrightnessFollower(followerDpc.dpc); - - float brightness = 0.3f; - when(mHolder.automaticBrightnessController.convertToNits(anyFloat())).thenReturn(-1f); - when(followerDpc.automaticBrightnessController.getBrightnessFromNits(anyFloat())) - .thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT); - when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); - listener.onBrightnessChanged(brightness); - advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - } - - @Test - public void testDisplayBrightnessFollowers_AutomaticBrightness() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - DisplayPowerControllerHolder followerDpc = - createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - float leadBrightness = 0.1f; - float rawLeadBrightness = 0.3f; - float followerBrightness = 0.4f; - float nits = 300; - float ambientLux = 3000; - when(mHolder.automaticBrightnessController.getRawAutomaticScreenBrightness()) - .thenReturn(rawLeadBrightness); - when(mHolder.automaticBrightnessController - .getAutomaticScreenBrightness(any(BrightnessEvent.class))) - .thenReturn(leadBrightness); - when(mHolder.automaticBrightnessController.convertToNits(rawLeadBrightness)) - .thenReturn(nits); - when(mHolder.automaticBrightnessController.getAmbientLux()).thenReturn(ambientLux); - when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) - .thenReturn(followerBrightness); - - mHolder.dpc.addDisplayBrightnessFollower(followerDpc.dpc); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - followerDpc.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - // One triggered by handleBrightnessModeChange, another triggered by setBrightnessToFollow - verify(followerDpc.hbmController, times(2)).onAmbientLuxChange(ambientLux); - verify(followerDpc.animator, times(2)).animateTo(eq(followerBrightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(leadBrightness); - when(followerDpc.displayPowerState.getScreenBrightness()).thenReturn(followerBrightness); - clearInvocations(mHolder.animator, followerDpc.animator); - - leadBrightness = 0.05f; - rawLeadBrightness = 0.2f; - followerBrightness = 0.3f; - nits = 200; - ambientLux = 2000; - when(mHolder.automaticBrightnessController.getRawAutomaticScreenBrightness()) - .thenReturn(rawLeadBrightness); - when(mHolder.automaticBrightnessController - .getAutomaticScreenBrightness(any(BrightnessEvent.class))) - .thenReturn(leadBrightness); - when(mHolder.automaticBrightnessController.convertToNits(rawLeadBrightness)) - .thenReturn(nits); - when(mHolder.automaticBrightnessController.getAmbientLux()).thenReturn(ambientLux); - when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) - .thenReturn(followerBrightness); - - mHolder.dpc.updateBrightness(); - advanceTime(1); // Run updatePowerState - - // The second time, the animation rate should be slow - verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE), eq(false)); - verify(followerDpc.hbmController).onAmbientLuxChange(ambientLux); - verify(followerDpc.animator).animateTo(eq(followerBrightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE), eq(false)); - } - - @Test - public void testDisplayBrightnessFollowersRemoval_RemoveSingleFollower() { - DisplayPowerControllerHolder followerDpc = createDisplayPowerController(FOLLOWER_DISPLAY_ID, - FOLLOWER_UNIQUE_ID); - DisplayPowerControllerHolder secondFollowerDpc = createDisplayPowerController( - SECOND_FOLLOWER_DISPLAY_ID, SECOND_FOLLOWER_UNIQUE_DISPLAY_ID); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(secondFollowerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - followerDpc.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - secondFollowerDpc.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor = - ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); - verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture()); - BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue(); - - // Set the initial brightness on the DPC we're going to remove so we have a fixed value for - // it to return to. - listenerCaptor = ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); - verify(followerDpc.brightnessSetting).registerListener(listenerCaptor.capture()); - BrightnessSetting.BrightnessSettingListener followerListener = listenerCaptor.getValue(); - final float initialFollowerBrightness = 0.3f; - when(followerDpc.brightnessSetting.getBrightness()).thenReturn(initialFollowerBrightness); - followerListener.onBrightnessChanged(initialFollowerBrightness); - advanceTime(1); - verify(followerDpc.animator).animateTo(eq(initialFollowerBrightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - - when(followerDpc.displayPowerState.getScreenBrightness()) - .thenReturn(initialFollowerBrightness); - - mHolder.dpc.addDisplayBrightnessFollower(followerDpc.dpc); - mHolder.dpc.addDisplayBrightnessFollower(secondFollowerDpc.dpc); - clearInvocations(followerDpc.animator); - - // Validate both followers are correctly registered and receiving brightness updates - float brightness = 0.6f; - float nits = 600; - when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits); - when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) - .thenReturn(brightness); - when(secondFollowerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) - .thenReturn(brightness); - when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); - listener.onBrightnessChanged(brightness); - advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - verify(secondFollowerDpc.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness); - when(followerDpc.displayPowerState.getScreenBrightness()).thenReturn(brightness); - when(secondFollowerDpc.displayPowerState.getScreenBrightness()).thenReturn(brightness); - clearInvocations(mHolder.animator, followerDpc.animator, secondFollowerDpc.animator); - - // Remove the first follower and validate it goes back to its original brightness. - mHolder.dpc.removeDisplayBrightnessFollower(followerDpc.dpc); - advanceTime(1); - verify(followerDpc.animator).animateTo(eq(initialFollowerBrightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), eq(false)); - - when(followerDpc.displayPowerState.getScreenBrightness()) - .thenReturn(initialFollowerBrightness); - clearInvocations(followerDpc.animator); - - // Change the brightness of the lead display and validate only the second follower responds - brightness = 0.7f; - nits = 700; - when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits); - when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) - .thenReturn(brightness); - when(secondFollowerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) - .thenReturn(brightness); - when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); - listener.onBrightnessChanged(brightness); - advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - verify(followerDpc.animator, never()).animateTo(anyFloat(), anyFloat(), anyFloat(), - anyBoolean()); - verify(secondFollowerDpc.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - } - - @Test - public void testDisplayBrightnessFollowersRemoval_RemoveAllFollowers() { - DisplayPowerControllerHolder followerHolder = - createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); - DisplayPowerControllerHolder secondFollowerHolder = - createDisplayPowerController(SECOND_FOLLOWER_DISPLAY_ID, - SECOND_FOLLOWER_UNIQUE_DISPLAY_ID); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(followerHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(secondFollowerHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - followerHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - secondFollowerHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor = - ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); - verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture()); - BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue(); - - // Set the initial brightness on the DPCs we're going to remove so we have a fixed value for - // it to return to. - listenerCaptor = ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); - verify(followerHolder.brightnessSetting).registerListener(listenerCaptor.capture()); - BrightnessSetting.BrightnessSettingListener followerListener = listenerCaptor.getValue(); - listenerCaptor = ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); - verify(secondFollowerHolder.brightnessSetting).registerListener(listenerCaptor.capture()); - BrightnessSetting.BrightnessSettingListener secondFollowerListener = - listenerCaptor.getValue(); - final float initialFollowerBrightness = 0.3f; - when(followerHolder.brightnessSetting.getBrightness()).thenReturn( - initialFollowerBrightness); - when(secondFollowerHolder.brightnessSetting.getBrightness()).thenReturn( - initialFollowerBrightness); - followerListener.onBrightnessChanged(initialFollowerBrightness); - secondFollowerListener.onBrightnessChanged(initialFollowerBrightness); - advanceTime(1); - verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - - when(followerHolder.displayPowerState.getScreenBrightness()) - .thenReturn(initialFollowerBrightness); - when(secondFollowerHolder.displayPowerState.getScreenBrightness()) - .thenReturn(initialFollowerBrightness); - - mHolder.dpc.addDisplayBrightnessFollower(followerHolder.dpc); - mHolder.dpc.addDisplayBrightnessFollower(secondFollowerHolder.dpc); - clearInvocations(followerHolder.animator, secondFollowerHolder.animator); - - // Validate both followers are correctly registered and receiving brightness updates - float brightness = 0.6f; - float nits = 600; - when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits); - when(followerHolder.automaticBrightnessController.getBrightnessFromNits(nits)) - .thenReturn(brightness); - when(secondFollowerHolder.automaticBrightnessController.getBrightnessFromNits(nits)) - .thenReturn(brightness); - when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); - listener.onBrightnessChanged(brightness); - advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - verify(followerHolder.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - verify(secondFollowerHolder.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness); - when(followerHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness); - when(secondFollowerHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness); - clearInvocations(mHolder.animator, followerHolder.animator, secondFollowerHolder.animator); - - // Stop the lead DPC and validate that the followers go back to their original brightness. - mHolder.dpc.stop(); - advanceTime(1); - verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), eq(false)); - verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), eq(false)); - clearInvocations(followerHolder.animator, secondFollowerHolder.animator); - } - - @Test - @RequiresFlagsEnabled(Flags.FLAG_FAST_HDR_TRANSITIONS) - public void testDisplayBrightnessHdr_SkipAnimationOnHdrAppearance() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - final float sdrBrightness = 0.1f; - final float hdrBrightness = 0.3f; - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( - any(BrightnessEvent.class))).thenReturn(sdrBrightness); - when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(1.0f); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(sdrBrightness); - when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness); - - when(mHolder.hbmController.getHighBrightnessMode()).thenReturn( - BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR); - when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness); - clearInvocations(mHolder.animator); - - mHolder.dpc.updateBrightness(); - advanceTime(1); // Run updatePowerState - - verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness), - eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false)); - } - - @Test - @RequiresFlagsEnabled(Flags.FLAG_FAST_HDR_TRANSITIONS) - public void testDisplayBrightnessHdr_SkipAnimationOnHdrRemoval() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - final float sdrBrightness = 0.1f; - final float hdrBrightness = 0.3f; - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(mHolder.automaticBrightnessController.isInIdleMode()).thenReturn(true); - when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( - any(BrightnessEvent.class))).thenReturn(sdrBrightness); - when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(1.0f); - - when(mHolder.hbmController.getHighBrightnessMode()).thenReturn( - BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR); - when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(hdrBrightness); - when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness); - when(mHolder.hbmController.getHighBrightnessMode()).thenReturn( - BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF); - - clearInvocations(mHolder.animator); - - mHolder.dpc.updateBrightness(); - advanceTime(1); // Run updatePowerState - - verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness), - eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false)); - } - - @Test - public void testDoesNotSetScreenStateForNonDefaultDisplayUntilBootCompleted() { - // We should still set screen state for the default display - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - verify(mHolder.displayPowerState, times(2)).setScreenState(anyInt()); - - mHolder = createDisplayPowerController(42, UNIQUE_ID); - - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - verify(mHolder.displayPowerState, never()).setScreenState(anyInt()); - - mHolder.dpc.onBootCompleted(); - advanceTime(1); // Run updatePowerState - verify(mHolder.displayPowerState).setScreenState(anyInt()); - } - - @Test - public void testSetScreenOffBrightnessSensorEnabled_DisplayIsOff() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_OFF); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_OFF; - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.screenOffBrightnessSensorController, atLeastOnce()) - .setLightSensorEnabled(true); - - // The display turns on and we use the brightness value recommended by - // ScreenOffBrightnessSensorController - clearInvocations(mHolder.screenOffBrightnessSensorController); - float brightness = 0.14f; - when(mHolder.screenOffBrightnessSensorController.getAutomaticScreenBrightness()) - .thenReturn(brightness); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( - any(BrightnessEvent.class))).thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT); - - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.screenOffBrightnessSensorController, atLeastOnce()) - .getAutomaticScreenBrightness(); - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat(), eq(false)); - } - - @Test - public void testSetScreenOffBrightnessSensorEnabled_DisplayIsInDoze() { - mContext.getOrCreateTestableResources().addOverride( - com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, false); - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); - - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_DOZE; - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.screenOffBrightnessSensorController, atLeastOnce()) - .setLightSensorEnabled(true); - - // The display turns on and we use the brightness value recommended by - // ScreenOffBrightnessSensorController - clearInvocations(mHolder.screenOffBrightnessSensorController); - float brightness = 0.14f; - when(mHolder.screenOffBrightnessSensorController.getAutomaticScreenBrightness()) - .thenReturn(brightness); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( - any(BrightnessEvent.class))).thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT); - - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.screenOffBrightnessSensorController, atLeastOnce()) - .getAutomaticScreenBrightness(); - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat(), eq(false)); - } - - @Test - public void testSetScreenOffBrightnessSensorDisabled_AutoBrightnessIsDisabled() { - // Tests are set up with manual brightness by default, so no need to set it here. - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_OFF; - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.screenOffBrightnessSensorController, atLeastOnce()) - .setLightSensorEnabled(false); - } - - @Test - public void testSetScreenOffBrightnessSensorDisabled_DisplayIsDisabled() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ false); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.screenOffBrightnessSensorController, atLeastOnce()) - .setLightSensorEnabled(false); - } - - @Test - public void testSetScreenOffBrightnessSensorDisabled_DisplayIsOn() { - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.screenOffBrightnessSensorController, atLeastOnce()) - .setLightSensorEnabled(false); - } - - @Test - public void testSetScreenOffBrightnessSensorDisabled_DisplayIsAFollower() { - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_OFF; - - mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, /* leadDisplayId= */ 42); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.screenOffBrightnessSensorController, atLeastOnce()) - .setLightSensorEnabled(false); - } - - @Test - public void testStopScreenOffBrightnessSensorControllerWhenDisplayDeviceChanges() { - // New display device - setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class), - mock(DisplayDeviceConfig.class), /* isEnabled= */ true); - - mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, Layout.NO_LEAD_DISPLAY); - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.screenOffBrightnessSensorController).stop(); - } - - @Test - public void testAutoBrightnessEnabled_DisplayIsOn() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.automaticBrightnessController).configure( - AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED, - /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, - /* userChangedBrightness= */ false, /* adjustment= */ 0, - /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_BRIGHT, - /* shouldResetShortTermModel= */ false - ); - verify(mHolder.hbmController) - .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED); - } - - @Test - public void testAutoBrightnessEnabled_DisplayIsInDoze() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - mContext.getOrCreateTestableResources().addOverride( - com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, true); - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_DOZE; - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.automaticBrightnessController).configure( - AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED, - /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, - /* userChangedBrightness= */ false, /* adjustment= */ 0, - /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_DOZE, - /* shouldResetShortTermModel= */ false - ); - verify(mHolder.hbmController) - .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED); - } - - @Test - public void testAutoBrightnessDisabled_ManualBrightnessMode() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - // One triggered by the test, the other by handleBrightnessModeChange - verify(mHolder.automaticBrightnessController, times(2)).configure( - AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, - /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, - /* userChangedBrightness= */ false, /* adjustment= */ 0, - /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_BRIGHT, - /* shouldResetShortTermModel= */ false - ); - verify(mHolder.hbmController, times(2)) - .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED); - } - - @Test - public void testAutoBrightnessDisabled_DisplayIsOff() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_OFF; - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_OFF); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.automaticBrightnessController).configure( - AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE, - /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, - /* userChangedBrightness= */ false, /* adjustment= */ 0, - /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_OFF, - /* shouldResetShortTermModel= */ false - ); - verify(mHolder.hbmController).setAutoBrightnessEnabled( - AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE); - } - - @Test - public void testAutoBrightnessDisabled_DisplayIsInDoze() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - mContext.getOrCreateTestableResources().addOverride( - com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, false); - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_DOZE; - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.automaticBrightnessController).configure( - AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE, - /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, - /* userChangedBrightness= */ false, /* adjustment= */ 0, - /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_DOZE, - /* shouldResetShortTermModel= */ false - ); - verify(mHolder.hbmController).setAutoBrightnessEnabled( - AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE); - } - - @Test - public void testAutoBrightnessDisabled_FollowerDisplay() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - mHolder.dpc.setBrightnessToFollow(0.3f, -1, 0, /* slowChange= */ false); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - // One triggered by the test, the other by handleBrightnessModeChange - verify(mHolder.automaticBrightnessController, times(2)).configure( - AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, - /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, - /* userChangedBrightness= */ false, /* adjustment= */ 0, - /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_BRIGHT, - /* shouldResetShortTermModel= */ false - ); - - // HBM should be allowed for the follower display - verify(mHolder.hbmController) - .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED); - } - - @Test - public void testBrightnessNitsPersistWhenDisplayDeviceChanges() { - float brightness = 0.3f; - float nits = 500; - mContext.getOrCreateTestableResources().addOverride( - com.android.internal.R.bool.config_persistBrightnessNitsForDefaultDisplay, - true); - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); - when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits); - - mHolder.dpc.setBrightness(brightness); - verify(mHolder.brightnessSetting).setBrightnessNitsForDefaultDisplay(nits); - - float newBrightness = 0.4f; - when(mHolder.brightnessSetting.getBrightnessNitsForDefaultDisplay()).thenReturn(nits); - when(mHolder.automaticBrightnessController.getBrightnessFromNits(nits)) - .thenReturn(newBrightness); - // New display device - setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class), - mock(DisplayDeviceConfig.class), /* isEnabled= */ true); - mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, Layout.NO_LEAD_DISPLAY); - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - // One triggered by handleBrightnessModeChange, another triggered by onDisplayChanged - verify(mHolder.animator, times(2)).animateTo(eq(newBrightness), anyFloat(), anyFloat(), - eq(false)); - } - - @Test - public void testShortTermModelPersistsWhenDisplayDeviceChanges() { - float lux = 2000; - float nits = 500; - when(mHolder.automaticBrightnessController.getUserLux()).thenReturn(lux); - when(mHolder.automaticBrightnessController.getUserNits()).thenReturn(nits); - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); - clearInvocations(mHolder.injector); - - // New display device - setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class), - mock(DisplayDeviceConfig.class), /* isEnabled= */ true); - mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, Layout.NO_LEAD_DISPLAY); - advanceTime(1); - - verify(mHolder.injector).getAutomaticBrightnessController( - any(AutomaticBrightnessController.Callbacks.class), - any(Looper.class), - eq(mSensorManagerMock), - /* lightSensor= */ any(), - /* brightnessMappingStrategyMap= */ any(SparseArray.class), - /* lightSensorWarmUpTime= */ anyInt(), - /* brightnessMin= */ anyFloat(), - /* brightnessMax= */ anyFloat(), - /* dozeScaleFactor */ anyFloat(), - /* lightSensorRate= */ anyInt(), - /* initialLightSensorRate= */ anyInt(), - /* brighteningLightDebounceConfig */ anyLong(), - /* darkeningLightDebounceConfig */ anyLong(), - /* brighteningLightDebounceConfigIdle= */ anyLong(), - /* darkeningLightDebounceConfigIdle= */ anyLong(), - /* resetAmbientLuxAfterWarmUpConfig= */ anyBoolean(), - any(HysteresisLevels.class), - any(HysteresisLevels.class), - any(HysteresisLevels.class), - any(HysteresisLevels.class), - eq(mContext), - any(BrightnessRangeController.class), - any(BrightnessThrottler.class), - /* ambientLightHorizonShort= */ anyInt(), - /* ambientLightHorizonLong= */ anyInt(), - eq(lux), - eq(nits) - ); - } - - @Test - public void testUpdateBrightnessThrottlingDataId() { - mHolder.display.getDisplayInfoLocked().thermalBrightnessThrottlingDataId = - "throttling-data-id"; - clearInvocations(mHolder.display.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig()); - - mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, Layout.NO_LEAD_DISPLAY); - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.display.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig()) - .getThermalBrightnessThrottlingDataMapByThrottlingId(); - } - - @Test - public void testSetBrightness_BrightnessShouldBeClamped() { - float clampedBrightness = 0.6f; - when(mHolder.hbmController.getCurrentBrightnessMax()).thenReturn(clampedBrightness); - - mHolder.dpc.setBrightness(PowerManager.BRIGHTNESS_MAX); - - verify(mHolder.brightnessSetting).setBrightness(clampedBrightness); - } - - @Test - public void testDwbcCallsHappenOnHandler() { - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); - - mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); - verify(mDisplayWhiteBalanceControllerMock, never()).setStrongModeEnabled(true); - - // dispatch handler looper - advanceTime(1); - verify(mDisplayWhiteBalanceControllerMock, times(1)).setStrongModeEnabled(true); - } - - @Test - public void testRampRatesIdle() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - float brightness = 0.6f; - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(mHolder.automaticBrightnessController.isInIdleMode()).thenReturn(true); - when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( - any(BrightnessEvent.class))).thenReturn(brightness); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness); - brightness = 0.05f; - when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( - any(BrightnessEvent.class))).thenReturn(brightness); - - mHolder.dpc.updateBrightness(); - advanceTime(1); // Run updatePowerState - - // The second time, the animation rate should be slow - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE_IDLE), eq(false)); - - brightness = 0.9f; - when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( - any(BrightnessEvent.class))).thenReturn(brightness); - - mHolder.dpc.updateBrightness(); - advanceTime(1); // Run updatePowerState - // The third time, the animation rate should be slow - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_SLOW_INCREASE_IDLE), eq(false)); - } - - @Test - public void testRampRateForHdrContent_HdrClamperOff() { - float hdrBrightness = 0.8f; - float clampedBrightness = 0.6f; - float transitionRate = 1.5f; - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f); - when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f); - when(mHolder.hbmController.getHighBrightnessMode()).thenReturn( - BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR); - when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness); - when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(clampedBrightness); - when(mHolder.hdrClamper.getTransitionRate()).thenReturn(transitionRate); - - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.animator, atLeastOnce()).animateTo(eq(hdrBrightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - } - - @Test - public void testRampRateForHdrContent_HdrClamperOn() { - float clampedBrightness = 0.6f; - float transitionRate = 1.5f; - when(mDisplayManagerFlagsMock.isHdrClamperEnabled()).thenReturn(true); - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f); - when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f); - when(mHolder.hbmController.getHighBrightnessMode()).thenReturn( - BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR); - when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(PowerManager.BRIGHTNESS_MAX); - when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(clampedBrightness); - when(mHolder.hdrClamper.getTransitionRate()).thenReturn(transitionRate); - - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.animator, atLeastOnce()).animateTo(eq(clampedBrightness), anyFloat(), - eq(transitionRate), eq(true)); - } - - @Test - public void testRampRateForClampersControllerApplied() { - float transitionRate = 1.5f; - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); - DisplayPowerRequest dpr = new DisplayPowerRequest(); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f); - when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f); - when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer( - invocation -> DisplayBrightnessState.builder() - .setIsSlowChange(invocation.getArgument(2)) - .setBrightness(invocation.getArgument(1)) - .setMaxBrightness(PowerManager.BRIGHTNESS_MAX) - .setCustomAnimationRate(transitionRate).build()); - - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.animator, atLeastOnce()).animateTo(anyFloat(), anyFloat(), - eq(transitionRate), anyBoolean()); - } - - @Test - public void testRampRateForClampersControllerNotApplied_ifDoze() { - float transitionRate = 1.5f; - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_DOZE; - dpr.dozeScreenState = Display.STATE_UNKNOWN; - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f); - when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f); - when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer( - invocation -> DisplayBrightnessState.builder() - .setIsSlowChange(invocation.getArgument(2)) - .setBrightness(invocation.getArgument(1)) - .setMaxBrightness(PowerManager.BRIGHTNESS_MAX) - .setCustomAnimationRate(transitionRate).build()); - - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.animator, atLeastOnce()).animateTo(anyFloat(), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), anyBoolean()); - verify(mHolder.animator, never()).animateTo(anyFloat(), anyFloat(), - eq(transitionRate), anyBoolean()); - } - - @Test - @RequiresFlagsDisabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1) - public void testRampMaxTimeInteractiveThenIdle() { - // Send a display power request - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - dpr.useProximitySensor = true; - mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */); - - // Run updatePowerState - advanceTime(1); - - setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class), - mHolder.config, /* isEnabled= */ true); - verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX, - BRIGHTNESS_RAMP_DECREASE_MAX); - - // switch to idle mode - mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); - advanceTime(1); - - // A second time, when switching to idle mode. - verify(mHolder.animator, times(2)).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX, - BRIGHTNESS_RAMP_DECREASE_MAX); - } - - @Test - @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1) - public void testRampMaxTimeInteractiveThenIdle_DifferentValues() { - when(mDisplayManagerFlagsMock.isAdaptiveTone1Enabled()).thenReturn(true); - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true); - - // Send a display power request - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - dpr.useProximitySensor = true; - mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */); - - // Run updatePowerState - advanceTime(1); - - setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class), - mHolder.config, /* isEnabled= */ true); - verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX, - BRIGHTNESS_RAMP_DECREASE_MAX); - - // switch to idle mode - mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); - advanceTime(1); - - // A second time, when switching to idle mode. - verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE, - BRIGHTNESS_RAMP_DECREASE_MAX_IDLE); - } - - @Test - @RequiresFlagsDisabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1) - public void testRampMaxTimeIdle() { - // Send a display power request - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - dpr.useProximitySensor = true; - mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */); - // Run updatePowerState - advanceTime(1); - // Once on setup - verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX, - BRIGHTNESS_RAMP_DECREASE_MAX); - - setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class), - mHolder.config, /* isEnabled= */ true); - - // switch to idle mode - mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); - - // A second time when switching to idle mode. - verify(mHolder.animator, times(2)).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX, - BRIGHTNESS_RAMP_DECREASE_MAX); - } - - @Test - @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1) - public void testRampMaxTimeIdle_DifferentValues() { - when(mDisplayManagerFlagsMock.isAdaptiveTone1Enabled()).thenReturn(true); - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true); - - // Send a display power request - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - dpr.useProximitySensor = true; - mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */); - - // Run updatePowerState - advanceTime(1); - - setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class), - mHolder.config, /* isEnabled= */ true); - - // switch to idle mode - mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); - - verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE, - BRIGHTNESS_RAMP_DECREASE_MAX_IDLE); - } - - @Test - public void testDozeScreenStateOverride_toSupportedOffloadStateFromDoze_DisplayStateChanges() { - // set up. - int initState = Display.STATE_DOZE; - int supportedTargetState = Display.STATE_DOZE_SUSPEND; - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - doAnswer(invocation -> { - when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0)); - return null; - }).when(mHolder.displayPowerState).setScreenState(anyInt()); - mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession); - - // start with DOZE. - when(mHolder.displayPowerState.getScreenState()).thenReturn(initState); - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_DOZE; - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - mHolder.dpc.overrideDozeScreenState(supportedTargetState); - advanceTime(1); // Run updatePowerState - - verify(mHolder.displayPowerState).setScreenState(supportedTargetState); - } - - @Test - public void testDozeScreenStateOverride_toUnSupportedOffloadStateFromDoze_stateRemains() { - // set up. - int initState = Display.STATE_DOZE; - int unSupportedTargetState = Display.STATE_ON; - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - doAnswer(invocation -> { - when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0)); - return null; - }).when(mHolder.displayPowerState).setScreenState(anyInt()); - mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession); - - // start with DOZE. - when(mHolder.displayPowerState.getScreenState()).thenReturn(initState); - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_DOZE; - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - mHolder.dpc.overrideDozeScreenState(unSupportedTargetState); - advanceTime(1); // Run updatePowerState - - verify(mHolder.displayPowerState, never()).setScreenState(anyInt()); - } - - @Test - public void testDozeScreenStateOverride_toSupportedOffloadStateFromOFF_stateRemains() { - // set up. - int initState = Display.STATE_OFF; - int supportedTargetState = Display.STATE_DOZE_SUSPEND; - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); - doAnswer(invocation -> { - when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0)); - return null; - }).when(mHolder.displayPowerState).setScreenState(anyInt()); - mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession); - - // start with OFF. - when(mHolder.displayPowerState.getScreenState()).thenReturn(initState); - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_OFF; - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - mHolder.dpc.overrideDozeScreenState(supportedTargetState); - advanceTime(1); // Run updatePowerState - - verify(mHolder.displayPowerState, never()).setScreenState(anyInt()); - } - - @Test - public void testBrightnessFromOffload() { - when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true); - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - float brightness = 0.34f; - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( - any(BrightnessEvent.class))).thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT); - mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession); - - mHolder.dpc.setBrightnessFromOffload(brightness); - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - // One triggered by handleBrightnessModeChange, another triggered by - // setBrightnessFromOffload - verify(mHolder.animator, times(2)).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - } - - @Test - public void testSwitchToDozeAutoBrightnessMode() { - when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(true); - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_DOZE; - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - // One triggered by handleBrightnessModeChange, another triggered by requestPowerState - verify(mHolder.automaticBrightnessController, times(2)) - .switchMode(AUTO_BRIGHTNESS_MODE_DOZE); - - // Back to default mode - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.automaticBrightnessController).switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT); - } - - @Test - public void testDoesNotSwitchFromIdleToDozeAutoBrightnessMode() { - when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(true); - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); - when(mHolder.automaticBrightnessController.isInIdleMode()).thenReturn(true); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.automaticBrightnessController, never()) - .switchMode(AUTO_BRIGHTNESS_MODE_DOZE); - } - - @Test - public void testDoesNotSwitchDozeAutoBrightnessModeIfFeatureFlagOff() { - when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(false); - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.automaticBrightnessController, never()) - .switchMode(AUTO_BRIGHTNESS_MODE_DOZE); - } - - /** - * Creates a mock and registers it to {@link LocalServices}. - */ - private static <T> void addLocalServiceMock(Class<T> clazz, T mock) { - LocalServices.removeServiceForTest(clazz); - LocalServices.addService(clazz, mock); - } - - private void advanceTime(long timeMs) { - mClock.fastForward(timeMs); - mTestLooper.dispatchAll(); - } - - private void setUpSensors() throws Exception { - mProxSensor = TestUtils.createSensor(Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY, - PROX_SENSOR_MAX_RANGE); - Sensor screenOffBrightnessSensor = TestUtils.createSensor( - Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT); - when(mSensorManagerMock.getSensorList(eq(Sensor.TYPE_ALL))) - .thenReturn(List.of(mProxSensor, screenOffBrightnessSensor)); - } - - private SensorEventListener getSensorEventListener(Sensor sensor) { - verify(mSensorManagerMock).registerListener(mSensorEventListenerCaptor.capture(), - eq(sensor), eq(SensorManager.SENSOR_DELAY_NORMAL), isA(Handler.class)); - return mSensorEventListenerCaptor.getValue(); - } - - private void setUpDisplay(int displayId, String uniqueId, LogicalDisplay logicalDisplayMock, - DisplayDevice displayDeviceMock, DisplayDeviceConfig displayDeviceConfigMock, - boolean isEnabled) { - DisplayInfo info = new DisplayInfo(); - DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo(); - deviceInfo.uniqueId = uniqueId; - - when(logicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId); - when(logicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(displayDeviceMock); - when(logicalDisplayMock.getDisplayInfoLocked()).thenReturn(info); - when(logicalDisplayMock.isEnabledLocked()).thenReturn(isEnabled); - when(logicalDisplayMock.isInTransitionLocked()).thenReturn(false); - when(displayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo); - when(displayDeviceMock.getUniqueId()).thenReturn(uniqueId); - when(displayDeviceMock.getDisplayDeviceConfig()).thenReturn(displayDeviceConfigMock); - when(displayDeviceConfigMock.getProximitySensor()).thenReturn( - new SensorData(Sensor.STRING_TYPE_PROXIMITY, null)); - when(displayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500}); - when(displayDeviceConfigMock.isAutoBrightnessAvailable()).thenReturn(true); - when(displayDeviceConfigMock.getAmbientLightSensor()).thenReturn( - new SensorData()); - when(displayDeviceConfigMock.getScreenOffBrightnessSensor()).thenReturn( - new SensorData(Sensor.STRING_TYPE_LIGHT, null)); - when(displayDeviceConfigMock.getScreenOffBrightnessSensorValueToLux()) - .thenReturn(new int[0]); - - when(displayDeviceConfigMock.getBrightnessRampFastDecrease()) - .thenReturn(BRIGHTNESS_RAMP_RATE_FAST_DECREASE); - when(displayDeviceConfigMock.getBrightnessRampFastIncrease()) - .thenReturn(BRIGHTNESS_RAMP_RATE_FAST_INCREASE); - when(displayDeviceConfigMock.getBrightnessRampSlowDecrease()) - .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE); - when(displayDeviceConfigMock.getBrightnessRampSlowIncrease()) - .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_INCREASE); - when(displayDeviceConfigMock.getBrightnessRampSlowIncreaseIdle()) - .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_INCREASE_IDLE); - when(displayDeviceConfigMock.getBrightnessRampSlowDecreaseIdle()) - .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE_IDLE); - - when(displayDeviceConfigMock.getBrightnessRampIncreaseMaxMillis()) - .thenReturn(BRIGHTNESS_RAMP_INCREASE_MAX); - when(displayDeviceConfigMock.getBrightnessRampDecreaseMaxMillis()) - .thenReturn(BRIGHTNESS_RAMP_DECREASE_MAX); - when(displayDeviceConfigMock.getBrightnessRampIncreaseMaxIdleMillis()) - .thenReturn(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE); - when(displayDeviceConfigMock.getBrightnessRampDecreaseMaxIdleMillis()) - .thenReturn(BRIGHTNESS_RAMP_DECREASE_MAX_IDLE); - } - - private DisplayPowerControllerHolder createDisplayPowerController(int displayId, - String uniqueId) { - return createDisplayPowerController(displayId, uniqueId, /* isEnabled= */ true); - } - - private DisplayPowerControllerHolder createDisplayPowerController(int displayId, - String uniqueId, boolean isEnabled) { - final DisplayPowerState displayPowerState = mock(DisplayPowerState.class); - final DualRampAnimator<DisplayPowerState> animator = mock(DualRampAnimator.class); - final AutomaticBrightnessController automaticBrightnessController = - mock(AutomaticBrightnessController.class); - final WakelockController wakelockController = mock(WakelockController.class); - final BrightnessMappingStrategy brightnessMappingStrategy = - mock(BrightnessMappingStrategy.class); - final HysteresisLevels hysteresisLevels = mock(HysteresisLevels.class); - final ScreenOffBrightnessSensorController screenOffBrightnessSensorController = - mock(ScreenOffBrightnessSensorController.class); - final HighBrightnessModeController hbmController = mock(HighBrightnessModeController.class); - final HdrClamper hdrClamper = mock(HdrClamper.class); - BrightnessClamperController clamperController = mock(BrightnessClamperController.class); - - when(hbmController.getCurrentBrightnessMax()).thenReturn(PowerManager.BRIGHTNESS_MAX); - when(clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer( - invocation -> DisplayBrightnessState.builder() - .setIsSlowChange(invocation.getArgument(2)) - .setBrightness(invocation.getArgument(1)) - .setMaxBrightness(PowerManager.BRIGHTNESS_MAX) - .setCustomAnimationRate(-1).build()); - - TestInjector injector = spy(new TestInjector(displayPowerState, animator, - automaticBrightnessController, wakelockController, brightnessMappingStrategy, - hysteresisLevels, screenOffBrightnessSensorController, hbmController, hdrClamper, - clamperController, mDisplayManagerFlagsMock)); - - final LogicalDisplay display = mock(LogicalDisplay.class); - final DisplayDevice device = mock(DisplayDevice.class); - final HighBrightnessModeMetadata hbmMetadata = mock(HighBrightnessModeMetadata.class); - final BrightnessSetting brightnessSetting = mock(BrightnessSetting.class); - final DisplayDeviceConfig config = mock(DisplayDeviceConfig.class); - - setUpDisplay(displayId, uniqueId, display, device, config, isEnabled); - - final DisplayPowerController2 dpc = new DisplayPowerController2( - mContext, injector, mDisplayPowerCallbacksMock, mHandler, - mSensorManagerMock, mDisplayBlankerMock, display, - mBrightnessTrackerMock, brightnessSetting, () -> { - }, - hbmMetadata, /* bootCompleted= */ false, mDisplayManagerFlagsMock); - - return new DisplayPowerControllerHolder(dpc, display, displayPowerState, brightnessSetting, - animator, automaticBrightnessController, wakelockController, - screenOffBrightnessSensorController, hbmController, hdrClamper, clamperController, - hbmMetadata, brightnessMappingStrategy, injector, config); - } - - /** - * A class for holding a DisplayPowerController under test and all the mocks specifically - * related to it. - */ - private static class DisplayPowerControllerHolder { - public final DisplayPowerController2 dpc; - public final LogicalDisplay display; - public final DisplayPowerState displayPowerState; - public final BrightnessSetting brightnessSetting; - public final DualRampAnimator<DisplayPowerState> animator; - public final AutomaticBrightnessController automaticBrightnessController; - public final WakelockController wakelockController; - public final ScreenOffBrightnessSensorController screenOffBrightnessSensorController; - public final HighBrightnessModeController hbmController; - - public final HdrClamper hdrClamper; - public final BrightnessClamperController clamperController; - public final HighBrightnessModeMetadata hbmMetadata; - public final BrightnessMappingStrategy brightnessMappingStrategy; - public final DisplayPowerController2.Injector injector; - public final DisplayDeviceConfig config; - - DisplayPowerControllerHolder(DisplayPowerController2 dpc, LogicalDisplay display, - DisplayPowerState displayPowerState, BrightnessSetting brightnessSetting, - DualRampAnimator<DisplayPowerState> animator, - AutomaticBrightnessController automaticBrightnessController, - WakelockController wakelockController, - ScreenOffBrightnessSensorController screenOffBrightnessSensorController, - HighBrightnessModeController hbmController, - HdrClamper hdrClamper, - BrightnessClamperController clamperController, - HighBrightnessModeMetadata hbmMetadata, - BrightnessMappingStrategy brightnessMappingStrategy, - DisplayPowerController2.Injector injector, - DisplayDeviceConfig config) { - this.dpc = dpc; - this.display = display; - this.displayPowerState = displayPowerState; - this.brightnessSetting = brightnessSetting; - this.animator = animator; - this.automaticBrightnessController = automaticBrightnessController; - this.wakelockController = wakelockController; - this.screenOffBrightnessSensorController = screenOffBrightnessSensorController; - this.hbmController = hbmController; - this.hdrClamper = hdrClamper; - this.clamperController = clamperController; - this.hbmMetadata = hbmMetadata; - this.brightnessMappingStrategy = brightnessMappingStrategy; - this.injector = injector; - this.config = config; - } - } - - private class TestInjector extends DisplayPowerController2.Injector { - private final DisplayPowerState mDisplayPowerState; - private final DualRampAnimator<DisplayPowerState> mAnimator; - private final AutomaticBrightnessController mAutomaticBrightnessController; - private final WakelockController mWakelockController; - private final BrightnessMappingStrategy mBrightnessMappingStrategy; - private final HysteresisLevels mHysteresisLevels; - private final ScreenOffBrightnessSensorController mScreenOffBrightnessSensorController; - private final HighBrightnessModeController mHighBrightnessModeController; - - private final HdrClamper mHdrClamper; - - private final BrightnessClamperController mClamperController; - - private final DisplayManagerFlags mFlags; - - TestInjector(DisplayPowerState dps, DualRampAnimator<DisplayPowerState> animator, - AutomaticBrightnessController automaticBrightnessController, - WakelockController wakelockController, - BrightnessMappingStrategy brightnessMappingStrategy, - HysteresisLevels hysteresisLevels, - ScreenOffBrightnessSensorController screenOffBrightnessSensorController, - HighBrightnessModeController highBrightnessModeController, - HdrClamper hdrClamper, - BrightnessClamperController clamperController, - DisplayManagerFlags flags) { - mDisplayPowerState = dps; - mAnimator = animator; - mAutomaticBrightnessController = automaticBrightnessController; - mWakelockController = wakelockController; - mBrightnessMappingStrategy = brightnessMappingStrategy; - mHysteresisLevels = hysteresisLevels; - mScreenOffBrightnessSensorController = screenOffBrightnessSensorController; - mHighBrightnessModeController = highBrightnessModeController; - mHdrClamper = hdrClamper; - mClamperController = clamperController; - mFlags = flags; - } - - @Override - DisplayPowerController2.Clock getClock() { - return mClock::now; - } - - @Override - DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade, - int displayId, int displayState) { - return mDisplayPowerState; - } - - @Override - DualRampAnimator<DisplayPowerState> getDualRampAnimator(DisplayPowerState dps, - FloatProperty<DisplayPowerState> firstProperty, - FloatProperty<DisplayPowerState> secondProperty) { - return mAnimator; - } - - @Override - WakelockController getWakelockController(int displayId, - DisplayPowerCallbacks displayPowerCallbacks) { - return mWakelockController; - } - - @Override - DisplayPowerProximityStateController getDisplayPowerProximityStateController( - WakelockController wakelockController, DisplayDeviceConfig displayDeviceConfig, - Looper looper, Runnable nudgeUpdatePowerState, int displayId, - SensorManager sensorManager) { - return new DisplayPowerProximityStateController(wakelockController, - displayDeviceConfig, looper, nudgeUpdatePowerState, displayId, - sensorManager, - new DisplayPowerProximityStateController.Injector() { - @Override - DisplayPowerProximityStateController.Clock createClock() { - return mClock::now; - } - }); - } - - @Override - AutomaticBrightnessController getAutomaticBrightnessController( - AutomaticBrightnessController.Callbacks callbacks, Looper looper, - SensorManager sensorManager, Sensor lightSensor, - SparseArray<BrightnessMappingStrategy> brightnessMappingStrategyMap, - int lightSensorWarmUpTime, float brightnessMin, float brightnessMax, - float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, - long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, - long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle, - boolean resetAmbientLuxAfterWarmUpConfig, - HysteresisLevels ambientBrightnessThresholds, - HysteresisLevels screenBrightnessThresholds, - HysteresisLevels ambientBrightnessThresholdsIdle, - HysteresisLevels screenBrightnessThresholdsIdle, Context context, - BrightnessRangeController brightnessRangeController, - BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort, - int ambientLightHorizonLong, float userLux, float userNits) { - return mAutomaticBrightnessController; - } - - @Override - BrightnessMappingStrategy getDefaultModeBrightnessMapper(Context context, - DisplayDeviceConfig displayDeviceConfig, - DisplayWhiteBalanceController displayWhiteBalanceController) { - return mBrightnessMappingStrategy; - } - - @Override - HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages, - float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels, - float[] darkeningThresholdLevels, float minDarkeningThreshold, - float minBrighteningThreshold) { - return mHysteresisLevels; - } - - @Override - HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages, - float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels, - float[] darkeningThresholdLevels, float minDarkeningThreshold, - float minBrighteningThreshold, boolean potentialOldBrightnessRange) { - return mHysteresisLevels; - } - - @Override - ScreenOffBrightnessSensorController getScreenOffBrightnessSensorController( - SensorManager sensorManager, Sensor lightSensor, Handler handler, - ScreenOffBrightnessSensorController.Clock clock, int[] sensorValueToLux, - BrightnessMappingStrategy brightnessMapper) { - return mScreenOffBrightnessSensorController; - } - - @Override - HighBrightnessModeController getHighBrightnessModeController(Handler handler, int width, - int height, IBinder displayToken, String displayUniqueId, float brightnessMin, - float brightnessMax, DisplayDeviceConfig.HighBrightnessModeData hbmData, - HighBrightnessModeController.HdrBrightnessDeviceConfig hdrBrightnessCfg, - Runnable hbmChangeCallback, HighBrightnessModeMetadata hbmMetadata, - Context context) { - return mHighBrightnessModeController; - } - - @Override - BrightnessRangeController getBrightnessRangeController( - HighBrightnessModeController hbmController, Runnable modeChangeCallback, - DisplayDeviceConfig displayDeviceConfig, Handler handler, - DisplayManagerFlags flags, IBinder displayToken, DisplayDeviceInfo info) { - return new BrightnessRangeController(hbmController, modeChangeCallback, - displayDeviceConfig, mHdrClamper, mFlags, displayToken, info); - } - - @Override - BrightnessClamperController getBrightnessClamperController(Handler handler, - BrightnessClamperController.ClamperChangeListener clamperChangeListener, - BrightnessClamperController.DisplayDeviceData data, Context context, - DisplayManagerFlags flags) { - return mClamperController; - } - - @Override - DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler, - SensorManager sensorManager, Resources resources) { - return mDisplayWhiteBalanceControllerMock; - } - } -} diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java index 943862f918bc..88a9758991f4 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -18,6 +18,8 @@ package com.android.server.display; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE; import static org.junit.Assert.assertNotNull; @@ -67,15 +69,16 @@ import android.view.Display; import android.view.DisplayInfo; import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; -import com.android.internal.util.test.LocalServiceKeeperRule; import com.android.modules.utils.testing.ExtendedMockitoRule; +import com.android.server.LocalServices; import com.android.server.am.BatteryStatsService; import com.android.server.display.RampAnimator.DualRampAnimator; import com.android.server.display.brightness.BrightnessEvent; +import com.android.server.display.brightness.clamper.BrightnessClamperController; +import com.android.server.display.brightness.clamper.HdrClamper; import com.android.server.display.color.ColorDisplayService; import com.android.server.display.config.SensorData; import com.android.server.display.feature.DisplayManagerFlags; @@ -85,6 +88,7 @@ import com.android.server.display.whitebalance.DisplayWhiteBalanceController; import com.android.server.policy.WindowManagerPolicy; import com.android.server.testutils.OffsettableClock; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -147,7 +151,6 @@ public final class DisplayPowerControllerTest { private DisplayManagerFlags mDisplayManagerFlagsMock; @Mock private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession; - @Captor private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor; @@ -165,9 +168,6 @@ public final class DisplayPowerControllerTest { .build(); @Rule - public LocalServiceKeeperRule mLocalServiceKeeperRule = new LocalServiceKeeperRule(); - - @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @Before @@ -183,10 +183,9 @@ public final class DisplayPowerControllerTest { Settings.System.putFloatForUser(mContext.getContentResolver(), Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0, UserHandle.USER_CURRENT); - mLocalServiceKeeperRule.overrideLocalService( - WindowManagerPolicy.class, mWindowManagerPolicyMock); - mLocalServiceKeeperRule.overrideLocalService( - ColorDisplayService.ColorDisplayServiceInternal.class, mCdsiMock); + addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock); + addLocalServiceMock(ColorDisplayService.ColorDisplayServiceInternal.class, + mCdsiMock); mContext.addMockSystemService(PowerManager.class, mPowerManagerMock); @@ -203,6 +202,12 @@ public final class DisplayPowerControllerTest { mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); } + @After + public void tearDown() { + LocalServices.removeServiceForTest(WindowManagerPolicy.class); + LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class); + } + @Test public void testReleaseProxSuspendBlockersOnExit() throws Exception { when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); @@ -222,19 +227,18 @@ public final class DisplayPowerControllerTest { advanceTime(1); // two times, one for unfinished business and one for proximity - verify(mDisplayPowerCallbacksMock).acquireSuspendBlocker( - mHolder.dpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID)); - verify(mDisplayPowerCallbacksMock).acquireSuspendBlocker( - mHolder.dpc.getSuspendBlockerProxDebounceId(DISPLAY_ID)); + verify(mHolder.wakelockController, times(2)).acquireWakelock( + WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS); + verify(mHolder.wakelockController).acquireWakelock( + WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE); mHolder.dpc.stop(); advanceTime(1); - // two times, one for unfinished business and one for proximity - verify(mDisplayPowerCallbacksMock).releaseSuspendBlocker( - mHolder.dpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID)); - verify(mDisplayPowerCallbacksMock).releaseSuspendBlocker( - mHolder.dpc.getSuspendBlockerProxDebounceId(DISPLAY_ID)); + verify(mHolder.wakelockController, times(2)).acquireWakelock( + WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS); + verify(mHolder.wakelockController).acquireWakelock( + WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE); } @Test @@ -316,14 +320,13 @@ public final class DisplayPowerControllerTest { @Test public void testProximitySensorListenerNotRegisteredForNonDefaultDisplay() { - DisplayPowerControllerHolder followerDpc = - createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); // send a display power request DisplayPowerRequest dpr = new DisplayPowerRequest(); dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; dpr.useProximitySensor = true; + final DisplayPowerControllerHolder followerDpc = + createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); followerDpc.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */); // Run updatePowerState @@ -334,7 +337,6 @@ public final class DisplayPowerControllerTest { } @Test - @FlakyTest(bugId = 294107062) public void testDisplayBrightnessFollowers_BothDpcsSupportNits() { DisplayPowerControllerHolder followerDpc = createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); @@ -363,13 +365,10 @@ public final class DisplayPowerControllerTest { when(mHolder.brightnessSetting.getBrightness()).thenReturn(leadBrightness); listener.onBrightnessChanged(leadBrightness); advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); + verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(), anyFloat(), eq(false)); verify(followerDpc.animator).animateTo(eq(followerBrightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); + anyFloat(), eq(false)); - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(leadBrightness); - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(followerBrightness); clearInvocations(mHolder.animator, followerDpc.animator); // Test the same float scale value @@ -388,7 +387,6 @@ public final class DisplayPowerControllerTest { } @Test - @FlakyTest(bugId = 294107062) public void testDisplayBrightnessFollowers_FollowerDoesNotSupportNits() { DisplayPowerControllerHolder followerDpc = createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); @@ -421,7 +419,6 @@ public final class DisplayPowerControllerTest { } @Test - @FlakyTest(bugId = 294107062) public void testDisplayBrightnessFollowers_LeadDpcDoesNotSupportNits() { DisplayPowerControllerHolder followerDpc = createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); @@ -452,7 +449,6 @@ public final class DisplayPowerControllerTest { } @Test - @FlakyTest(bugId = 294107062) public void testDisplayBrightnessFollowers_NeitherDpcSupportsNits() { DisplayPowerControllerHolder followerDpc = createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); @@ -485,7 +481,6 @@ public final class DisplayPowerControllerTest { } @Test - @FlakyTest(bugId = 294107062) public void testDisplayBrightnessFollowers_AutomaticBrightness() { Settings.System.putInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE, @@ -557,7 +552,6 @@ public final class DisplayPowerControllerTest { } @Test - @FlakyTest(bugId = 294107062) public void testDisplayBrightnessFollowersRemoval_RemoveSingleFollower() { DisplayPowerControllerHolder followerDpc = createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); @@ -650,7 +644,6 @@ public final class DisplayPowerControllerTest { } @Test - @FlakyTest(bugId = 294107062) public void testDisplayBrightnessFollowersRemoval_RemoveAllFollowers() { DisplayPowerControllerHolder followerHolder = createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); @@ -737,6 +730,82 @@ public final class DisplayPowerControllerTest { } @Test + @RequiresFlagsEnabled(Flags.FLAG_FAST_HDR_TRANSITIONS) + public void testDisplayBrightnessHdr_SkipAnimationOnHdrAppearance() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + final float sdrBrightness = 0.1f; + final float hdrBrightness = 0.3f; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( + any(BrightnessEvent.class))).thenReturn(sdrBrightness); + when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(1.0f); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); + + when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(sdrBrightness); + when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness); + + when(mHolder.hbmController.getHighBrightnessMode()).thenReturn( + BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR); + when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness); + clearInvocations(mHolder.animator); + + mHolder.dpc.updateBrightness(); + advanceTime(1); // Run updatePowerState + + verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness), + eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false)); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_FAST_HDR_TRANSITIONS) + public void testDisplayBrightnessHdr_SkipAnimationOnHdrRemoval() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + final float sdrBrightness = 0.1f; + final float hdrBrightness = 0.3f; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(mHolder.automaticBrightnessController.isInIdleMode()).thenReturn(true); + when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( + any(BrightnessEvent.class))).thenReturn(sdrBrightness); + when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(1.0f); + + when(mHolder.hbmController.getHighBrightnessMode()).thenReturn( + BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR); + when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); + + when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(hdrBrightness); + when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness); + when(mHolder.hbmController.getHighBrightnessMode()).thenReturn( + BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF); + + clearInvocations(mHolder.animator); + + mHolder.dpc.updateBrightness(); + advanceTime(1); // Run updatePowerState + + verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness), + eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false)); + } + + @Test public void testDoesNotSetScreenStateForNonDefaultDisplayUntilBootCompleted() { // We should still set screen state for the default display DisplayPowerRequest dpr = new DisplayPowerRequest(); @@ -761,6 +830,8 @@ public final class DisplayPowerControllerTest { Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_OFF); + DisplayPowerRequest dpr = new DisplayPowerRequest(); dpr.policy = DisplayPowerRequest.POLICY_OFF; mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); @@ -798,6 +869,7 @@ public final class DisplayPowerControllerTest { Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); DisplayPowerRequest dpr = new DisplayPowerRequest(); dpr.policy = DisplayPowerRequest.POLICY_DOZE; mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); @@ -842,7 +914,6 @@ public final class DisplayPowerControllerTest { Settings.System.putInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ false); DisplayPowerRequest dpr = new DisplayPowerRequest(); @@ -1048,7 +1119,6 @@ public final class DisplayPowerControllerTest { mContext.getOrCreateTestableResources().addOverride( com.android.internal.R.bool.config_persistBrightnessNitsForDefaultDisplay, true); - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits); @@ -1199,76 +1269,98 @@ public final class DisplayPowerControllerTest { } @Test - @RequiresFlagsEnabled(Flags.FLAG_FAST_HDR_TRANSITIONS) - public void testDisplayBrightnessHdr_SkipAnimationOnHdrAppearance() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - final float sdrBrightness = 0.1f; - final float hdrBrightness = 0.3f; - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( - any(BrightnessEvent.class))).thenReturn(sdrBrightness); + public void testRampRateForHdrContent_HdrClamperOff() { + float hdrBrightness = 0.8f; + float clampedBrightness = 0.6f; + float transitionRate = 1.5f; DisplayPowerRequest dpr = new DisplayPowerRequest(); + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f); + when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f); + when(mHolder.hbmController.getHighBrightnessMode()).thenReturn( + BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR); + when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness); + when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(clampedBrightness); + when(mHolder.hdrClamper.getTransitionRate()).thenReturn(transitionRate); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); advanceTime(1); // Run updatePowerState - verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness), + verify(mHolder.animator, atLeastOnce()).animateTo(eq(hdrBrightness), anyFloat(), eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); + } - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(sdrBrightness); - when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness); + @Test + public void testRampRateForHdrContent_HdrClamperOn() { + float clampedBrightness = 0.6f; + float transitionRate = 1.5f; + when(mDisplayManagerFlagsMock.isHdrClamperEnabled()).thenReturn(true); + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true); + DisplayPowerRequest dpr = new DisplayPowerRequest(); + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f); + when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f); when(mHolder.hbmController.getHighBrightnessMode()).thenReturn( BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR); - when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness); - clearInvocations(mHolder.animator); + when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(PowerManager.BRIGHTNESS_MAX); + when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(clampedBrightness); + when(mHolder.hdrClamper.getTransitionRate()).thenReturn(transitionRate); - mHolder.dpc.updateBrightness(); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); advanceTime(1); // Run updatePowerState - verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness), - eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false)); + verify(mHolder.animator, atLeastOnce()).animateTo(eq(clampedBrightness), anyFloat(), + eq(transitionRate), eq(true)); } @Test - @RequiresFlagsEnabled(Flags.FLAG_FAST_HDR_TRANSITIONS) - public void testDisplayBrightnessHdr_SkipAnimationOnHdrRemoval() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - final float sdrBrightness = 0.1f; - final float hdrBrightness = 0.3f; - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + public void testRampRateForClampersControllerApplied() { + float transitionRate = 1.5f; + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); + DisplayPowerRequest dpr = new DisplayPowerRequest(); when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( - any(BrightnessEvent.class))).thenReturn(sdrBrightness); + when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f); + when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f); + when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer( + invocation -> DisplayBrightnessState.builder() + .setIsSlowChange(invocation.getArgument(2)) + .setBrightness(invocation.getArgument(1)) + .setMaxBrightness(PowerManager.BRIGHTNESS_MAX) + .setCustomAnimationRate(transitionRate).build()); - when(mHolder.hbmController.getHighBrightnessMode()).thenReturn( - BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR); - when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); advanceTime(1); // Run updatePowerState - verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(hdrBrightness); - when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness); - when(mHolder.hbmController.getHighBrightnessMode()).thenReturn( - BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF); + verify(mHolder.animator, atLeastOnce()).animateTo(anyFloat(), anyFloat(), + eq(transitionRate), anyBoolean()); + } - clearInvocations(mHolder.animator); + @Test + public void testRampRateForClampersControllerNotApplied_ifDoze() { + float transitionRate = 1.5f; + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_DOZE; + dpr.dozeScreenState = Display.STATE_UNKNOWN; + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f); + when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f); + when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer( + invocation -> DisplayBrightnessState.builder() + .setIsSlowChange(invocation.getArgument(2)) + .setBrightness(invocation.getArgument(1)) + .setMaxBrightness(PowerManager.BRIGHTNESS_MAX) + .setCustomAnimationRate(transitionRate).build()); - mHolder.dpc.updateBrightness(); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); advanceTime(1); // Run updatePowerState - verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness), - eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false)); + verify(mHolder.animator, atLeastOnce()).animateTo(anyFloat(), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), anyBoolean()); + verify(mHolder.animator, never()).animateTo(anyFloat(), anyFloat(), + eq(transitionRate), anyBoolean()); } @Test @@ -1285,14 +1377,14 @@ public final class DisplayPowerControllerTest { setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class), mHolder.config, /* isEnabled= */ true); - verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX, BRIGHTNESS_RAMP_DECREASE_MAX); - // switch to idle + // switch to idle mode mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); advanceTime(1); + // A second time, when switching to idle mode. verify(mHolder.animator, times(2)).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX, BRIGHTNESS_RAMP_DECREASE_MAX); } @@ -1301,6 +1393,8 @@ public final class DisplayPowerControllerTest { @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1) public void testRampMaxTimeInteractiveThenIdle_DifferentValues() { when(mDisplayManagerFlagsMock.isAdaptiveTone1Enabled()).thenReturn(true); + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true); + // Send a display power request DisplayPowerRequest dpr = new DisplayPowerRequest(); dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; @@ -1312,14 +1406,14 @@ public final class DisplayPowerControllerTest { setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class), mHolder.config, /* isEnabled= */ true); - verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX, BRIGHTNESS_RAMP_DECREASE_MAX); - // switch to idle + // switch to idle mode mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); advanceTime(1); + // A second time, when switching to idle mode. verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE, BRIGHTNESS_RAMP_DECREASE_MAX_IDLE); } @@ -1332,11 +1426,9 @@ public final class DisplayPowerControllerTest { dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; dpr.useProximitySensor = true; mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */); - // Run updatePowerState advanceTime(1); - - // once on setup + // Once on setup verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX, BRIGHTNESS_RAMP_DECREASE_MAX); @@ -1346,7 +1438,7 @@ public final class DisplayPowerControllerTest { // switch to idle mode mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); - // second time when switching to idle screen brightness mode + // A second time when switching to idle mode. verify(mHolder.animator, times(2)).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX, BRIGHTNESS_RAMP_DECREASE_MAX); } @@ -1355,6 +1447,7 @@ public final class DisplayPowerControllerTest { @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1) public void testRampMaxTimeIdle_DifferentValues() { when(mDisplayManagerFlagsMock.isAdaptiveTone1Enabled()).thenReturn(true); + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true); // Send a display power request DisplayPowerRequest dpr = new DisplayPowerRequest(); @@ -1374,6 +1467,7 @@ public final class DisplayPowerControllerTest { verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE, BRIGHTNESS_RAMP_DECREASE_MAX_IDLE); } + @Test public void testDozeScreenStateOverride_toSupportedOffloadStateFromDoze_DisplayStateChanges() { // set up. @@ -1451,6 +1545,89 @@ public final class DisplayPowerControllerTest { verify(mHolder.displayPowerState, never()).setScreenState(anyInt()); } + @Test + public void testBrightnessFromOffload() { + when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true); + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + float brightness = 0.34f; + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( + any(BrightnessEvent.class))).thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT); + mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession); + + mHolder.dpc.setBrightnessFromOffload(brightness); + DisplayPowerRequest dpr = new DisplayPowerRequest(); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + // One triggered by handleBrightnessModeChange, another triggered by + // setBrightnessFromOffload + verify(mHolder.animator, times(2)).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); + } + + @Test + public void testSwitchToDozeAutoBrightnessMode() { + when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(true); + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_DOZE; + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + // One triggered by handleBrightnessModeChange, another triggered by requestPowerState + verify(mHolder.automaticBrightnessController, times(2)) + .switchMode(AUTO_BRIGHTNESS_MODE_DOZE); + + // Back to default mode + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.automaticBrightnessController).switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT); + } + + @Test + public void testDoesNotSwitchFromIdleToDozeAutoBrightnessMode() { + when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(true); + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); + when(mHolder.automaticBrightnessController.isInIdleMode()).thenReturn(true); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.automaticBrightnessController, never()) + .switchMode(AUTO_BRIGHTNESS_MODE_DOZE); + } + + @Test + public void testDoesNotSwitchDozeAutoBrightnessModeIfFeatureFlagOff() { + when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(false); + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.automaticBrightnessController, never()) + .switchMode(AUTO_BRIGHTNESS_MODE_DOZE); + } + + /** + * Creates a mock and registers it to {@link LocalServices}. + */ + private static <T> void addLocalServiceMock(Class<T> clazz, T mock) { + LocalServices.removeServiceForTest(clazz); + LocalServices.addService(clazz, mock); + } + private void advanceTime(long timeMs) { mClock.fastForward(timeMs); mTestLooper.dispatchAll(); @@ -1505,10 +1682,10 @@ public final class DisplayPowerControllerTest { .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE); when(displayDeviceConfigMock.getBrightnessRampSlowIncrease()) .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_INCREASE); - when(displayDeviceConfigMock.getBrightnessRampSlowDecreaseIdle()) - .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE_IDLE); when(displayDeviceConfigMock.getBrightnessRampSlowIncreaseIdle()) .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_INCREASE_IDLE); + when(displayDeviceConfigMock.getBrightnessRampSlowDecreaseIdle()) + .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE_IDLE); when(displayDeviceConfigMock.getBrightnessRampIncreaseMaxMillis()) .thenReturn(BRIGHTNESS_RAMP_INCREASE_MAX); @@ -1531,18 +1708,28 @@ public final class DisplayPowerControllerTest { final DualRampAnimator<DisplayPowerState> animator = mock(DualRampAnimator.class); final AutomaticBrightnessController automaticBrightnessController = mock(AutomaticBrightnessController.class); + final WakelockController wakelockController = mock(WakelockController.class); final BrightnessMappingStrategy brightnessMappingStrategy = mock(BrightnessMappingStrategy.class); final HysteresisLevels hysteresisLevels = mock(HysteresisLevels.class); final ScreenOffBrightnessSensorController screenOffBrightnessSensorController = mock(ScreenOffBrightnessSensorController.class); final HighBrightnessModeController hbmController = mock(HighBrightnessModeController.class); + final HdrClamper hdrClamper = mock(HdrClamper.class); + BrightnessClamperController clamperController = mock(BrightnessClamperController.class); when(hbmController.getCurrentBrightnessMax()).thenReturn(PowerManager.BRIGHTNESS_MAX); - - DisplayPowerController.Injector injector = spy(new TestInjector(displayPowerState, animator, - automaticBrightnessController, brightnessMappingStrategy, hysteresisLevels, - screenOffBrightnessSensorController, hbmController)); + when(clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer( + invocation -> DisplayBrightnessState.builder() + .setIsSlowChange(invocation.getArgument(2)) + .setBrightness(invocation.getArgument(1)) + .setMaxBrightness(PowerManager.BRIGHTNESS_MAX) + .setCustomAnimationRate(-1).build()); + + TestInjector injector = spy(new TestInjector(displayPowerState, animator, + automaticBrightnessController, wakelockController, brightnessMappingStrategy, + hysteresisLevels, screenOffBrightnessSensorController, hbmController, hdrClamper, + clamperController, mDisplayManagerFlagsMock)); final LogicalDisplay display = mock(LogicalDisplay.class); final DisplayDevice device = mock(DisplayDevice.class); @@ -1555,12 +1742,14 @@ public final class DisplayPowerControllerTest { final DisplayPowerController dpc = new DisplayPowerController( mContext, injector, mDisplayPowerCallbacksMock, mHandler, mSensorManagerMock, mDisplayBlankerMock, display, - mBrightnessTrackerMock, brightnessSetting, () -> {}, + mBrightnessTrackerMock, brightnessSetting, () -> { + }, hbmMetadata, /* bootCompleted= */ false, mDisplayManagerFlagsMock); return new DisplayPowerControllerHolder(dpc, display, displayPowerState, brightnessSetting, - animator, automaticBrightnessController, screenOffBrightnessSensorController, - hbmController, hbmMetadata, brightnessMappingStrategy, injector, config); + animator, automaticBrightnessController, wakelockController, + screenOffBrightnessSensorController, hbmController, hdrClamper, clamperController, + hbmMetadata, brightnessMappingStrategy, injector, config); } /** @@ -1574,8 +1763,12 @@ public final class DisplayPowerControllerTest { public final BrightnessSetting brightnessSetting; public final DualRampAnimator<DisplayPowerState> animator; public final AutomaticBrightnessController automaticBrightnessController; + public final WakelockController wakelockController; public final ScreenOffBrightnessSensorController screenOffBrightnessSensorController; public final HighBrightnessModeController hbmController; + + public final HdrClamper hdrClamper; + public final BrightnessClamperController clamperController; public final HighBrightnessModeMetadata hbmMetadata; public final BrightnessMappingStrategy brightnessMappingStrategy; public final DisplayPowerController.Injector injector; @@ -1585,8 +1778,11 @@ public final class DisplayPowerControllerTest { DisplayPowerState displayPowerState, BrightnessSetting brightnessSetting, DualRampAnimator<DisplayPowerState> animator, AutomaticBrightnessController automaticBrightnessController, + WakelockController wakelockController, ScreenOffBrightnessSensorController screenOffBrightnessSensorController, HighBrightnessModeController hbmController, + HdrClamper hdrClamper, + BrightnessClamperController clamperController, HighBrightnessModeMetadata hbmMetadata, BrightnessMappingStrategy brightnessMappingStrategy, DisplayPowerController.Injector injector, @@ -1597,8 +1793,11 @@ public final class DisplayPowerControllerTest { this.brightnessSetting = brightnessSetting; this.animator = animator; this.automaticBrightnessController = automaticBrightnessController; + this.wakelockController = wakelockController; this.screenOffBrightnessSensorController = screenOffBrightnessSensorController; this.hbmController = hbmController; + this.hdrClamper = hdrClamper; + this.clamperController = clamperController; this.hbmMetadata = hbmMetadata; this.brightnessMappingStrategy = brightnessMappingStrategy; this.injector = injector; @@ -1610,24 +1809,39 @@ public final class DisplayPowerControllerTest { private final DisplayPowerState mDisplayPowerState; private final DualRampAnimator<DisplayPowerState> mAnimator; private final AutomaticBrightnessController mAutomaticBrightnessController; + private final WakelockController mWakelockController; private final BrightnessMappingStrategy mBrightnessMappingStrategy; private final HysteresisLevels mHysteresisLevels; private final ScreenOffBrightnessSensorController mScreenOffBrightnessSensorController; private final HighBrightnessModeController mHighBrightnessModeController; + private final HdrClamper mHdrClamper; + + private final BrightnessClamperController mClamperController; + + private final DisplayManagerFlags mFlags; + TestInjector(DisplayPowerState dps, DualRampAnimator<DisplayPowerState> animator, AutomaticBrightnessController automaticBrightnessController, + WakelockController wakelockController, BrightnessMappingStrategy brightnessMappingStrategy, HysteresisLevels hysteresisLevels, ScreenOffBrightnessSensorController screenOffBrightnessSensorController, - HighBrightnessModeController highBrightnessModeController) { + HighBrightnessModeController highBrightnessModeController, + HdrClamper hdrClamper, + BrightnessClamperController clamperController, + DisplayManagerFlags flags) { mDisplayPowerState = dps; mAnimator = animator; mAutomaticBrightnessController = automaticBrightnessController; + mWakelockController = wakelockController; mBrightnessMappingStrategy = brightnessMappingStrategy; mHysteresisLevels = hysteresisLevels; mScreenOffBrightnessSensorController = screenOffBrightnessSensorController; mHighBrightnessModeController = highBrightnessModeController; + mHdrClamper = hdrClamper; + mClamperController = clamperController; + mFlags = flags; } @Override @@ -1649,6 +1863,28 @@ public final class DisplayPowerControllerTest { } @Override + WakelockController getWakelockController(int displayId, + DisplayPowerCallbacks displayPowerCallbacks) { + return mWakelockController; + } + + @Override + DisplayPowerProximityStateController getDisplayPowerProximityStateController( + WakelockController wakelockController, DisplayDeviceConfig displayDeviceConfig, + Looper looper, Runnable nudgeUpdatePowerState, int displayId, + SensorManager sensorManager) { + return new DisplayPowerProximityStateController(wakelockController, + displayDeviceConfig, looper, nudgeUpdatePowerState, displayId, + sensorManager, + new DisplayPowerProximityStateController.Injector() { + @Override + DisplayPowerProximityStateController.Clock createClock() { + return mClock::now; + } + }); + } + + @Override AutomaticBrightnessController getAutomaticBrightnessController( AutomaticBrightnessController.Callbacks callbacks, Looper looper, SensorManager sensorManager, Sensor lightSensor, @@ -1710,6 +1946,23 @@ public final class DisplayPowerControllerTest { } @Override + BrightnessRangeController getBrightnessRangeController( + HighBrightnessModeController hbmController, Runnable modeChangeCallback, + DisplayDeviceConfig displayDeviceConfig, Handler handler, + DisplayManagerFlags flags, IBinder displayToken, DisplayDeviceInfo info) { + return new BrightnessRangeController(hbmController, modeChangeCallback, + displayDeviceConfig, mHdrClamper, mFlags, displayToken, info); + } + + @Override + BrightnessClamperController getBrightnessClamperController(Handler handler, + BrightnessClamperController.ClamperChangeListener clamperChangeListener, + BrightnessClamperController.DisplayDeviceData data, Context context, + DisplayManagerFlags flags) { + return mClamperController; + } + + @Override DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler, SensorManager sensorManager, Resources resources) { return mDisplayWhiteBalanceControllerMock; 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 e4b6206f7f54..d876dae29798 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java @@ -101,6 +101,7 @@ import android.os.Process; import android.os.SystemClock; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; import android.util.ArrayMap; import android.util.SparseArray; @@ -111,7 +112,9 @@ import com.android.server.wm.WindowProcessController; import org.junit.After; import org.junit.AfterClass; +import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; import java.io.File; @@ -161,6 +164,9 @@ public class MockingOomAdjusterTests { private static PackageManagerInternal sPackageManagerInternal; private static ActivityManagerService sService; + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @SuppressWarnings("GuardedBy") @BeforeClass public static void setUpOnce() { @@ -227,6 +233,11 @@ public class MockingOomAdjusterTests { } } + @Before + public void setUp() { + mSetFlagsRule.enableFlags(Flags.FLAG_NEW_FGS_RESTRICTION_LOGIC); + } + @AfterClass public static void tearDownOnce() { LocalServices.removeServiceForTest(PackageManagerInternal.class); @@ -540,6 +551,7 @@ public class MockingOomAdjusterTests { sService.mConstants.mShortFgsProcStateExtraWaitDuration = 200_000; ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService); + s.appInfo = new ApplicationInfo(); s.startRequested = true; s.isForeground = true; s.foregroundServiceType = FOREGROUND_SERVICE_TYPE_SHORT_SERVICE; @@ -582,6 +594,7 @@ public class MockingOomAdjusterTests { // SHORT_SERVICE, timed out already. s = ServiceRecord.newEmptyInstanceForTest(sService); + s.appInfo = new ApplicationInfo(); s.startRequested = true; s.isForeground = true; s.foregroundServiceType = FOREGROUND_SERVICE_TYPE_SHORT_SERVICE; @@ -1100,6 +1113,7 @@ public class MockingOomAdjusterTests { // In order to trick OomAdjuster to think it has a short-service, we need this logic. ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService); + s.appInfo = new ApplicationInfo(); s.startRequested = true; s.isForeground = true; s.foregroundServiceType = FOREGROUND_SERVICE_TYPE_SHORT_SERVICE; @@ -1130,6 +1144,7 @@ public class MockingOomAdjusterTests { // In order to trick OomAdjuster to think it has a short-service, we need this logic. ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService); + s.appInfo = new ApplicationInfo(); s.startRequested = true; s.isForeground = true; s.foregroundServiceType = FOREGROUND_SERVICE_TYPE_SHORT_SERVICE; @@ -1421,6 +1436,7 @@ public class MockingOomAdjusterTests { // In order to trick OomAdjuster to think it has a short-service, we need this logic. ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService); + s.appInfo = new ApplicationInfo(); s.startRequested = true; s.isForeground = true; s.foregroundServiceType = FOREGROUND_SERVICE_TYPE_SHORT_SERVICE; diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java index fc2e5b0981e4..0703db2ab648 100644 --- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java @@ -118,7 +118,8 @@ import java.util.function.Supplier; @Presubmit public class GameManagerServiceTests { @Mock MockContext mMockContext; - @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule( + SetFlagsRule.DefaultInitValueType.NULL_DEFAULT); private static final String TAG = "GameManagerServiceTests"; private static final String PACKAGE_NAME_INVALID = "com.android.app"; private static final int USER_ID_1 = 1001; diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundInstallControlCallbackHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundInstallControlCallbackHelperTest.java new file mode 100644 index 000000000000..e1fce9b75906 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundInstallControlCallbackHelperTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static com.android.server.pm.BackgroundInstallControlCallbackHelper.FLAGGED_PACKAGE_NAME_KEY; +import static com.android.server.pm.BackgroundInstallControlCallbackHelper.FLAGGED_USER_ID_KEY; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.after; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.os.Bundle; +import android.os.IRemoteCallback; +import android.os.RemoteException; +import android.platform.test.annotations.Presubmit; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.ArgumentCaptor; + +/** Unit tests for {@link BackgroundInstallControlCallbackHelperTest} */ +@Presubmit +@RunWith(JUnit4.class) +public class BackgroundInstallControlCallbackHelperTest { + + private final IRemoteCallback mCallback = + spy( + new IRemoteCallback.Stub() { + @Override + public void sendResult(Bundle extras) {} + }); + + private BackgroundInstallControlCallbackHelper mCallbackHelper; + + @Before + public void setup() { + mCallbackHelper = new BackgroundInstallControlCallbackHelper(); + } + + @Test + public void registerBackgroundInstallControlCallback_registers_successfully() { + mCallbackHelper.registerBackgroundInstallCallback(mCallback); + + synchronized (mCallbackHelper.mCallbacks) { + assertEquals(1, mCallbackHelper.mCallbacks.getRegisteredCallbackCount()); + assertEquals(mCallback, mCallbackHelper.mCallbacks.getRegisteredCallbackItem(0)); + } + } + + @Test + public void unregisterBackgroundInstallControlCallback_unregisters_successfully() { + synchronized (mCallbackHelper.mCallbacks) { + mCallbackHelper.mCallbacks.register(mCallback); + } + + mCallbackHelper.unregisterBackgroundInstallCallback(mCallback); + + synchronized (mCallbackHelper.mCallbacks) { + assertEquals(0, mCallbackHelper.mCallbacks.getRegisteredCallbackCount()); + } + } + + @Test + public void notifyAllCallbacks_broadcastsToCallbacks() + throws RemoteException { + String testPackageName = "testname"; + int testUserId = 1; + mCallbackHelper.registerBackgroundInstallCallback(mCallback); + + mCallbackHelper.notifyAllCallbacks(testUserId, testPackageName); + + ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); + verify(mCallback, after(1000).times(1)).sendResult(bundleCaptor.capture()); + Bundle receivedBundle = bundleCaptor.getValue(); + assertEquals(testPackageName, receivedBundle.getString(FLAGGED_PACKAGE_NAME_KEY)); + assertEquals(testUserId, receivedBundle.getInt(FLAGGED_USER_ID_KEY)); + } +} diff --git a/services/tests/powerstatstests/Android.bp b/services/tests/powerstatstests/Android.bp index 05e0e8f4a4f7..654d7a8de168 100644 --- a/services/tests/powerstatstests/Android.bp +++ b/services/tests/powerstatstests/Android.bp @@ -11,6 +11,7 @@ filegroup { "src/com/android/server/power/stats/MultiStateStatsTest.java", "src/com/android/server/power/stats/PowerStatsAggregatorTest.java", "src/com/android/server/power/stats/PowerStatsCollectorTest.java", + "src/com/android/server/power/stats/PowerStatsExporterTest.java", "src/com/android/server/power/stats/PowerStatsSchedulerTest.java", "src/com/android/server/power/stats/PowerStatsStoreTest.java", "src/com/android/server/power/stats/PowerStatsUidResolverTest.java", @@ -83,6 +84,9 @@ android_ravenwood_test { ], srcs: [ ":power_stats_ravenwood_tests", + + "src/com/android/server/power/stats/BatteryUsageStatsRule.java", + "src/com/android/server/power/stats/MockBatteryStatsImpl.java", "src/com/android/server/power/stats/MockClock.java", ], auto_gen_config: true, diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java index 4ea0805ffaa7..3f058a2348c3 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java @@ -37,12 +37,12 @@ public class BatteryChargeCalculatorTest { private static final double PRECISION = 0.00001; @Rule - public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(); + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() + .setAveragePower(PowerProfile.POWER_BATTERY_CAPACITY, 4000.0); @Test public void testDischargeTotals() { // Nominal battery capacity should be ignored - mStatsRule.setAveragePower(PowerProfile.POWER_BATTERY_CAPACITY, 1234.0); final BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); @@ -84,8 +84,6 @@ public class BatteryChargeCalculatorTest { @Test public void testDischargeTotals_chargeUahUnavailable() { - mStatsRule.setAveragePower(PowerProfile.POWER_BATTERY_CAPACITY, 4000.0); - final BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100, diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java index e61dd0b41423..ca162e0b46e1 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java @@ -18,11 +18,11 @@ package com.android.server.power.stats; import static org.mockito.ArgumentMatchers.anyDouble; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.annotation.XmlRes; -import android.content.Context; import android.net.NetworkStats; import android.os.BatteryConsumer; import android.os.BatteryStats; @@ -54,12 +54,11 @@ public class BatteryUsageStatsRule implements TestRule { .powerProfileModeledOnly() .includePowerModels() .build(); - private final Context mContext; private final PowerProfile mPowerProfile; private final MockClock mMockClock = new MockClock(); private final MockBatteryStatsImpl mBatteryStats; - private final Handler mHandler; + private Handler mHandler; private BatteryUsageStats mBatteryUsageStats; private boolean mScreenOn; @@ -76,11 +75,8 @@ public class BatteryUsageStatsRule implements TestRule { } public BatteryUsageStatsRule(long currentTime, File historyDir) { - HandlerThread bgThread = new HandlerThread("bg thread"); - bgThread.start(); - mHandler = new Handler(bgThread.getLooper()); - mContext = InstrumentationRegistry.getContext(); - mPowerProfile = spy(new PowerProfile(mContext, true /* forTest */)); + mHandler = mock(Handler.class); + mPowerProfile = spy(new PowerProfile()); mMockClock.currentTime = currentTime; mBatteryStats = new MockBatteryStatsImpl(mMockClock, historyDir, mHandler); mBatteryStats.setPowerProfile(mPowerProfile); @@ -103,7 +99,7 @@ public class BatteryUsageStatsRule implements TestRule { } public BatteryUsageStatsRule setTestPowerProfile(@XmlRes int xmlId) { - mPowerProfile.forceInitForTesting(mContext, xmlId); + mPowerProfile.forceInitForTesting(InstrumentationRegistry.getContext(), xmlId); return this; } @@ -222,13 +218,17 @@ public class BatteryUsageStatsRule implements TestRule { return new Statement() { @Override public void evaluate() throws Throwable { - noteOnBattery(); + before(); base.evaluate(); } }; } - private void noteOnBattery() { + private void before() { + HandlerThread bgThread = new HandlerThread("bg thread"); + bgThread.start(); + mHandler = new Handler(bgThread.getLooper()); + mBatteryStats.setHandler(mHandler); mBatteryStats.setOnBatteryInternal(true); mBatteryStats.getOnBatteryTimeBase().setRunning(true, 0, 0); mBatteryStats.getOnBatteryScreenOffTimeBase().setRunning(!mScreenOn, 0, 0); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java index fb71ac83a8d9..78c4bac14a74 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java @@ -271,6 +271,10 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { public void writeSyncLocked() { } + public void setHandler(Handler handler) { + mHandler = handler; + } + public static class DummyExternalStatsSync implements ExternalStatsSync { public int flags = 0; @@ -315,4 +319,3 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { } } } - diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java index 3c482625b038..3560a2620c8f 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java @@ -16,8 +16,6 @@ package com.android.server.power.stats; -import static androidx.test.InstrumentationRegistry.getContext; - import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; @@ -34,6 +32,7 @@ import android.os.Message; import android.os.Parcel; import android.os.PersistableBundle; import android.os.UidBatteryConsumer; +import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.runner.AndroidJUnit4; @@ -48,6 +47,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; import java.util.List; @RunWith(AndroidJUnit4.class) @@ -57,7 +58,12 @@ public class PowerStatsExporterTest { private static final int APP_UID2 = 84; private static final double TOLERANCE = 0.01; - @Rule + @Rule(order = 0) + public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() + .setProvideMainThread(true) + .build(); + + @Rule(order = 1) public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() .setAveragePower(PowerProfile.POWER_CPU_ACTIVE, 720) .setCpuScalingPolicy(0, new int[]{0}, new int[]{100}) @@ -75,8 +81,8 @@ public class PowerStatsExporterTest { private PowerStats.Descriptor mPowerStatsDescriptor; @Before - public void setup() { - File storeDirectory = new File(getContext().getCacheDir(), getClass().getSimpleName()); + public void setup() throws IOException { + File storeDirectory = Files.createTempDirectory("PowerStatsExporterTest").toFile(); clearDirectory(storeDirectory); AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig(); diff --git a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java index c0051c6c9e17..eee37525ee37 100644 --- a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java @@ -133,7 +133,8 @@ public class AnrHelperTest { verify(mAnrApp.mErrorState, timeout(TIMEOUT_MS)).appNotResponding( eq(activityShortComponentName), eq(appInfo), eq(parentShortComponentName), eq(parentProcess), eq(aboveSystem), eq(timeoutRecord), eq(mAuxExecutorService), - eq(false) /* onlyDumpSelf */, eq(false) /*isContinuousAnr*/, eq(mEarlyDumpFuture)); + anyBoolean() /* onlyDumpSelf */, eq(false) /*isContinuousAnr*/, + eq(mEarlyDumpFuture)); } @Test diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java index 77b1455a2ecc..26934d8e5d6e 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -763,8 +763,7 @@ public class UserControllerTest { mUserController.startUser(TEST_USER_ID, USER_START_MODE_BACKGROUND); - verify(mInjector.mStorageManagerMock, never()) - .unlockCeStorage(eq(TEST_USER_ID), anyInt(), any()); + verify(mInjector.mStorageManagerMock, never()).unlockCeStorage(eq(TEST_USER_ID), any()); } @Test diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java index 89299002a227..f7480dea780b 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java @@ -44,12 +44,18 @@ import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.IBiometricService; +import android.hardware.biometrics.fingerprint.IFingerprint; +import android.hardware.biometrics.fingerprint.ISession; import android.hardware.fingerprint.Fingerprint; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; +import android.os.UserHandle; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.testing.AndroidTestingRunner; import android.testing.TestableContext; import android.testing.TestableLooper; @@ -60,6 +66,7 @@ import androidx.annotation.Nullable; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; +import com.android.server.biometrics.Flags; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.nano.BiometricSchedulerProto; @@ -95,14 +102,39 @@ public class BiometricSchedulerTest { @Rule public final TestableContext mContext = new TestableContext( InstrumentationRegistry.getContext(), null); - private BiometricScheduler mScheduler; + @Rule + public final CheckFlagsRule mCheckFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); + private BiometricScheduler<IFingerprint, ISession> mScheduler; private IBinder mToken; + private int mCurrentUserId = UserHandle.USER_SYSTEM; + private boolean mShouldFailStopUser = false; + private final List<Integer> mStartedUsers = new ArrayList<>(); + private final StartUserClient.UserStartedCallback<ISession> mUserStartedCallback = + (newUserId, newUser, halInterfaceVersion) -> { + mStartedUsers.add(newUserId); + mCurrentUserId = newUserId; + }; + private int mUsersStoppedCount = 0; + private final StopUserClient.UserStoppedCallback mUserStoppedCallback = + () -> { + mUsersStoppedCount++; + mCurrentUserId = UserHandle.USER_NULL; + }; + private boolean mStartOperationsFinish = true; + private int mStartUserClientCount = 0; @Mock private IBiometricService mBiometricService; @Mock private BiometricContext mBiometricContext; @Mock private AuthSessionCoordinator mAuthSessionCoordinator; + @Mock + private BiometricLogger mBiometricLogger; + @Mock + private ISession mSession; + @Mock + private IFingerprint mFingerprint; @Before public void setUp() { @@ -111,9 +143,39 @@ public class BiometricSchedulerTest { when(mAuthSessionCoordinator.getLockoutStateFor(anyInt(), anyInt())).thenReturn( BIOMETRIC_SUCCESS); when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator); - mScheduler = new BiometricScheduler(TAG, new Handler(TestableLooper.get(this).getLooper()), - BiometricScheduler.SENSOR_TYPE_UNKNOWN, null /* gestureAvailabilityTracker */, - mBiometricService, LOG_NUM_RECENT_OPERATIONS); + if (Flags.deHidl()) { + mScheduler = new BiometricScheduler<>( + new Handler(TestableLooper.get(this).getLooper()), + BiometricScheduler.SENSOR_TYPE_UNKNOWN, + null /* gestureAvailabilityDispatcher */, + mBiometricService, + LOG_NUM_RECENT_OPERATIONS, + () -> mCurrentUserId, + new UserSwitchProvider<IFingerprint, ISession>() { + @NonNull + @Override + public StopUserClient<ISession> getStopUserClient(int userId) { + return new TestStopUserClient(mContext, () -> mSession, mToken, userId, + TEST_SENSOR_ID, mBiometricLogger, mBiometricContext, + mUserStoppedCallback, () -> mShouldFailStopUser); + } + + @NonNull + @Override + public StartUserClient<IFingerprint, ISession> getStartUserClient( + int newUserId) { + mStartUserClientCount++; + return new TestStartUserClient(mContext, () -> mFingerprint, mToken, + newUserId, TEST_SENSOR_ID, mBiometricLogger, mBiometricContext, + mUserStartedCallback, mStartOperationsFinish); + } + }); + } else { + mScheduler = new BiometricScheduler<>( + new Handler(TestableLooper.get(this).getLooper()), + BiometricScheduler.SENSOR_TYPE_UNKNOWN, null /* gestureAvailabilityTracker */, + mBiometricService, LOG_NUM_RECENT_OPERATIONS); + } } @Test @@ -479,6 +541,7 @@ public class BiometricSchedulerTest { final boolean isEnroll = client instanceof TestEnrollClient; mScheduler.scheduleClientMonitor(client); + waitForIdle(); if (started) { mScheduler.startPreparedClient(client.getCookie()); } @@ -789,6 +852,172 @@ public class BiometricSchedulerTest { assertEquals(1, client1.getFingerprints().size()); } + @Test + @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL) + public void testScheduleOperation_whenNoUser() { + mCurrentUserId = UserHandle.USER_NULL; + + final BaseClientMonitor nextClient = mock(BaseClientMonitor.class); + when(nextClient.getTargetUserId()).thenReturn(0); + + mScheduler.scheduleClientMonitor(nextClient); + waitForIdle(); + + assertThat(mUsersStoppedCount).isEqualTo(0); + assertThat(mStartedUsers).containsExactly(0); + verify(nextClient).start(any()); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL) + public void testScheduleOperation_whenNoUser_notStarted() { + mCurrentUserId = UserHandle.USER_NULL; + mStartOperationsFinish = false; + + final BaseClientMonitor[] nextClients = new BaseClientMonitor[]{ + mock(BaseClientMonitor.class), + mock(BaseClientMonitor.class), + mock(BaseClientMonitor.class) + }; + for (BaseClientMonitor client : nextClients) { + when(client.getTargetUserId()).thenReturn(5); + mScheduler.scheduleClientMonitor(client); + waitForIdle(); + } + + assertThat(mUsersStoppedCount).isEqualTo(0); + assertThat(mStartedUsers).isEmpty(); + assertThat(mStartUserClientCount).isEqualTo(1); + for (BaseClientMonitor client : nextClients) { + verify(client, never()).start(any()); + } + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL) + public void testScheduleOperation_whenNoUser_notStarted_andReset() { + mCurrentUserId = UserHandle.USER_NULL; + mStartOperationsFinish = false; + + final BaseClientMonitor client = mock(BaseClientMonitor.class); + + when(client.getTargetUserId()).thenReturn(5); + + mScheduler.scheduleClientMonitor(client); + waitForIdle(); + + final TestStartUserClient startUserClient = + (TestStartUserClient) mScheduler.mCurrentOperation.getClientMonitor(); + mScheduler.reset(); + + assertThat(mScheduler.mCurrentOperation).isNull(); + + final BiometricSchedulerOperation fakeOperation = new BiometricSchedulerOperation( + mock(BaseClientMonitor.class), new ClientMonitorCallback() {}); + mScheduler.mCurrentOperation = fakeOperation; + startUserClient.mCallback.onClientFinished(startUserClient, true); + + assertThat(fakeOperation).isSameInstanceAs(mScheduler.mCurrentOperation); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL) + public void testScheduleOperation_whenSameUser() { + mCurrentUserId = 10; + + BaseClientMonitor nextClient = mock(BaseClientMonitor.class); + when(nextClient.getTargetUserId()).thenReturn(mCurrentUserId); + + mScheduler.scheduleClientMonitor(nextClient); + + waitForIdle(); + + verify(nextClient).start(any()); + assertThat(mUsersStoppedCount).isEqualTo(0); + assertThat(mStartedUsers).isEmpty(); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL) + public void testScheduleOperation_whenDifferentUser() { + mCurrentUserId = 10; + + final int nextUserId = 11; + BaseClientMonitor nextClient = mock(BaseClientMonitor.class); + when(nextClient.getTargetUserId()).thenReturn(nextUserId); + + mScheduler.scheduleClientMonitor(nextClient); + + waitForIdle(); + assertThat(mUsersStoppedCount).isEqualTo(1); + + waitForIdle(); + assertThat(mStartedUsers).containsExactly(nextUserId); + + waitForIdle(); + verify(nextClient).start(any()); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL) + public void testStartUser_alwaysStartsNextOperation() { + mCurrentUserId = UserHandle.USER_NULL; + + BaseClientMonitor nextClient = mock(BaseClientMonitor.class); + when(nextClient.getTargetUserId()).thenReturn(10); + + mScheduler.scheduleClientMonitor(nextClient); + + waitForIdle(); + verify(nextClient).start(any()); + + // finish first operation + mScheduler.getInternalCallback().onClientFinished(nextClient, true /* success */); + waitForIdle(); + + // schedule second operation but swap out the current operation + // before it runs so that it's not current when it's completion callback runs + nextClient = mock(BaseClientMonitor.class); + when(nextClient.getTargetUserId()).thenReturn(11); + mScheduler.scheduleClientMonitor(nextClient); + + waitForIdle(); + verify(nextClient).start(any()); + assertThat(mStartedUsers).containsExactly(10, 11).inOrder(); + assertThat(mUsersStoppedCount).isEqualTo(1); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL) + public void testStartUser_failsClearsStopUserClient() { + mCurrentUserId = UserHandle.USER_NULL; + + // When a stop user client fails, check that mStopUserClient + // is set to null to prevent the scheduler from getting stuck. + BaseClientMonitor nextClient = mock(BaseClientMonitor.class); + when(nextClient.getTargetUserId()).thenReturn(10); + + mScheduler.scheduleClientMonitor(nextClient); + + waitForIdle(); + verify(nextClient).start(any()); + + // finish first operation + mScheduler.getInternalCallback().onClientFinished(nextClient, true /* success */); + waitForIdle(); + + // schedule second operation but swap out the current operation + // before it runs so that it's not current when it's completion callback runs + nextClient = mock(BaseClientMonitor.class); + when(nextClient.getTargetUserId()).thenReturn(11); + mShouldFailStopUser = true; + mScheduler.scheduleClientMonitor(nextClient); + + waitForIdle(); + assertThat(mStartedUsers).containsExactly(10, 11).inOrder(); + assertThat(mUsersStoppedCount).isEqualTo(0); + assertThat(mScheduler.getStopUserClient()).isEqualTo(null); + } private BiometricSchedulerProto getDump(boolean clearSchedulerBuffer) throws Exception { return BiometricSchedulerProto.parseFrom(mScheduler.dumpProtoState(clearSchedulerBuffer)); @@ -1069,4 +1298,82 @@ public class BiometricSchedulerTest { return mFingerprints; } } + + private interface StopUserClientShouldFail { + boolean shouldFail(); + } + + private class TestStopUserClient extends StopUserClient<ISession> { + private StopUserClientShouldFail mShouldFailClient; + TestStopUserClient(@NonNull Context context, + @NonNull Supplier<ISession> lazyDaemon, @Nullable IBinder token, int userId, + int sensorId, @NonNull BiometricLogger logger, + @NonNull BiometricContext biometricContext, + @NonNull UserStoppedCallback callback, StopUserClientShouldFail shouldFail) { + super(context, lazyDaemon, token, userId, sensorId, logger, biometricContext, callback); + mShouldFailClient = shouldFail; + } + + @Override + protected void startHalOperation() { + + } + + @Override + public void start(@NonNull ClientMonitorCallback callback) { + super.start(callback); + if (mShouldFailClient.shouldFail()) { + getCallback().onClientFinished(this, false /* success */); + // When the above fails, it means that the HAL has died, in this case we + // need to ensure the UserSwitchCallback correctly returns the NULL user handle. + mCurrentUserId = UserHandle.USER_NULL; + } else { + onUserStopped(); + } + } + + @Override + public void unableToStart() { + + } + } + + private static class TestStartUserClient extends StartUserClient<IFingerprint, ISession> { + + @Mock + private ISession mSession; + private final boolean mShouldFinish; + ClientMonitorCallback mCallback; + + TestStartUserClient(@NonNull Context context, + @NonNull Supplier<IFingerprint> lazyDaemon, @Nullable IBinder token, int userId, + int sensorId, @NonNull BiometricLogger logger, + @NonNull BiometricContext biometricContext, + @NonNull UserStartedCallback<ISession> callback, boolean shouldFinish) { + super(context, lazyDaemon, token, userId, sensorId, logger, biometricContext, callback); + mShouldFinish = shouldFinish; + } + + @Override + protected void startHalOperation() { + + } + + @Override + public void start(@NonNull ClientMonitorCallback callback) { + super.start(callback); + + mCallback = callback; + if (mShouldFinish) { + mUserStartedCallback.onUserStarted( + getTargetUserId(), mSession, 1 /* halInterfaceVersion */); + callback.onClientFinished(this, true /* success */); + } + } + + @Override + public void unableToStart() { + + } + } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java index 8b1a2915820a..772ec8b73393 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java @@ -36,8 +36,10 @@ import android.hardware.biometrics.face.IFace; import android.hardware.biometrics.face.ISession; import android.hardware.biometrics.face.SensorProps; import android.hardware.face.HidlFaceSensorConfig; +import android.os.Handler; import android.os.RemoteException; import android.os.UserManager; +import android.os.test.TestLooper; import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; @@ -88,14 +90,11 @@ public class FaceProviderTest { @Mock private BiometricStateCallback mBiometricStateCallback; + private final TestLooper mLooper = new TestLooper(); private SensorProps[] mSensorProps; private LockoutResetDispatcher mLockoutResetDispatcher; private FaceProvider mFaceProvider; - private static void waitForIdle() { - InstrumentationRegistry.getInstrumentation().waitForIdleSync(); - } - @Before public void setUp() throws RemoteException { MockitoAnnotations.initMocks(this); @@ -121,7 +120,8 @@ public class FaceProviderTest { mFaceProvider = new FaceProvider(mContext, mBiometricStateCallback, mSensorProps, TAG, mLockoutResetDispatcher, mBiometricContext, - mDaemon, false /* resetLockoutRequiresChallenge */, false /* testHalEnabled */); + mDaemon, new Handler(mLooper.getLooper()), + false /* resetLockoutRequiresChallenge */, false /* testHalEnabled */); } @Test @@ -156,6 +156,7 @@ public class FaceProviderTest { mFaceProvider = new FaceProvider(mContext, mBiometricStateCallback, hidlFaceSensorConfig, TAG, mLockoutResetDispatcher, mBiometricContext, mDaemon, + new Handler(mLooper.getLooper()), true /* resetLockoutRequiresChallenge */, true /* testHalEnabled */); @@ -210,4 +211,12 @@ public class FaceProviderTest { assertEquals(0, scheduler.getCurrentPendingCount()); } } + + private void waitForIdle() { + if (Flags.deHidl()) { + mLooper.dispatchAll(); + } else { + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + } + } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java index e7f7195588ff..fe9cd4353603 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java @@ -31,6 +31,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.hardware.biometrics.IBiometricService; import android.hardware.biometrics.common.CommonProps; +import android.hardware.biometrics.face.IFace; import android.hardware.biometrics.face.ISession; import android.hardware.biometrics.face.SensorProps; import android.hardware.face.FaceSensorPropertiesInternal; @@ -40,6 +41,7 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import com.android.server.biometrics.Flags; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.AuthSessionCoordinator; @@ -49,6 +51,7 @@ import com.android.server.biometrics.sensors.LockoutCache; import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.LockoutTracker; import com.android.server.biometrics.sensors.UserAwareBiometricScheduler; +import com.android.server.biometrics.sensors.UserSwitchProvider; import org.junit.Before; import org.junit.Test; @@ -74,6 +77,8 @@ public class SensorTest { @Mock private UserAwareBiometricScheduler.UserSwitchCallback mUserSwitchCallback; @Mock + private UserSwitchProvider<IFace, ISession> mUserSwitchProvider; + @Mock private AidlResponseHandler.HardwareUnavailableCallback mHardwareUnavailableCallback; @Mock private LockoutResetDispatcher mLockoutResetDispatcher; @@ -84,16 +89,16 @@ public class SensorTest { @Mock private AuthSessionCoordinator mAuthSessionCoordinator; @Mock - FaceProvider mFaceProvider; + private FaceProvider mFaceProvider; @Mock - BaseClientMonitor mClientMonitor; + private BaseClientMonitor mClientMonitor; @Mock - AidlSession mCurrentSession; + private AidlSession mCurrentSession; private final TestLooper mLooper = new TestLooper(); private final LockoutCache mLockoutCache = new LockoutCache(); - private UserAwareBiometricScheduler mScheduler; + private BiometricScheduler<IFace, ISession> mScheduler; private AidlResponseHandler mHalCallback; @Before @@ -101,16 +106,26 @@ public class SensorTest { MockitoAnnotations.initMocks(this); when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService); - when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator); - mScheduler = new UserAwareBiometricScheduler(TAG, - new Handler(mLooper.getLooper()), - BiometricScheduler.SENSOR_TYPE_FACE, - null /* gestureAvailabilityDispatcher */, - mBiometricService, - () -> USER_ID, - mUserSwitchCallback); + if (Flags.deHidl()) { + mScheduler = new BiometricScheduler<>( + new Handler(mLooper.getLooper()), + BiometricScheduler.SENSOR_TYPE_FACE, + null /* gestureAvailabilityDispatcher */, + mBiometricService, + 2 /* recentOperationsLimit */, + () -> USER_ID, + mUserSwitchProvider); + } else { + mScheduler = new UserAwareBiometricScheduler<>(TAG, + new Handler(mLooper.getLooper()), + BiometricScheduler.SENSOR_TYPE_FACE, + null /* gestureAvailabilityDispatcher */, + mBiometricService, + () -> USER_ID, + mUserSwitchCallback); + } mHalCallback = new AidlResponseHandler(mContext, mScheduler, SENSOR_ID, USER_ID, mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator, mHardwareUnavailableCallback); @@ -146,18 +161,8 @@ public class SensorTest { @Test public void onBinderDied_noErrorOnNullClient() { mLooper.dispatchAll(); - - final SensorProps sensorProps = new SensorProps(); - sensorProps.commonProps = new CommonProps(); - sensorProps.commonProps.sensorId = 1; - final FaceSensorPropertiesInternal internalProp = new FaceSensorPropertiesInternal( - sensorProps.commonProps.sensorId, sensorProps.commonProps.sensorStrength, - sensorProps.commonProps.maxEnrollmentsPerUser, null /* componentInfo */, - sensorProps.sensorType, sensorProps.supportsDetectInteraction, - sensorProps.halControlsPreview, false /* resetLockoutRequiresChallenge */); - final Sensor sensor = new Sensor("SensorTest", mFaceProvider, mContext, - null /* handler */, internalProp, mLockoutResetDispatcher, mBiometricContext); - sensor.init(mLockoutResetDispatcher, mFaceProvider); + final Sensor sensor = getSensor(); + mScheduler = sensor.getScheduler(); mScheduler.reset(); assertNull(mScheduler.getCurrentClient()); @@ -175,18 +180,8 @@ public class SensorTest { when(mClientMonitor.getTargetUserId()).thenReturn(USER_ID); when(mClientMonitor.isInterruptable()).thenReturn(false); - final SensorProps sensorProps = new SensorProps(); - sensorProps.commonProps = new CommonProps(); - sensorProps.commonProps.sensorId = 1; - final FaceSensorPropertiesInternal internalProp = new FaceSensorPropertiesInternal( - sensorProps.commonProps.sensorId, sensorProps.commonProps.sensorStrength, - sensorProps.commonProps.maxEnrollmentsPerUser, null /* componentInfo */, - sensorProps.sensorType, sensorProps.supportsDetectInteraction, - sensorProps.halControlsPreview, false /* resetLockoutRequiresChallenge */); - final Sensor sensor = new Sensor("SensorTest", mFaceProvider, mContext, null, - internalProp, mLockoutResetDispatcher, mBiometricContext, mCurrentSession); - sensor.init(mLockoutResetDispatcher, mFaceProvider); - mScheduler = (UserAwareBiometricScheduler) sensor.getScheduler(); + final Sensor sensor = getSensor(); + mScheduler = sensor.getScheduler(); sensor.mCurrentSession = new AidlSession(0, mock(ISession.class), USER_ID, mHalCallback); @@ -206,4 +201,20 @@ public class SensorTest { verify(mLockoutResetDispatcher).notifyLockoutResetCallbacks(eq(SENSOR_ID)); verify(mAuthSessionCoordinator).resetLockoutFor(eq(USER_ID), anyInt(), anyLong()); } + + private Sensor getSensor() { + final SensorProps sensorProps = new SensorProps(); + sensorProps.commonProps = new CommonProps(); + sensorProps.commonProps.sensorId = 1; + final FaceSensorPropertiesInternal internalProp = new FaceSensorPropertiesInternal( + sensorProps.commonProps.sensorId, sensorProps.commonProps.sensorStrength, + sensorProps.commonProps.maxEnrollmentsPerUser, null /* componentInfo */, + sensorProps.sensorType, sensorProps.supportsDetectInteraction, + sensorProps.halControlsPreview, false /* resetLockoutRequiresChallenge */); + final Sensor sensor = new Sensor(mFaceProvider, mContext, + null /* handler */, internalProp, mBiometricContext); + sensor.init(mLockoutResetDispatcher, mFaceProvider); + + return sensor; + } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java index 4e43332ab52c..940fe69925b5 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java @@ -45,7 +45,6 @@ import androidx.test.core.app.ApplicationProvider; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.AuthSessionCoordinator; -import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.LockoutTracker; @@ -88,9 +87,9 @@ public class HidlToAidlSensorAdapterTest { @Mock private IBiometricsFace mDaemon; @Mock - AidlResponseHandler.AidlResponseHandlerCallback mAidlResponseHandlerCallback; + private AidlResponseHandler.AidlResponseHandlerCallback mAidlResponseHandlerCallback; @Mock - BiometricUtils<Face> mBiometricUtils; + private BiometricUtils<Face> mBiometricUtils; private final TestLooper mLooper = new TestLooper(); private HidlToAidlSensorAdapter mHidlToAidlSensorAdapter; @@ -118,20 +117,14 @@ public class HidlToAidlSensorAdapterTest { mContext.getOrCreateTestableResources(); final String config = String.format("%d:8:15", SENSOR_ID); - final BiometricScheduler scheduler = new BiometricScheduler(TAG, - new Handler(mLooper.getLooper()), - BiometricScheduler.SENSOR_TYPE_FACE, - null /* gestureAvailabilityTracker */, - mBiometricService, 10 /* recentOperationsLimit */); final HidlFaceSensorConfig faceSensorConfig = new HidlFaceSensorConfig(); faceSensorConfig.parse(config, mContext); - mHidlToAidlSensorAdapter = new HidlToAidlSensorAdapter(TAG, mFaceProvider, + mHidlToAidlSensorAdapter = new HidlToAidlSensorAdapter(mFaceProvider, mContext, new Handler(mLooper.getLooper()), faceSensorConfig, mLockoutResetDispatcherForSensor, mBiometricContext, false /* resetLockoutRequiresChallenge */, mInternalCleanupAndGetFeatureRunnable, mAuthSessionCoordinator, mDaemon, mAidlResponseHandlerCallback); mHidlToAidlSensorAdapter.init(mLockoutResetDispatcherForSensor, mFaceProvider); - mHidlToAidlSensorAdapter.setScheduler(scheduler); mHidlToAidlSensorAdapter.handleUserChanged(USER_ID); } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java index bf5986c1d0f3..258be573d005 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java @@ -38,8 +38,10 @@ import android.hardware.biometrics.fingerprint.ISession; import android.hardware.biometrics.fingerprint.SensorLocation; import android.hardware.biometrics.fingerprint.SensorProps; import android.hardware.fingerprint.HidlFingerprintSensorConfig; +import android.os.Handler; import android.os.RemoteException; import android.os.UserManager; +import android.os.test.TestLooper; import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; @@ -92,14 +94,12 @@ public class FingerprintProviderTest { @Mock private BiometricContext mBiometricContext; + private final TestLooper mLooper = new TestLooper(); + private SensorProps[] mSensorProps; private LockoutResetDispatcher mLockoutResetDispatcher; private FingerprintProvider mFingerprintProvider; - private static void waitForIdle() { - InstrumentationRegistry.getInstrumentation().waitForIdleSync(); - } - @Before public void setUp() throws RemoteException { MockitoAnnotations.initMocks(this); @@ -126,7 +126,8 @@ public class FingerprintProviderTest { mFingerprintProvider = new FingerprintProvider(mContext, mBiometricStateCallback, mAuthenticationStateListeners, mSensorProps, TAG, mLockoutResetDispatcher, mGestureAvailabilityDispatcher, mBiometricContext, - mDaemon, false /* resetLockoutRequiresHardwareAuthToken */, + mDaemon, new Handler(mLooper.getLooper()), + false /* resetLockoutRequiresHardwareAuthToken */, true /* testHalEnabled */); } @@ -159,6 +160,7 @@ public class FingerprintProviderTest { mBiometricStateCallback, mAuthenticationStateListeners, hidlFingerprintSensorConfigs, TAG, mLockoutResetDispatcher, mGestureAvailabilityDispatcher, mBiometricContext, mDaemon, + new Handler(mLooper.getLooper()), false /* resetLockoutRequiresHardwareAuthToken */, true /* testHalEnabled */); @@ -215,4 +217,12 @@ public class FingerprintProviderTest { assertEquals(0, scheduler.getCurrentPendingCount()); } } + + private void waitForIdle() { + if (Flags.deHidl()) { + mLooper.dispatchAll(); + } else { + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + } + } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java index 126a05e12628..b4c2ee8f89bf 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java @@ -32,14 +32,17 @@ import android.content.Context; import android.hardware.biometrics.IBiometricService; import android.hardware.biometrics.common.CommonProps; import android.hardware.biometrics.face.SensorProps; +import android.hardware.biometrics.fingerprint.IFingerprint; import android.hardware.biometrics.fingerprint.ISession; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.os.Handler; +import android.os.HandlerThread; import android.os.test.TestLooper; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import com.android.server.biometrics.Flags; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.AuthSessionCoordinator; @@ -49,6 +52,7 @@ import com.android.server.biometrics.sensors.LockoutCache; import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.LockoutTracker; import com.android.server.biometrics.sensors.UserAwareBiometricScheduler; +import com.android.server.biometrics.sensors.UserSwitchProvider; import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher; import org.junit.Before; @@ -75,6 +79,8 @@ public class SensorTest { @Mock private UserAwareBiometricScheduler.UserSwitchCallback mUserSwitchCallback; @Mock + private UserSwitchProvider<IFingerprint, ISession> mUserSwitchProvider; + @Mock private AidlResponseHandler.HardwareUnavailableCallback mHardwareUnavailableCallback; @Mock private LockoutResetDispatcher mLockoutResetDispatcher; @@ -92,11 +98,13 @@ public class SensorTest { private AidlSession mCurrentSession; @Mock private BaseClientMonitor mClientMonitor; + @Mock + private HandlerThread mThread; private final TestLooper mLooper = new TestLooper(); private final LockoutCache mLockoutCache = new LockoutCache(); - private BiometricScheduler mScheduler; + private BiometricScheduler<IFingerprint, ISession> mScheduler; private AidlResponseHandler mHalCallback; @Before @@ -105,14 +113,26 @@ public class SensorTest { when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService); when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator); - - mScheduler = new UserAwareBiometricScheduler(TAG, - new Handler(mLooper.getLooper()), - BiometricScheduler.SENSOR_TYPE_FP_OTHER, - null /* gestureAvailabilityDispatcher */, - mBiometricService, - () -> USER_ID, - mUserSwitchCallback); + when(mThread.getLooper()).thenReturn(mLooper.getLooper()); + + if (Flags.deHidl()) { + mScheduler = new BiometricScheduler<>( + new Handler(mLooper.getLooper()), + BiometricScheduler.SENSOR_TYPE_FP_OTHER, + null /* gestureAvailabilityDispatcher */, + mBiometricService, + 2 /* recentOperationsLimit */, + () -> USER_ID, + mUserSwitchProvider); + } else { + mScheduler = new UserAwareBiometricScheduler<>(TAG, + new Handler(mLooper.getLooper()), + BiometricScheduler.SENSOR_TYPE_FP_OTHER, + null /* gestureAvailabilityDispatcher */, + mBiometricService, + () -> USER_ID, + mUserSwitchCallback); + } mHalCallback = new AidlResponseHandler(mContext, mScheduler, SENSOR_ID, USER_ID, mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator, mHardwareUnavailableCallback); @@ -153,18 +173,7 @@ public class SensorTest { when(mClientMonitor.getTargetUserId()).thenReturn(USER_ID); when(mClientMonitor.isInterruptable()).thenReturn(false); - final SensorProps sensorProps = new SensorProps(); - sensorProps.commonProps = new CommonProps(); - sensorProps.commonProps.sensorId = 1; - final FingerprintSensorPropertiesInternal internalProp = new - FingerprintSensorPropertiesInternal( - sensorProps.commonProps.sensorId, sensorProps.commonProps.sensorStrength, - sensorProps.commonProps.maxEnrollmentsPerUser, null, - sensorProps.sensorType, false /* resetLockoutRequiresHardwareAuthToken */); - final Sensor sensor = new Sensor("SensorTest", mFingerprintProvider, mContext, - null /* handler */, internalProp, mLockoutResetDispatcher, - mGestureAvailabilityDispatcher, mBiometricContext, mCurrentSession); - sensor.init(mGestureAvailabilityDispatcher, mLockoutResetDispatcher); + final Sensor sensor = getSensor(); mScheduler = sensor.getScheduler(); sensor.mCurrentSession = new AidlSession(0, mock(ISession.class), USER_ID, mHalCallback); @@ -185,4 +194,21 @@ public class SensorTest { verify(mLockoutResetDispatcher).notifyLockoutResetCallbacks(eq(SENSOR_ID)); verify(mAuthSessionCoordinator).resetLockoutFor(eq(USER_ID), anyInt(), anyLong()); } + + private Sensor getSensor() { + final SensorProps sensorProps = new SensorProps(); + sensorProps.commonProps = new CommonProps(); + sensorProps.commonProps.sensorId = 1; + final FingerprintSensorPropertiesInternal internalProp = new + FingerprintSensorPropertiesInternal( + sensorProps.commonProps.sensorId, sensorProps.commonProps.sensorStrength, + sensorProps.commonProps.maxEnrollmentsPerUser, null, + sensorProps.sensorType, false /* resetLockoutRequiresHardwareAuthToken */); + final Sensor sensor = new Sensor(mFingerprintProvider, mContext, + null /* handler */, internalProp, + mBiometricContext, mCurrentSession); + sensor.init(mGestureAvailabilityDispatcher, mLockoutResetDispatcher); + + return sensor; + } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java index 89a49615dbe1..cbbc54547bf6 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java @@ -36,12 +36,12 @@ import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.HidlFingerprintSensorConfig; import android.os.Handler; +import android.os.HandlerThread; import android.os.RemoteException; import android.os.test.TestLooper; import android.platform.test.annotations.Presubmit; import android.testing.TestableContext; -import androidx.annotation.NonNull; import androidx.test.core.app.ApplicationProvider; import androidx.test.filters.SmallTest; @@ -49,17 +49,11 @@ import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.AuthSessionCoordinator; import com.android.server.biometrics.sensors.AuthenticationStateListeners; -import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.BiometricUtils; -import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.LockoutTracker; -import com.android.server.biometrics.sensors.StartUserClient; -import com.android.server.biometrics.sensors.StopUserClient; -import com.android.server.biometrics.sensors.UserAwareBiometricScheduler; import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher; import com.android.server.biometrics.sensors.fingerprint.aidl.AidlResponseHandler; -import com.android.server.biometrics.sensors.fingerprint.aidl.AidlSession; import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintEnrollClient; import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintProvider; import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintResetLockoutClient; @@ -111,60 +105,18 @@ public class HidlToAidlSensorAdapterTest { private BiometricUtils<Fingerprint> mBiometricUtils; @Mock private AuthenticationStateListeners mAuthenticationStateListeners; + @Mock + private HandlerThread mThread; private final TestLooper mLooper = new TestLooper(); private HidlToAidlSensorAdapter mHidlToAidlSensorAdapter; private final TestableContext mContext = new TestableContext( ApplicationProvider.getApplicationContext()); - private final UserAwareBiometricScheduler.UserSwitchCallback mUserSwitchCallback = - new UserAwareBiometricScheduler.UserSwitchCallback() { - @NonNull - @Override - public StopUserClient<?> getStopUserClient(int userId) { - return new StopUserClient<IBiometricsFingerprint>(mContext, - mHidlToAidlSensorAdapter::getIBiometricsFingerprint, null, USER_ID, - SENSOR_ID, mLogger, mBiometricContext, () -> {}) { - @Override - protected void startHalOperation() { - getCallback().onClientFinished(this, true /* success */); - } - - @Override - public void unableToStart() {} - }; - } - - @NonNull - @Override - public StartUserClient<?, ?> getStartUserClient(int newUserId) { - return new StartUserClient<IBiometricsFingerprint, AidlSession>(mContext, - mHidlToAidlSensorAdapter::getIBiometricsFingerprint, null, - USER_ID, SENSOR_ID, - mLogger, mBiometricContext, - (newUserId1, newUser, halInterfaceVersion) -> - mHidlToAidlSensorAdapter.handleUserChanged(newUserId1)) { - @Override - public void start(@NonNull ClientMonitorCallback callback) { - super.start(callback); - startHalOperation(); - } - - @Override - protected void startHalOperation() { - mUserStartedCallback.onUserStarted(USER_ID, null, 0); - getCallback().onClientFinished(this, true /* success */); - } - - @Override - public void unableToStart() {} - }; - } - };; - @Before public void setUp() throws RemoteException { when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator); + when(mThread.getLooper()).thenReturn(mLooper.getLooper()); doAnswer((answer) -> { mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback() .onEnrollmentProgress(1 /* enrollmentId */, 0 /* remaining */); @@ -175,26 +127,18 @@ public class HidlToAidlSensorAdapterTest { mContext.getOrCreateTestableResources(); final String config = String.format("%d:2:15", SENSOR_ID); - final UserAwareBiometricScheduler scheduler = new UserAwareBiometricScheduler(TAG, - new Handler(mLooper.getLooper()), - BiometricScheduler.SENSOR_TYPE_FP_OTHER, - null /* gestureAvailabilityDispatcher */, - mBiometricService, - () -> USER_ID, - mUserSwitchCallback); final HidlFingerprintSensorConfig fingerprintSensorConfig = new HidlFingerprintSensorConfig(); fingerprintSensorConfig.parse(config, mContext); - mHidlToAidlSensorAdapter = new HidlToAidlSensorAdapter(TAG, + mHidlToAidlSensorAdapter = new HidlToAidlSensorAdapter( mFingerprintProvider, mContext, new Handler(mLooper.getLooper()), fingerprintSensorConfig, mLockoutResetDispatcherForSensor, - mGestureAvailabilityDispatcher, mBiometricContext, - false /* resetLockoutRequiresHardwareAuthToken */, + mBiometricContext, false /* resetLockoutRequiresHardwareAuthToken */, mInternalCleanupRunnable, mAuthSessionCoordinator, mDaemon, mAidlResponseHandlerCallback); mHidlToAidlSensorAdapter.init(mGestureAvailabilityDispatcher, mLockoutResetDispatcherForSensor); - mHidlToAidlSensorAdapter.setScheduler(scheduler); + mHidlToAidlSensorAdapter.handleUserChanged(USER_ID); } diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java index 276c8321fb65..e1f490ae3e2f 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java @@ -341,7 +341,8 @@ public class VirtualDeviceManagerServiceTest { mSetFlagsRule.initAllFlagsToReleaseConfigDefault(); doReturn(true).when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt()); - doNothing().when(mInputManagerInternalMock).setPointerAcceleration(anyFloat(), anyInt()); + doNothing().when(mInputManagerInternalMock) + .setMousePointerAccelerationEnabled(anyBoolean(), anyInt()); doNothing().when(mInputManagerInternalMock).setPointerIconVisible(anyBoolean(), anyInt()); LocalServices.removeServiceForTest(InputManagerInternal.class); LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java index f5d50d173466..6986cab72f56 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java @@ -305,9 +305,9 @@ public abstract class BaseLockSettingsServiceTests { doAnswer(invocation -> { Object[] args = invocation.getArguments(); mStorageManager.unlockCeStorage(/* userId= */ (int) args[0], - /* secret= */ (byte[]) args[2]); + /* secret= */ (byte[]) args[1]); return null; - }).when(sm).unlockCeStorage(anyInt(), anyInt(), any()); + }).when(sm).unlockCeStorage(anyInt(), any()); doAnswer(invocation -> { Object[] args = invocation.getArguments(); diff --git a/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java index daf18edaf2de..3069d25e39d7 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java @@ -16,6 +16,10 @@ package com.android.server.pm; +import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; +import static android.Manifest.permission.QUERY_ALL_PACKAGES; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -27,6 +31,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -97,7 +102,6 @@ public final class BackgroundInstallControlServiceTest { private Looper mLooper; private File mFile; - @Mock private Context mContext; @Mock @@ -108,8 +112,12 @@ public final class BackgroundInstallControlServiceTest { private UsageStatsManagerInternal mUsageStatsManagerInternal; @Mock private PermissionManagerServiceInternal mPermissionManager; + @Mock + private BackgroundInstallControlCallbackHelper mCallbackHelper; + @Captor private ArgumentCaptor<PackageManagerInternal.PackageListObserver> mPackageListObserverCaptor; + @Captor private ArgumentCaptor<UsageEventListener> mUsageEventListenerCaptor; @@ -119,11 +127,12 @@ public final class BackgroundInstallControlServiceTest { mTestLooper = new TestLooper(); mLooper = mTestLooper.getLooper(); - mFile = new File( - InstrumentationRegistry.getInstrumentation().getContext().getCacheDir(), - "test"); - mBackgroundInstallControlService = new BackgroundInstallControlService( - new MockInjector(mContext)); + mFile = + new File( + InstrumentationRegistry.getInstrumentation().getContext().getCacheDir(), + "test"); + mBackgroundInstallControlService = + new BackgroundInstallControlService(new MockInjector(mContext)); verify(mUsageStatsManagerInternal).registerListener(mUsageEventListenerCaptor.capture()); mUsageEventListener = mUsageEventListenerCaptor.getValue(); @@ -143,8 +152,7 @@ public final class BackgroundInstallControlServiceTest { assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); mBackgroundInstallControlService.initBackgroundInstalledPackages(); assertNotNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); - assertEquals(0, - mBackgroundInstallControlService.getBackgroundInstalledPackages().size()); + assertEquals(0, mBackgroundInstallControlService.getBackgroundInstalledPackages().size()); } @Test @@ -161,12 +169,9 @@ public final class BackgroundInstallControlServiceTest { // Write test data to the file on the disk. try { ProtoOutputStream protoOutputStream = new ProtoOutputStream(fileOutputStream); - long token = protoOutputStream.start( - BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); - protoOutputStream.write( - BackgroundInstalledPackageProto.PACKAGE_NAME, PACKAGE_NAME_1); - protoOutputStream.write( - BackgroundInstalledPackageProto.USER_ID, USER_ID_1 + 1); + long token = protoOutputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); + protoOutputStream.write(BackgroundInstalledPackageProto.PACKAGE_NAME, PACKAGE_NAME_1); + protoOutputStream.write(BackgroundInstalledPackageProto.USER_ID, USER_ID_1 + 1); protoOutputStream.end(token); protoOutputStream.flush(); atomicFile.finishWrite(fileOutputStream); @@ -198,20 +203,14 @@ public final class BackgroundInstallControlServiceTest { try { ProtoOutputStream protoOutputStream = new ProtoOutputStream(fileOutputStream); - long token = protoOutputStream.start( - BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); - protoOutputStream.write( - BackgroundInstalledPackageProto.PACKAGE_NAME, PACKAGE_NAME_1); - protoOutputStream.write( - BackgroundInstalledPackageProto.USER_ID, USER_ID_1 + 1); + long token = protoOutputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); + protoOutputStream.write(BackgroundInstalledPackageProto.PACKAGE_NAME, PACKAGE_NAME_1); + protoOutputStream.write(BackgroundInstalledPackageProto.USER_ID, USER_ID_1 + 1); protoOutputStream.end(token); - token = protoOutputStream.start( - BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); - protoOutputStream.write( - BackgroundInstalledPackageProto.PACKAGE_NAME, PACKAGE_NAME_2); - protoOutputStream.write( - BackgroundInstalledPackageProto.USER_ID, USER_ID_2 + 1); + token = protoOutputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); + protoOutputStream.write(BackgroundInstalledPackageProto.PACKAGE_NAME, PACKAGE_NAME_2); + protoOutputStream.write(BackgroundInstalledPackageProto.USER_ID, USER_ID_2 + 1); protoOutputStream.end(token); protoOutputStream.flush(); @@ -241,7 +240,7 @@ public final class BackgroundInstallControlServiceTest { // Read the file on the disk to verify var packagesInDisk = new SparseSetArray<>(); AtomicFile atomicFile = new AtomicFile(mFile); - try (FileInputStream fileInputStream = atomicFile.openRead()) { + try (FileInputStream fileInputStream = atomicFile.openRead()) { ProtoInputStream protoInputStream = new ProtoInputStream(fileInputStream); while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { @@ -249,23 +248,25 @@ public final class BackgroundInstallControlServiceTest { != (int) BackgroundInstalledPackagesProto.BG_INSTALLED_PKG) { continue; } - long token = protoInputStream.start( - BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); + long token = + protoInputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); String packageName = null; int userId = UserHandle.USER_NULL; while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { switch (protoInputStream.getFieldNumber()) { case (int) BackgroundInstalledPackageProto.PACKAGE_NAME: - packageName = protoInputStream.readString( - BackgroundInstalledPackageProto.PACKAGE_NAME); + packageName = + protoInputStream.readString( + BackgroundInstalledPackageProto.PACKAGE_NAME); break; case (int) BackgroundInstalledPackageProto.USER_ID: - userId = protoInputStream.readInt( - BackgroundInstalledPackageProto.USER_ID) - 1; + userId = + protoInputStream.readInt( + BackgroundInstalledPackageProto.USER_ID) + - 1; break; default: - fail("Undefined field in proto: " - + protoInputStream.getFieldNumber()); + fail("Undefined field in proto: " + protoInputStream.getFieldNumber()); } } protoInputStream.end(token); @@ -296,7 +297,7 @@ public final class BackgroundInstallControlServiceTest { // Read the file on the disk to verify var packagesInDisk = new SparseSetArray<>(); AtomicFile atomicFile = new AtomicFile(mFile); - try (FileInputStream fileInputStream = atomicFile.openRead()) { + try (FileInputStream fileInputStream = atomicFile.openRead()) { ProtoInputStream protoInputStream = new ProtoInputStream(fileInputStream); while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { @@ -304,23 +305,25 @@ public final class BackgroundInstallControlServiceTest { != (int) BackgroundInstalledPackagesProto.BG_INSTALLED_PKG) { continue; } - long token = protoInputStream.start( - BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); + long token = + protoInputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); String packageName = null; int userId = UserHandle.USER_NULL; while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { switch (protoInputStream.getFieldNumber()) { case (int) BackgroundInstalledPackageProto.PACKAGE_NAME: - packageName = protoInputStream.readString( - BackgroundInstalledPackageProto.PACKAGE_NAME); + packageName = + protoInputStream.readString( + BackgroundInstalledPackageProto.PACKAGE_NAME); break; case (int) BackgroundInstalledPackageProto.USER_ID: - userId = protoInputStream.readInt( - BackgroundInstalledPackageProto.USER_ID) - 1; + userId = + protoInputStream.readInt( + BackgroundInstalledPackageProto.USER_ID) + - 1; break; default: - fail("Undefined field in proto: " - + protoInputStream.getFieldNumber()); + fail("Undefined field in proto: " + protoInputStream.getFieldNumber()); } } protoInputStream.end(token); @@ -353,7 +356,7 @@ public final class BackgroundInstallControlServiceTest { // Read the file on the disk to verify var packagesInDisk = new SparseSetArray<>(); AtomicFile atomicFile = new AtomicFile(mFile); - try (FileInputStream fileInputStream = atomicFile.openRead()) { + try (FileInputStream fileInputStream = atomicFile.openRead()) { ProtoInputStream protoInputStream = new ProtoInputStream(fileInputStream); while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { @@ -361,23 +364,25 @@ public final class BackgroundInstallControlServiceTest { != (int) BackgroundInstalledPackagesProto.BG_INSTALLED_PKG) { continue; } - long token = protoInputStream.start( - BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); + long token = + protoInputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); String packageName = null; int userId = UserHandle.USER_NULL; while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { switch (protoInputStream.getFieldNumber()) { case (int) BackgroundInstalledPackageProto.PACKAGE_NAME: - packageName = protoInputStream.readString( - BackgroundInstalledPackageProto.PACKAGE_NAME); + packageName = + protoInputStream.readString( + BackgroundInstalledPackageProto.PACKAGE_NAME); break; case (int) BackgroundInstalledPackageProto.USER_ID: - userId = protoInputStream.readInt( - BackgroundInstalledPackageProto.USER_ID) - 1; + userId = + protoInputStream.readInt( + BackgroundInstalledPackageProto.USER_ID) + - 1; break; default: - fail("Undefined field in proto: " - + protoInputStream.getFieldNumber()); + fail("Undefined field in proto: " + protoInputStream.getFieldNumber()); } } protoInputStream.end(token); @@ -399,51 +404,55 @@ public final class BackgroundInstallControlServiceTest { @Test public void testHandleUsageEvent_permissionDenied() { - assertEquals(0, - mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); - doReturn(PackageManager.PERMISSION_DENIED).when(mPermissionManager).checkPermission( - anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_1, INSTALLER_NAME_1, 0); + assertEquals( + 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + doReturn(PackageManager.PERMISSION_DENIED) + .when(mPermissionManager) + .checkPermission(anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_1, INSTALLER_NAME_1, 0); mTestLooper.dispatchAll(); - assertEquals(0, - mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + assertEquals( + 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); } @Test public void testHandleUsageEvent_permissionGranted() { - assertEquals(0, - mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); - doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( - anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_1, INSTALLER_NAME_1, 0); + assertEquals( + 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + doReturn(PERMISSION_GRANTED) + .when(mPermissionManager) + .checkPermission(anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_1, INSTALLER_NAME_1, 0); mTestLooper.dispatchAll(); - assertEquals(1, - mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + assertEquals( + 1, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); } @Test public void testHandleUsageEvent_ignoredEvent() { - assertEquals(0, - mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); - doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( - anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent(UsageEvents.Event.USER_INTERACTION, - USER_ID_1, INSTALLER_NAME_1, 0); + assertEquals( + 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + doReturn(PERMISSION_GRANTED) + .when(mPermissionManager) + .checkPermission(anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent(UsageEvents.Event.USER_INTERACTION, USER_ID_1, INSTALLER_NAME_1, 0); mTestLooper.dispatchAll(); - assertEquals(0, - mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + assertEquals( + 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); } @Test public void testHandleUsageEvent_firstActivityResumedHalfTimeFrame() { - assertEquals(0, - mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); - doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( - anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1); + assertEquals( + 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + doReturn(PERMISSION_GRANTED) + .when(mPermissionManager) + .checkPermission(anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent( + UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, + INSTALLER_NAME_1, + USAGE_EVENT_TIMESTAMP_1); mTestLooper.dispatchAll(); var installerForegroundTimeFrames = @@ -461,14 +470,18 @@ public final class BackgroundInstallControlServiceTest { @Test public void testHandleUsageEvent_firstActivityResumedOneTimeFrame() { - assertEquals(0, - mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); - doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( - anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1); - generateUsageEvent(Event.ACTIVITY_STOPPED, - USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); + assertEquals( + 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + doReturn(PERMISSION_GRANTED) + .when(mPermissionManager) + .checkPermission(anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent( + UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, + INSTALLER_NAME_1, + USAGE_EVENT_TIMESTAMP_1); + generateUsageEvent( + Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); mTestLooper.dispatchAll(); var installerForegroundTimeFrames = @@ -486,16 +499,23 @@ public final class BackgroundInstallControlServiceTest { @Test public void testHandleUsageEvent_firstActivityResumedOneAndHalfTimeFrame() { - assertEquals(0, - mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); - doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( - anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1); - generateUsageEvent(Event.ACTIVITY_STOPPED, - USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); - generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3); + assertEquals( + 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + doReturn(PERMISSION_GRANTED) + .when(mPermissionManager) + .checkPermission(anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent( + UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, + INSTALLER_NAME_1, + USAGE_EVENT_TIMESTAMP_1); + generateUsageEvent( + Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); + generateUsageEvent( + UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, + INSTALLER_NAME_1, + USAGE_EVENT_TIMESTAMP_3); mTestLooper.dispatchAll(); var installerForegroundTimeFrames = @@ -517,12 +537,13 @@ public final class BackgroundInstallControlServiceTest { @Test public void testHandleUsageEvent_firstNoneActivityResumed() { - assertEquals(0, - mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); - doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( - anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent(Event.ACTIVITY_STOPPED, - USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1); + assertEquals( + 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + doReturn(PERMISSION_GRANTED) + .when(mPermissionManager) + .checkPermission(anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent( + Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1); mTestLooper.dispatchAll(); var installerForegroundTimeFrames = @@ -535,27 +556,26 @@ public final class BackgroundInstallControlServiceTest { } @Test - public void testHandleUsageEvent_packageAddedNoUsageEvent() throws - NoSuchFieldException, PackageManager.NameNotFoundException { + public void testHandleUsageEvent_packageAddedNoUsageEvent() + throws NoSuchFieldException, PackageManager.NameNotFoundException { assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); - InstallSourceInfo installSourceInfo = new InstallSourceInfo( - /* initiatingPackageName = */ INSTALLER_NAME_1, - /* initiatingPackageSigningInfo = */ null, - /* originatingPackageName = */ null, - /* installingPackageName = */ INSTALLER_NAME_1); + InstallSourceInfo installSourceInfo = + new InstallSourceInfo( + /* initiatingPackageName= */ INSTALLER_NAME_1, + /* initiatingPackageSigningInfo= */ null, + /* originatingPackageName= */ null, + /* installingPackageName= */ INSTALLER_NAME_1); assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1); when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo); ApplicationInfo appInfo = mock(ApplicationInfo.class); - when(mPackageManager.getApplicationInfoAsUser( - eq(PACKAGE_NAME_1), - any(), - anyInt()) - ).thenReturn(appInfo); + when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt())) + .thenReturn(appInfo); - long createTimestamp = PACKAGE_ADD_TIMESTAMP_1 - - (System.currentTimeMillis() - SystemClock.uptimeMillis()); - FieldSetter.setField(appInfo, + long createTimestamp = + PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis()); + FieldSetter.setField( + appInfo, ApplicationInfo.class.getDeclaredField("createTimestamp"), createTimestamp); @@ -572,27 +592,26 @@ public final class BackgroundInstallControlServiceTest { } @Test - public void testHandleUsageEvent_packageAddedInsideTimeFrame() throws - NoSuchFieldException, PackageManager.NameNotFoundException { + public void testHandleUsageEvent_packageAddedInsideTimeFrame() + throws NoSuchFieldException, PackageManager.NameNotFoundException { assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); - InstallSourceInfo installSourceInfo = new InstallSourceInfo( - /* initiatingPackageName = */ INSTALLER_NAME_1, - /* initiatingPackageSigningInfo = */ null, - /* originatingPackageName = */ null, - /* installingPackageName = */ INSTALLER_NAME_1); + InstallSourceInfo installSourceInfo = + new InstallSourceInfo( + /* initiatingPackageName= */ INSTALLER_NAME_1, + /* initiatingPackageSigningInfo= */ null, + /* originatingPackageName= */ null, + /* installingPackageName= */ INSTALLER_NAME_1); assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1); when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo); ApplicationInfo appInfo = mock(ApplicationInfo.class); - when(mPackageManager.getApplicationInfoAsUser( - eq(PACKAGE_NAME_1), - any(), - anyInt()) - ).thenReturn(appInfo); + when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt())) + .thenReturn(appInfo); - long createTimestamp = PACKAGE_ADD_TIMESTAMP_1 - - (System.currentTimeMillis() - SystemClock.uptimeMillis()); - FieldSetter.setField(appInfo, + long createTimestamp = + PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis()); + FieldSetter.setField( + appInfo, ApplicationInfo.class.getDeclaredField("createTimestamp"), createTimestamp); @@ -604,12 +623,16 @@ public final class BackgroundInstallControlServiceTest { // The 2 usage events make the package adding inside a time frame. // So it's not a background install. Thus, it's null for the return of // mBackgroundInstallControlService.getBackgroundInstalledPackages() - doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( - anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1); - generateUsageEvent(Event.ACTIVITY_STOPPED, - USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); + doReturn(PERMISSION_GRANTED) + .when(mPermissionManager) + .checkPermission(anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent( + UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, + INSTALLER_NAME_1, + USAGE_EVENT_TIMESTAMP_1); + generateUsageEvent( + Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid); mTestLooper.dispatchAll(); @@ -617,27 +640,26 @@ public final class BackgroundInstallControlServiceTest { } @Test - public void testHandleUsageEvent_packageAddedOutsideTimeFrame1() throws - NoSuchFieldException, PackageManager.NameNotFoundException { + public void testHandleUsageEvent_packageAddedOutsideTimeFrame1() + throws NoSuchFieldException, PackageManager.NameNotFoundException { assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); - InstallSourceInfo installSourceInfo = new InstallSourceInfo( - /* initiatingPackageName = */ INSTALLER_NAME_1, - /* initiatingPackageSigningInfo = */ null, - /* originatingPackageName = */ null, - /* installingPackageName = */ INSTALLER_NAME_1); + InstallSourceInfo installSourceInfo = + new InstallSourceInfo( + /* initiatingPackageName= */ INSTALLER_NAME_1, + /* initiatingPackageSigningInfo= */ null, + /* originatingPackageName= */ null, + /* installingPackageName= */ INSTALLER_NAME_1); assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1); when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo); ApplicationInfo appInfo = mock(ApplicationInfo.class); - when(mPackageManager.getApplicationInfoAsUser( - eq(PACKAGE_NAME_1), - any(), - anyInt()) - ).thenReturn(appInfo); + when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt())) + .thenReturn(appInfo); - long createTimestamp = PACKAGE_ADD_TIMESTAMP_1 - - (System.currentTimeMillis() - SystemClock.uptimeMillis()); - FieldSetter.setField(appInfo, + long createTimestamp = + PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis()); + FieldSetter.setField( + appInfo, ApplicationInfo.class.getDeclaredField("createTimestamp"), createTimestamp); @@ -650,12 +672,16 @@ public final class BackgroundInstallControlServiceTest { // Compared to testHandleUsageEvent_packageAddedInsideTimeFrame, // it's a background install. Thus, it's not null for the return of // mBackgroundInstallControlService.getBackgroundInstalledPackages() - doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( - anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); - generateUsageEvent(Event.ACTIVITY_STOPPED, - USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3); + doReturn(PERMISSION_GRANTED) + .when(mPermissionManager) + .checkPermission(anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent( + UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, + INSTALLER_NAME_1, + USAGE_EVENT_TIMESTAMP_2); + generateUsageEvent( + Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3); mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid); mTestLooper.dispatchAll(); @@ -665,28 +691,28 @@ public final class BackgroundInstallControlServiceTest { assertEquals(1, packages.size()); assertTrue(packages.contains(USER_ID_1, PACKAGE_NAME_1)); } + @Test - public void testHandleUsageEvent_packageAddedOutsideTimeFrame2() throws - NoSuchFieldException, PackageManager.NameNotFoundException { + public void testHandleUsageEvent_packageAddedOutsideTimeFrame2() + throws NoSuchFieldException, PackageManager.NameNotFoundException { assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); - InstallSourceInfo installSourceInfo = new InstallSourceInfo( - /* initiatingPackageName = */ INSTALLER_NAME_1, - /* initiatingPackageSigningInfo = */ null, - /* originatingPackageName = */ null, - /* installingPackageName = */ INSTALLER_NAME_1); + InstallSourceInfo installSourceInfo = + new InstallSourceInfo( + /* initiatingPackageName= */ INSTALLER_NAME_1, + /* initiatingPackageSigningInfo= */ null, + /* originatingPackageName= */ null, + /* installingPackageName= */ INSTALLER_NAME_1); assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1); when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo); ApplicationInfo appInfo = mock(ApplicationInfo.class); - when(mPackageManager.getApplicationInfoAsUser( - eq(PACKAGE_NAME_1), - any(), - anyInt()) - ).thenReturn(appInfo); + when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt())) + .thenReturn(appInfo); - long createTimestamp = PACKAGE_ADD_TIMESTAMP_1 - - (System.currentTimeMillis() - SystemClock.uptimeMillis()); - FieldSetter.setField(appInfo, + long createTimestamp = + PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis()); + FieldSetter.setField( + appInfo, ApplicationInfo.class.getDeclaredField("createTimestamp"), createTimestamp); @@ -700,12 +726,16 @@ public final class BackgroundInstallControlServiceTest { // Compared to testHandleUsageEvent_packageAddedInsideTimeFrame, // it's a background install. Thus, it's not null for the return of // mBackgroundInstallControlService.getBackgroundInstalledPackages() - doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( - anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_2, INSTALLER_NAME_2, USAGE_EVENT_TIMESTAMP_2); - generateUsageEvent(Event.ACTIVITY_STOPPED, - USER_ID_2, INSTALLER_NAME_2, USAGE_EVENT_TIMESTAMP_3); + doReturn(PERMISSION_GRANTED) + .when(mPermissionManager) + .checkPermission(anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent( + UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_2, + INSTALLER_NAME_2, + USAGE_EVENT_TIMESTAMP_2); + generateUsageEvent( + Event.ACTIVITY_STOPPED, USER_ID_2, INSTALLER_NAME_2, USAGE_EVENT_TIMESTAMP_3); mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid); mTestLooper.dispatchAll(); @@ -715,31 +745,31 @@ public final class BackgroundInstallControlServiceTest { assertEquals(1, packages.size()); assertTrue(packages.contains(USER_ID_1, PACKAGE_NAME_1)); } + @Test - public void testHandleUsageEvent_packageAddedThroughAdb() throws - NoSuchFieldException, PackageManager.NameNotFoundException { + public void testHandleUsageEvent_packageAddedThroughAdb() + throws NoSuchFieldException, PackageManager.NameNotFoundException { assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); // This test is a duplicate of testHandleUsageEvent_packageAddedThroughAdb except the // initiatingPackageName used to be null but is now "com.android.shell". This test ensures // that the behavior is still the same for when the initiatingPackageName is null. - InstallSourceInfo installSourceInfo = new InstallSourceInfo( - /* initiatingPackageName = */ null, - /* initiatingPackageSigningInfo = */ null, - /* originatingPackageName = */ null, - /* installingPackageName = */ INSTALLER_NAME_1); + InstallSourceInfo installSourceInfo = + new InstallSourceInfo( + /* initiatingPackageName= */ null, + /* initiatingPackageSigningInfo= */ null, + /* originatingPackageName= */ null, + /* installingPackageName= */ INSTALLER_NAME_1); // b/265203007 when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo); ApplicationInfo appInfo = mock(ApplicationInfo.class); - when(mPackageManager.getApplicationInfoAsUser( - eq(PACKAGE_NAME_1), - any(), - anyInt()) - ).thenReturn(appInfo); + when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt())) + .thenReturn(appInfo); - long createTimestamp = PACKAGE_ADD_TIMESTAMP_1 - - (System.currentTimeMillis() - SystemClock.uptimeMillis()); - FieldSetter.setField(appInfo, + long createTimestamp = + PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis()); + FieldSetter.setField( + appInfo, ApplicationInfo.class.getDeclaredField("createTimestamp"), createTimestamp); @@ -751,12 +781,16 @@ public final class BackgroundInstallControlServiceTest { // for ADB installs the initiatingPackageName used to be null, despite being detected // as a background install. Since we do not want to treat side-loaded apps as background // install getBackgroundInstalledPackages() is expected to return null - doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( - anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); - generateUsageEvent(Event.ACTIVITY_STOPPED, - USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3); + doReturn(PERMISSION_GRANTED) + .when(mPermissionManager) + .checkPermission(anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent( + UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, + INSTALLER_NAME_1, + USAGE_EVENT_TIMESTAMP_2); + generateUsageEvent( + Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3); mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid); mTestLooper.dispatchAll(); @@ -764,31 +798,31 @@ public final class BackgroundInstallControlServiceTest { var packages = mBackgroundInstallControlService.getBackgroundInstalledPackages(); assertNull(packages); } + @Test - public void testHandleUsageEvent_packageAddedThroughAdb2() throws - NoSuchFieldException, PackageManager.NameNotFoundException { + public void testHandleUsageEvent_packageAddedThroughAdb2() + throws NoSuchFieldException, PackageManager.NameNotFoundException { assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); // This test is a duplicate of testHandleUsageEvent_packageAddedThroughAdb except the // initiatingPackageName used to be null but is now "com.android.shell". This test ensures // that the behavior is still the same after this change. - InstallSourceInfo installSourceInfo = new InstallSourceInfo( - /* initiatingPackageName = */ "com.android.shell", - /* initiatingPackageSigningInfo = */ null, - /* originatingPackageName = */ null, - /* installingPackageName = */ INSTALLER_NAME_1); + InstallSourceInfo installSourceInfo = + new InstallSourceInfo( + /* initiatingPackageName= */ "com.android.shell", + /* initiatingPackageSigningInfo= */ null, + /* originatingPackageName= */ null, + /* installingPackageName= */ INSTALLER_NAME_1); // b/265203007 when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo); ApplicationInfo appInfo = mock(ApplicationInfo.class); - when(mPackageManager.getApplicationInfoAsUser( - eq(PACKAGE_NAME_1), - any(), - anyInt()) - ).thenReturn(appInfo); + when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt())) + .thenReturn(appInfo); - long createTimestamp = PACKAGE_ADD_TIMESTAMP_1 - - (System.currentTimeMillis() - SystemClock.uptimeMillis()); - FieldSetter.setField(appInfo, + long createTimestamp = + PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis()); + FieldSetter.setField( + appInfo, ApplicationInfo.class.getDeclaredField("createTimestamp"), createTimestamp); @@ -800,12 +834,16 @@ public final class BackgroundInstallControlServiceTest { // for ADB installs the initiatingPackageName is com.android.shell, despite being detected // as a background install. Since we do not want to treat side-loaded apps as background // install getBackgroundInstalledPackages() is expected to return null - doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( - anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); - generateUsageEvent(Event.ACTIVITY_STOPPED, - USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3); + doReturn(PERMISSION_GRANTED) + .when(mPermissionManager) + .checkPermission(anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent( + UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, + INSTALLER_NAME_1, + USAGE_EVENT_TIMESTAMP_2); + generateUsageEvent( + Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3); mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid); mTestLooper.dispatchAll(); @@ -813,6 +851,7 @@ public final class BackgroundInstallControlServiceTest { var packages = mBackgroundInstallControlService.getBackgroundInstalledPackages(); assertNull(packages); } + @Test public void testPackageRemoved() { assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); @@ -859,8 +898,7 @@ public final class BackgroundInstallControlServiceTest { packages.add(packageInfo2); var packageInfo3 = makePackageInfo(PACKAGE_NAME_3); packages.add(packageInfo3); - doReturn(packages).when(mPackageManager).getInstalledPackagesAsUser( - any(), anyInt()); + doReturn(packages).when(mPackageManager).getInstalledPackagesAsUser(any(), anyInt()); var resultPackages = mBackgroundInstallControlService.getBackgroundInstalledPackages(0L, USER_ID_1); @@ -870,18 +908,44 @@ public final class BackgroundInstallControlServiceTest { assertFalse(resultPackages.getList().contains(packageInfo3)); } + @Test(expected = SecurityException.class) + public void enforceCallerQueryPackagesPermissionsThrowsSecurityException() { + doThrow(new SecurityException("test")).when(mContext) + .enforceCallingPermission(eq(QUERY_ALL_PACKAGES), anyString()); + + mBackgroundInstallControlService.enforceCallerQueryPackagesPermissions(); + } + + @Test + public void enforceCallerQueryPackagesPermissionsDoesNotThrowSecurityException() { + //enforceCallerQueryPackagesPermissions do not throw + + mBackgroundInstallControlService.enforceCallerQueryPackagesPermissions(); + } + + @Test(expected = SecurityException.class) + public void enforceCallerInteractCrossUserPermissionsThrowsSecurityException() { + doThrow(new SecurityException("test")).when(mContext) + .enforceCallingPermission(eq(INTERACT_ACROSS_USERS_FULL), anyString()); + + mBackgroundInstallControlService.enforceCallerInteractCrossUserPermissions(); + } + @Test + public void enforceCallerInteractCrossUserPermissionsDoesNotThrowSecurityException() { + //enforceCallerQueryPackagesPermissions do not throw + + mBackgroundInstallControlService.enforceCallerInteractCrossUserPermissions(); + } + /** * Mock a usage event occurring. * * @param usageEventId id of a usage event - * @param userId user id of a usage event - * @param pkgName package name of a usage event - * @param timestamp timestamp of a usage event + * @param userId user id of a usage event + * @param pkgName package name of a usage event + * @param timestamp timestamp of a usage event */ - private void generateUsageEvent(int usageEventId, - int userId, - String pkgName, - long timestamp) { + private void generateUsageEvent(int usageEventId, int userId, String pkgName, long timestamp) { Event event = new Event(usageEventId, timestamp); event.mPackage = pkgName; mUsageEventListener.onUsageEvent(userId, event); @@ -935,5 +999,10 @@ public final class BackgroundInstallControlServiceTest { public File getDiskFile() { return mFile; } + + @Override + public BackgroundInstallControlCallbackHelper getBackgroundInstallControlCallbackHelper() { + return mCallbackHelper; + } } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java index c10c3c28e9dd..9b25f58acc96 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java @@ -183,7 +183,6 @@ public class NotificationHistoryManagerTest extends UiServiceTestCase { assertThat(mHistoryManager.doesHistoryExistForUser(mProfileId)).isFalse(); verify(mDb, times(2)).disableHistory(); } - @Test public void testAddProfile_historyEnabledInPrimary() { // create a history @@ -610,4 +609,14 @@ public class NotificationHistoryManagerTest extends UiServiceTestCase { assertThat(mHistoryManager.isHistoryEnabled(USER_SYSTEM)).isFalse(); } + @Test + public void testDelayedPackageRemoval_userLocked() { + String pkg = "pkg"; + mHistoryManager.onPackageRemoved(USER_SYSTEM, pkg); + mHistoryManager.onUserUnlocked(USER_SYSTEM); + mHistoryManager.onUserStopped(USER_SYSTEM); + mHistoryManager.onPackageRemoved(USER_SYSTEM, pkg); + + // no exception, yay + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index 9eed974c701f..0224ff35219b 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -185,8 +185,9 @@ import java.util.concurrent.TimeUnit; @TestableLooper.RunWithLooper public class ZenModeHelperTest extends UiServiceTestCase { - private static final String EVENTS_DEFAULT_RULE_ID = "EVENTS_DEFAULT_RULE"; - private static final String SCHEDULE_DEFAULT_RULE_ID = "EVERY_NIGHT_DEFAULT_RULE"; + private static final String EVENTS_DEFAULT_RULE_ID = ZenModeConfig.EVENTS_DEFAULT_RULE_ID; + private static final String SCHEDULE_DEFAULT_RULE_ID = + ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID; private static final String CUSTOM_PKG_NAME = "not.android"; private static final String CUSTOM_APP_LABEL = "This is not Android"; private static final int CUSTOM_PKG_UID = 1; @@ -2330,6 +2331,68 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void addAutomaticZenRule_withTypeBedtime_replacesDisabledSleeping() { + ZenRule sleepingRule = createCustomAutomaticRule(ZEN_MODE_IMPORTANT_INTERRUPTIONS, + ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID); + sleepingRule.enabled = false; + sleepingRule.userModifiedFields = 0; + sleepingRule.name = "ZZZZZZZ..."; + mZenModeHelper.mConfig.automaticRules.clear(); + mZenModeHelper.mConfig.automaticRules.put(sleepingRule.id, sleepingRule); + + AutomaticZenRule bedtime = new AutomaticZenRule.Builder("Bedtime Mode (TM)", CONDITION_ID) + .setType(TYPE_BEDTIME) + .build(); + String bedtimeRuleId = mZenModeHelper.addAutomaticZenRule("pkg", bedtime, UPDATE_ORIGIN_APP, + "reason", CUSTOM_PKG_UID); + + assertThat(mZenModeHelper.mConfig.automaticRules.keySet()).containsExactly(bedtimeRuleId); + } + + @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void addAutomaticZenRule_withTypeBedtime_keepsEnabledSleeping() { + ZenRule sleepingRule = createCustomAutomaticRule(ZEN_MODE_IMPORTANT_INTERRUPTIONS, + ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID); + sleepingRule.enabled = true; + sleepingRule.userModifiedFields = 0; + sleepingRule.name = "ZZZZZZZ..."; + mZenModeHelper.mConfig.automaticRules.clear(); + mZenModeHelper.mConfig.automaticRules.put(sleepingRule.id, sleepingRule); + + AutomaticZenRule bedtime = new AutomaticZenRule.Builder("Bedtime Mode (TM)", CONDITION_ID) + .setType(TYPE_BEDTIME) + .build(); + String bedtimeRuleId = mZenModeHelper.addAutomaticZenRule("pkg", bedtime, UPDATE_ORIGIN_APP, + "reason", CUSTOM_PKG_UID); + + assertThat(mZenModeHelper.mConfig.automaticRules.keySet()).containsExactly( + ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID, bedtimeRuleId); + } + + @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void addAutomaticZenRule_withTypeBedtime_keepsCustomizedSleeping() { + ZenRule sleepingRule = createCustomAutomaticRule(ZEN_MODE_IMPORTANT_INTERRUPTIONS, + ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID); + sleepingRule.enabled = false; + sleepingRule.userModifiedFields = AutomaticZenRule.FIELD_INTERRUPTION_FILTER; + sleepingRule.name = "ZZZZZZZ..."; + mZenModeHelper.mConfig.automaticRules.clear(); + mZenModeHelper.mConfig.automaticRules.put(sleepingRule.id, sleepingRule); + + AutomaticZenRule bedtime = new AutomaticZenRule.Builder("Bedtime Mode (TM)", CONDITION_ID) + .setType(TYPE_BEDTIME) + .build(); + String bedtimeRuleId = mZenModeHelper.addAutomaticZenRule("pkg", bedtime, UPDATE_ORIGIN_APP, + "reason", CUSTOM_PKG_UID); + + assertThat(mZenModeHelper.mConfig.automaticRules.keySet()).containsExactly( + ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID, bedtimeRuleId); + } + + @Test public void testSetManualZenMode() { mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); setupZenConfig(); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java index 98f18433e53d..03d30294e1d8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java @@ -17,13 +17,31 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import android.content.ComponentName; +import android.graphics.ColorSpace; +import android.graphics.Point; +import android.graphics.Rect; +import android.hardware.HardwareBuffer; import android.platform.test.annotations.Presubmit; +import android.util.ArraySet; +import android.view.Surface; +import android.window.TaskSnapshot; import androidx.test.filters.SmallTest; @@ -32,6 +50,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.ArrayList; +import java.util.Arrays; /** * Test class for {@link ActivitySnapshotController}. @@ -45,6 +64,7 @@ import java.util.ArrayList; public class ActivitySnapshotControllerTests extends WindowTestsBase { private ActivitySnapshotController mActivitySnapshotController; + @Before public void setUp() throws Exception { spyOn(mWm.mSnapshotController.mActivitySnapshotController); @@ -154,4 +174,90 @@ public class ActivitySnapshotControllerTests extends WindowTestsBase { assertEquals(openingWindowBelow.mActivityRecord, mActivitySnapshotController.mPendingLoadActivity.valueAt(0)); } + + /** + * Simulate multiple TaskFragments inside a task. + */ + @Test + public void testMultipleActivitiesLoadSnapshot() { + final Task testTask = createTask(mDisplayContent); + final ActivityRecord activityA = createActivityRecord(testTask); + final ActivityRecord activityB = createActivityRecord(testTask); + final ActivityRecord activityC = createActivityRecord(testTask); + final TaskSnapshot taskSnapshot = createSnapshot(); + + final int[] mixedCode = new int[3]; + mixedCode[0] = ActivitySnapshotController.getSystemHashCode(activityA); + mixedCode[1] = ActivitySnapshotController.getSystemHashCode(activityB); + mixedCode[2] = ActivitySnapshotController.getSystemHashCode(activityC); + + mActivitySnapshotController.addUserSavedFile(testTask.mUserId, taskSnapshot, mixedCode); + mActivitySnapshotController.mCache.putSnapshot(activityA, taskSnapshot); + mActivitySnapshotController.mCache.putSnapshot(activityB, taskSnapshot); + mActivitySnapshotController.mCache.putSnapshot(activityC, taskSnapshot); + + assertTrue(mActivitySnapshotController.hasRecord(activityA)); + assertTrue(mActivitySnapshotController.hasRecord(activityB)); + + // If A is removed, B and C should also be removed because they share the same snapshot. + mActivitySnapshotController.onAppRemoved(activityA); + assertFalse(mActivitySnapshotController.hasRecord(activityA)); + assertFalse(mActivitySnapshotController.hasRecord(activityB)); + final ActivityRecord[] singleActivityList = new ActivityRecord[1]; + singleActivityList[0] = activityA; + assertNull(mActivitySnapshotController.getSnapshot(singleActivityList)); + singleActivityList[0] = activityB; + assertNull(mActivitySnapshotController.getSnapshot(singleActivityList)); + final ActivityRecord[] activities = new ActivityRecord[3]; + activities[0] = activityA; + activities[1] = activityB; + activities[2] = activityC; + assertNull(mActivitySnapshotController.getSnapshot(activities)); + + // Reset and test load snapshot + mActivitySnapshotController.addUserSavedFile(testTask.mUserId, taskSnapshot, mixedCode); + // Request to load by B, nothing will be loaded because the snapshot was [A,B,C]. + mActivitySnapshotController.mPendingLoadActivity.add(activityB); + mActivitySnapshotController.loadActivitySnapshot(); + verify(mActivitySnapshotController, never()).loadSnapshotInner(any(), any()); + + // Able to load snapshot when requesting for all A, B, C + mActivitySnapshotController.mPendingLoadActivity.clear(); + mActivitySnapshotController.mPendingLoadActivity.add(activityA); + mActivitySnapshotController.mPendingLoadActivity.add(activityB); + mActivitySnapshotController.mPendingLoadActivity.add(activityC); + final ArraySet<ActivityRecord> verifyList = new ArraySet<>(); + verifyList.add(activityA); + verifyList.add(activityB); + verifyList.add(activityC); + mActivitySnapshotController.loadActivitySnapshot(); + verify(mActivitySnapshotController).loadSnapshotInner(argThat( + argument -> { + final ArrayList<ActivityRecord> argumentList = new ArrayList<>( + Arrays.asList(argument)); + return verifyList.containsAll(argumentList) + && argumentList.containsAll(verifyList); + }), + any()); + + for (int i = activities.length - 1; i >= 0; --i) { + mActivitySnapshotController.mCache.putSnapshot(activities[i], taskSnapshot); + } + // The loaded snapshot can be retrieved only if the activities match exactly. + singleActivityList[0] = activityB; + assertNull(mActivitySnapshotController.getSnapshot(singleActivityList)); + assertEquals(taskSnapshot, mActivitySnapshotController.getSnapshot(activities)); + } + + private TaskSnapshot createSnapshot() { + HardwareBuffer buffer = mock(HardwareBuffer.class); + doReturn(100).when(buffer).getWidth(); + doReturn(100).when(buffer).getHeight(); + return new TaskSnapshot(1, 0 /* captureTime */, new ComponentName("", ""), buffer, + ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT, + Surface.ROTATION_0, new Point(100, 100), new Rect() /* contentInsets */, + new Rect() /* letterboxInsets*/, false /* isLowResolution */, + true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN, 0 /* mSystemUiVisibility */, + false /* isTranslucent */, false /* hasImeSurface */); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java index d83824a39730..402cbccbca01 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java @@ -742,7 +742,7 @@ public class BackNavigationControllerTests extends WindowTestsBase { MockitoSession mockitoSession = mockitoSession().mockStatic(BackNavigationController.class) .strictness(Strictness.LENIENT).startMocking(); - doReturn(taskSnapshot).when(() -> BackNavigationController.getSnapshot(any())); + doReturn(taskSnapshot).when(() -> BackNavigationController.getSnapshot(any(), any())); when(resourcesSpy.getBoolean( com.android.internal.R.bool.config_predictShowStartingSurface)) .thenReturn(preferWindowlessSurface); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java index e9fe4bb91329..22ddf8420121 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java @@ -53,6 +53,7 @@ import android.graphics.Rect; import android.os.Binder; import android.platform.test.annotations.Presubmit; import android.view.SurfaceControl; +import android.view.View; import android.window.ITaskFragmentOrganizer; import android.window.TaskFragmentAnimationParams; import android.window.TaskFragmentInfo; @@ -695,4 +696,75 @@ public class TaskFragmentTest extends WindowTestsBase { mTaskFragment.getDimBounds(dimBounds); assertEquals(taskFragmentBounds, dimBounds); } + + @Test + public void testMoveFocusToAdjacentWindow() { + // Setup two activities in ActivityEmbedding split. + final Task task = createTask(mDisplayContent); + final TaskFragment taskFragmentLeft = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .createActivityCount(2) + .setOrganizer(mOrganizer) + .setFragmentToken(new Binder()) + .build(); + final TaskFragment taskFragmentRight = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .createActivityCount(1) + .setOrganizer(mOrganizer) + .setFragmentToken(new Binder()) + .build(); + taskFragmentLeft.setAdjacentTaskFragment(taskFragmentRight); + taskFragmentLeft.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + taskFragmentRight.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + task.setBounds(0, 0, 1200, 1000); + taskFragmentLeft.setBounds(0, 0, 600, 1000); + taskFragmentRight.setBounds(600, 0, 1200, 1000); + final ActivityRecord appLeftTop = taskFragmentLeft.getTopMostActivity(); + final ActivityRecord appLeftBottom = taskFragmentLeft.getBottomMostActivity(); + final ActivityRecord appRightTop = taskFragmentRight.getTopMostActivity(); + appLeftTop.setVisibleRequested(true); + appRightTop.setVisibleRequested(true); + final WindowState winLeftTop = createAppWindow(appLeftTop, "winLeftTop"); + final WindowState winLeftBottom = createAppWindow(appLeftBottom, "winLeftBottom"); + final WindowState winRightTop = createAppWindow(appRightTop, "winRightTop"); + winLeftTop.setHasSurface(true); + winRightTop.setHasSurface(true); + + taskFragmentLeft.setResumedActivity(appLeftTop, "test"); + taskFragmentRight.setResumedActivity(appRightTop, "test"); + appLeftTop.setState(RESUMED, "test"); + appRightTop.setState(RESUMED, "test"); + mDisplayContent.mFocusedApp = appRightTop; + + // Make the appLeftTop be the focused activity and ensure the focused app is updated. + appLeftTop.moveFocusableActivityToTop("test"); + assertEquals(winLeftTop, mDisplayContent.mCurrentFocus); + + // Send request from a non-focused window with valid direction. + assertFalse(mWm.moveFocusToAdjacentWindow(null, winLeftBottom.mClient, View.FOCUS_RIGHT)); + // The focus should remain the same. + assertEquals(winLeftTop, mDisplayContent.mCurrentFocus); + + // Send request from the focused window with valid direction. + assertTrue(mWm.moveFocusToAdjacentWindow(null, winLeftTop.mClient, View.FOCUS_RIGHT)); + // The focus should change. + assertEquals(winRightTop, mDisplayContent.mCurrentFocus); + + // Send request from the focused window with invalid direction. + assertFalse(mWm.moveFocusToAdjacentWindow(null, winRightTop.mClient, View.FOCUS_UP)); + // The focus should remain the same. + assertEquals(winRightTop, mDisplayContent.mCurrentFocus); + + // Send request from the focused window with valid direction. + assertTrue(mWm.moveFocusToAdjacentWindow(null, winRightTop.mClient, View.FOCUS_BACKWARD)); + // The focus should change. + assertEquals(winLeftTop, mDisplayContent.mCurrentFocus); + } + + private WindowState createAppWindow(ActivityRecord app, String name) { + final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, app, name, + 0 /* ownerId */, false /* ownerCanAddInternalSystemWindow */, new TestIWindow()); + mWm.mWindowMap.put(win.mClient.asBinder(), win); + return win; + } } diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java b/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java index 8773cabeeb92..1df7012c44f8 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java @@ -24,6 +24,7 @@ import android.telephony.TelephonyManager; import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import com.android.internal.telephony.flags.Flags; import java.util.ArrayList; import java.util.List; @@ -117,12 +118,28 @@ public class PhoneCallStateHandler { private boolean checkCallStatus() { List<SubscriptionInfo> infoList = mSubscriptionManager.getActiveSubscriptionInfoList(); if (infoList == null) return false; - return infoList.stream() - .filter(s -> (s.getSubscriptionId() != SubscriptionManager.INVALID_SUBSCRIPTION_ID)) - .anyMatch(s -> isCallOngoingFromState( - mTelephonyManager - .createForSubscriptionId(s.getSubscriptionId()) - .getCallStateForSubscription())); + if (!Flags.enforceTelephonyFeatureMapping()) { + return infoList.stream() + .filter(s -> (s.getSubscriptionId() + != SubscriptionManager.INVALID_SUBSCRIPTION_ID)) + .anyMatch(s -> isCallOngoingFromState( + mTelephonyManager + .createForSubscriptionId(s.getSubscriptionId()) + .getCallStateForSubscription())); + } else { + return infoList.stream() + .filter(s -> (s.getSubscriptionId() + != SubscriptionManager.INVALID_SUBSCRIPTION_ID)) + .anyMatch(s -> { + try { + return isCallOngoingFromState(mTelephonyManager + .createForSubscriptionId(s.getSubscriptionId()) + .getCallStateForSubscription()); + } catch (UnsupportedOperationException e) { + return false; + } + }); + } } private void updateTelephonyListeners() { diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java index 250c3a54c928..2a6ac98b4d98 100644 --- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java @@ -196,7 +196,7 @@ public final class TelephonyPermissions { // We have READ_PHONE_STATE permission, so return true as long as the AppOps bit hasn't been // revoked. AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); - return appOps.noteOp(AppOpsManager.OPSTR_READ_PHONE_STATE, uid, callingPackage, + return appOps.noteOpNoThrow(AppOpsManager.OPSTR_READ_PHONE_STATE, uid, callingPackage, callingFeatureId, null) == AppOpsManager.MODE_ALLOWED; } @@ -249,7 +249,7 @@ public final class TelephonyPermissions { // We have READ_PHONE_STATE permission, so return true as long as the AppOps bit hasn't been // revoked. AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); - return appOps.noteOp(AppOpsManager.OPSTR_READ_PHONE_STATE, uid, callingPackage, + return appOps.noteOpNoThrow(AppOpsManager.OPSTR_READ_PHONE_STATE, uid, callingPackage, callingFeatureId, null) == AppOpsManager.MODE_ALLOWED; } @@ -521,7 +521,7 @@ public final class TelephonyPermissions { // We have READ_CALL_LOG permission, so return true as long as the AppOps bit hasn't been // revoked. AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); - return appOps.noteOp(AppOpsManager.OPSTR_READ_CALL_LOG, uid, callingPackage, + return appOps.noteOpNoThrow(AppOpsManager.OPSTR_READ_CALL_LOG, uid, callingPackage, callingPackageName, null) == AppOpsManager.MODE_ALLOWED; } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index c7b84a3b9530..79e4ad0575f7 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -10955,6 +10955,9 @@ public class CarrierConfigManager { * @return A {@link PersistableBundle} containing the config for the given subId, or default * values for an invalid subId. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * * @deprecated Use {@link #getConfigForSubId(int, String...)} instead. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @@ -11002,6 +11005,9 @@ public class CarrierConfigManager { * @return A {@link PersistableBundle} with key/value mapping for the specified configuration * on success, or an empty (but never null) bundle on failure (for example, when the calling app * has no permission). + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @RequiresPermission(anyOf = { Manifest.permission.READ_PHONE_STATE, @@ -11047,6 +11053,9 @@ public class CarrierConfigManager { * @param overrideValues Key-value pairs of the values that are to be overridden. If set to * {@code null}, this will remove all previous overrides and set the * carrier configuration back to production values. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) @@ -11104,6 +11113,10 @@ public class CarrierConfigManager { * * @see #getConfigForSubId * @see #getConfig(String...) + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * * @deprecated use {@link #getConfig(String...)} instead. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @@ -11138,6 +11151,9 @@ public class CarrierConfigManager { * configs on success, or an empty (but never null) bundle on failure. * @see #getConfigForSubId(int, String...) * @see SubscriptionManager#getDefaultSubscriptionId() + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @RequiresPermission(anyOf = { Manifest.permission.READ_PHONE_STATE, @@ -11189,6 +11205,9 @@ public class CarrierConfigManager { * * <p>This method returns before the reload has completed, and {@link * android.service.carrier.CarrierService#onLoadConfig} will be called from an arbitrary thread. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @@ -11212,6 +11231,8 @@ public class CarrierConfigManager { * <p>Depending on simState, the config may be cleared or loaded from config app. This is only * used by SubscriptionInfoUpdater. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @SystemApi @@ -11234,6 +11255,8 @@ public class CarrierConfigManager { * Gets the package name for a default carrier service. * @return the package name for a default carrier service; empty string if not available. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @NonNull @@ -11287,6 +11310,9 @@ public class CarrierConfigManager { * @param subId the subscription ID, normally obtained from {@link SubscriptionManager}. * * @see #getConfigForSubId + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(Manifest.permission.READ_PHONE_STATE) diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 1b47dfe0eba1..0dce0844ce64 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -18923,6 +18923,62 @@ public class TelephonyManager { } /** + * Enables or disables notifications sent when cellular null cipher or integrity algorithms + * are in use by the cellular modem. + * + * @throws IllegalStateException if the Telephony process is not currently available + * @throws SecurityException if the caller does not have the required privileges + * @throws UnsupportedOperationException if the modem does not support reporting on ciphering + * and integrity algorithms in use + * @hide + */ + @FlaggedApi(Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY) + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @SystemApi + public void setEnableNullCipherNotifications(boolean enable) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.setEnableNullCipherNotifications(enable); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "setEnableNullCipherNotifications RemoteException", ex); + ex.rethrowFromSystemServer(); + } + } + + /** + * Get whether notifications are enabled for null cipher or integrity algorithms in use by the + * cellular modem. + * + * @throws IllegalStateException if the Telephony process is not currently available + * @throws SecurityException if the caller does not have the required privileges + * @throws UnsupportedOperationException if the modem does not support reporting on ciphering + * and integrity algorithms in use + * @hide + */ + @FlaggedApi(Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY) + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SystemApi + public boolean isNullCipherNotificationsEnabled() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.isNullCipherNotificationsEnabled(); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "isNullCipherNotificationsEnabled RemoteException", ex); + ex.rethrowFromSystemServer(); + } + return false; + } + + + /** * Get current cell broadcast message identifier ranges. * * @throws SecurityException if the caller does not have the required permission diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java index 9dd83d1438e2..b6f9e1f4c3af 100644 --- a/telephony/java/android/telephony/data/DataCallResponse.java +++ b/telephony/java/android/telephony/data/DataCallResponse.java @@ -451,7 +451,7 @@ public final class DataCallResponse implements Parcelable { /** * Return the network validation status that was initiated by {@link - * DataService.DataServiceProvider#requestValidation} + * DataService.DataServiceProvider#requestNetworkValidation} * * @return The network validation status of data connection. */ @@ -931,7 +931,7 @@ public final class DataCallResponse implements Parcelable { /** * Set the network validation status that corresponds to the state of the network validation - * request started by {@link DataService.DataServiceProvider#requestValidation} + * request started by {@link DataService.DataServiceProvider#requestNetworkValidation} * * @param status The network validation status. * @return The same instance of the builder. diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java index 80e91a330185..f04e1c9b221d 100644 --- a/telephony/java/android/telephony/data/DataService.java +++ b/telephony/java/android/telephony/data/DataService.java @@ -415,13 +415,13 @@ public abstract class DataService extends Service { * request validation to the DataService and checks if the request has been submitted. */ @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION) - public void requestValidation(int cid, + public void requestNetworkValidation(int cid, @NonNull @CallbackExecutor Executor executor, @NonNull @DataServiceCallback.ResultCode Consumer<Integer> resultCodeCallback) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(resultCodeCallback, "resultCodeCallback cannot be null"); - Log.d(TAG, "requestValidation: " + cid); + Log.d(TAG, "requestNetworkValidation: " + cid); // The default implementation is to return unsupported. executor.execute(() -> resultCodeCallback @@ -741,7 +741,7 @@ public abstract class DataService extends Service { case DATA_SERVICE_REQUEST_VALIDATION: if (serviceProvider == null) break; ValidationRequest validationRequest = (ValidationRequest) message.obj; - serviceProvider.requestValidation( + serviceProvider.requestNetworkValidation( validationRequest.cid, validationRequest.executor, FunctionalUtils @@ -924,9 +924,10 @@ public abstract class DataService extends Service { } @Override - public void requestValidation(int slotIndex, int cid, IIntegerConsumer resultCodeCallback) { + public void requestNetworkValidation(int slotIndex, int cid, + IIntegerConsumer resultCodeCallback) { if (resultCodeCallback == null) { - loge("requestValidation: resultCodeCallback is null"); + loge("requestNetworkValidation: resultCodeCallback is null"); return; } ValidationRequest validationRequest = diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl index 15f88815ec6b..c36c302ee986 100644 --- a/telephony/java/android/telephony/data/IDataService.aidl +++ b/telephony/java/android/telephony/data/IDataService.aidl @@ -48,5 +48,5 @@ oneway interface IDataService void cancelHandover(int slotId, int cid, IDataServiceCallback callback); void registerForUnthrottleApn(int slotIndex, IDataServiceCallback callback); void unregisterForUnthrottleApn(int slotIndex, IDataServiceCallback callback); - void requestValidation(int slotId, int cid, IIntegerConsumer callback); + void requestNetworkValidation(int slotId, int cid, IIntegerConsumer callback); } diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java index 54ceaed617ec..9f83da930221 100644 --- a/telephony/java/android/telephony/ims/RegistrationManager.java +++ b/telephony/java/android/telephony/ims/RegistrationManager.java @@ -84,7 +84,7 @@ public interface RegistrationManager { SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK, SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT, SUGGESTED_ACTION_TRIGGER_RAT_BLOCK, - SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCK + SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCKS }) @Retention(RetentionPolicy.SOURCE) public @interface SuggestedAction {} @@ -116,9 +116,10 @@ public interface RegistrationManager { /** * Indicates that the IMS registration on current RAT failed multiple times. - * The radio shall block the current RAT and search for other available RATs in the - * background. If no other RAT is available that meets the carrier requirements, the - * radio may remain on the current RAT for internet service. The radio clears all + * The radio shall block the {@link ImsRegistrationImplBase.ImsRegistrationTech} + * included with this and search for other available RATs in the background. + * If no other RAT is available that meets the carrier requirements, the + * radio may remain on the blocked RAT for internet service. The radio clears all * RATs marked as unavailable if the IMS service is registered to the carrier network. * @hide */ @@ -133,7 +134,7 @@ public interface RegistrationManager { */ @SystemApi @FlaggedApi(Flags.FLAG_ADD_RAT_RELATED_SUGGESTED_ACTION_TO_IMS_REGISTRATION) - int SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCK = 4; + int SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCKS = 4; /**@hide*/ // Translate ImsRegistrationImplBase API to new AccessNetworkConstant because WLAN diff --git a/telephony/java/android/telephony/satellite/AntennaPosition.java b/telephony/java/android/telephony/satellite/AntennaPosition.java index 8842886d3a1c..d6440fc7a119 100644 --- a/telephony/java/android/telephony/satellite/AntennaPosition.java +++ b/telephony/java/android/telephony/satellite/AntennaPosition.java @@ -35,10 +35,10 @@ import java.util.Objects; @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public final class AntennaPosition implements Parcelable { /** Antenna direction used for satellite communication. */ - @NonNull AntennaDirection mAntennaDirection; + @NonNull private AntennaDirection mAntennaDirection; /** Enum corresponding to device hold position to be used by the end user. */ - @SatelliteManager.DeviceHoldPosition int mSuggestedHoldPosition; + @SatelliteManager.DeviceHoldPosition private int mSuggestedHoldPosition; /** * @hide diff --git a/telephony/java/android/telephony/satellite/ISatelliteStateCallback.aidl b/telephony/java/android/telephony/satellite/ISatelliteModemStateCallback.aidl index cd9d81e1ee9b..9ff73e2c2f12 100644 --- a/telephony/java/android/telephony/satellite/ISatelliteStateCallback.aidl +++ b/telephony/java/android/telephony/satellite/ISatelliteModemStateCallback.aidl @@ -20,7 +20,7 @@ package android.telephony.satellite; * Interface for satellite state change callback. * @hide */ -oneway interface ISatelliteStateCallback { +oneway interface ISatelliteModemStateCallback { /** * Indicates that the satellite modem state has changed. * diff --git a/telephony/java/android/telephony/satellite/PointingInfo.java b/telephony/java/android/telephony/satellite/PointingInfo.java index 022a856e48dd..9440b65a61aa 100644 --- a/telephony/java/android/telephony/satellite/PointingInfo.java +++ b/telephony/java/android/telephony/satellite/PointingInfo.java @@ -17,6 +17,7 @@ package android.telephony.satellite; import android.annotation.FlaggedApi; +import android.annotation.FloatRange; import android.annotation.NonNull; import android.annotation.SystemApi; import android.os.Parcel; @@ -108,11 +109,19 @@ public final class PointingInfo implements Parcelable { return sb.toString(); } + /** + * Returns the azimuth of the satellite, in degrees. + */ + @FloatRange(from = -180, to = 180) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public float getSatelliteAzimuthDegrees() { return mSatelliteAzimuthDegrees; } + /** + * Returns the elevation of the satellite, in degrees. + */ + @FloatRange(from = -90, to = 90) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public float getSatelliteElevationDegrees() { return mSatelliteElevationDegrees; diff --git a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java index b5763c38e69c..8e79ca5cf3d3 100644 --- a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java +++ b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java @@ -22,10 +22,15 @@ import android.annotation.SystemApi; import com.android.internal.telephony.flags.Flags; +import java.util.concurrent.Executor; import java.util.function.Consumer; /** * A callback class for listening to satellite datagrams. + * {@link SatelliteDatagramCallback} is registered to telephony when an app which invokes + * {@link SatelliteManager#registerForSatelliteDatagram(Executor, SatelliteDatagramCallback)}, + * and {@link #onSatelliteDatagramReceived(long, SatelliteDatagram, int, Consumer)} will be invoked + * when a new datagram is received from satellite. * * @hide */ diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java index e09bd201f93e..2a697033c132 100644 --- a/telephony/java/android/telephony/satellite/SatelliteManager.java +++ b/telephony/java/android/telephony/satellite/SatelliteManager.java @@ -75,8 +75,9 @@ public final class SatelliteManager { private static final ConcurrentHashMap<SatelliteProvisionStateCallback, ISatelliteProvisionStateCallback> sSatelliteProvisionStateCallbackMap = new ConcurrentHashMap<>(); - private static final ConcurrentHashMap<SatelliteStateCallback, ISatelliteStateCallback> - sSatelliteStateCallbackMap = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap<SatelliteModemStateCallback, + ISatelliteModemStateCallback> + sSatelliteModemStateCallbackMap = new ConcurrentHashMap<>(); private static final ConcurrentHashMap<SatelliteTransmissionUpdateCallback, ISatelliteTransmissionUpdateCallback> sSatelliteTransmissionUpdateCallbackMap = new ConcurrentHashMap<>(); @@ -624,6 +625,11 @@ public final class SatelliteManager { /** * Request to get whether the satellite service is supported on the device. * + * <p> + * Note: This API only checks whether the device supports the satellite feature. The result will + * not be affected by whether the device is provisioned. + * </p> + * * @param executor The executor on which the callback will be called. * @param callback The callback object to which the result will be delivered. * If the request is successful, {@link OutcomeReceiver#onResult(Object)} @@ -1301,21 +1307,22 @@ public final class SatelliteManager { @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) @SatelliteResult public int registerForSatelliteModemStateChanged( @NonNull @CallbackExecutor Executor executor, - @NonNull SatelliteStateCallback callback) { + @NonNull SatelliteModemStateCallback callback) { Objects.requireNonNull(executor); Objects.requireNonNull(callback); try { ITelephony telephony = getITelephony(); if (telephony != null) { - ISatelliteStateCallback internalCallback = new ISatelliteStateCallback.Stub() { + ISatelliteModemStateCallback internalCallback = + new ISatelliteModemStateCallback.Stub() { @Override public void onSatelliteModemStateChanged(int state) { executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onSatelliteModemStateChanged(state))); } }; - sSatelliteStateCallbackMap.put(callback, internalCallback); + sSatelliteModemStateCallbackMap.put(callback, internalCallback); return telephony.registerForSatelliteModemStateChanged(mSubId, internalCallback); } else { throw new IllegalStateException("telephony service is null."); @@ -1332,16 +1339,18 @@ public final class SatelliteManager { * If callback was not registered before, the request will be ignored. * * @param callback The callback that was passed to - * {@link #registerForSatelliteModemStateChanged(Executor, SatelliteStateCallback)}. + * {@link #registerForSatelliteModemStateChanged(Executor, SatelliteModemStateCallback)}. * * @throws SecurityException if the caller doesn't have required permission. * @throws IllegalStateException if the Telephony process is not currently available. */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) - public void unregisterForSatelliteModemStateChanged(@NonNull SatelliteStateCallback callback) { + public void unregisterForSatelliteModemStateChanged( + @NonNull SatelliteModemStateCallback callback) { Objects.requireNonNull(callback); - ISatelliteStateCallback internalCallback = sSatelliteStateCallbackMap.remove(callback); + ISatelliteModemStateCallback internalCallback = sSatelliteModemStateCallbackMap.remove( + callback); try { ITelephony telephony = getITelephony(); diff --git a/telephony/java/android/telephony/satellite/SatelliteStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteModemStateCallback.java index bfe6e101ffb4..8d33c88136a6 100644 --- a/telephony/java/android/telephony/satellite/SatelliteStateCallback.java +++ b/telephony/java/android/telephony/satellite/SatelliteModemStateCallback.java @@ -28,7 +28,7 @@ import com.android.internal.telephony.flags.Flags; */ @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) -public interface SatelliteStateCallback { +public interface SatelliteModemStateCallback { /** * Called when satellite modem state changes. * @param state The new satellite modem state. diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 9b5ee0cd82f3..3ea86c7bd686 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -72,7 +72,7 @@ import android.telephony.satellite.ISatelliteCapabilitiesCallback; import android.telephony.satellite.ISatelliteDatagramCallback; import android.telephony.satellite.ISatelliteTransmissionUpdateCallback; import android.telephony.satellite.ISatelliteProvisionStateCallback; -import android.telephony.satellite.ISatelliteStateCallback; +import android.telephony.satellite.ISatelliteModemStateCallback; import android.telephony.satellite.NtnSignalStrength; import android.telephony.satellite.SatelliteCapabilities; import android.telephony.satellite.SatelliteDatagram; @@ -2896,7 +2896,7 @@ interface ITelephony { */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - int registerForSatelliteModemStateChanged(int subId, ISatelliteStateCallback callback); + int registerForSatelliteModemStateChanged(int subId, ISatelliteModemStateCallback callback); /** * Unregisters for modem state changed from satellite modem. @@ -2907,7 +2907,7 @@ interface ITelephony { */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void unregisterForSatelliteModemStateChanged(int subId, ISatelliteStateCallback callback); + void unregisterForSatelliteModemStateChanged(int subId, ISatelliteModemStateCallback callback); /** * Register to receive incoming datagrams over satellite. @@ -3230,4 +3230,32 @@ interface ITelephony { @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)") boolean isCellularIdentifierDisclosureNotificationsEnabled(); + + /** + * Enables or disables notifications sent when cellular null cipher or integrity algorithms + * are in use by the cellular modem. + * + * @throws IllegalStateException if the Telephony process is not currently available + * @throws SecurityException if the caller does not have the required privileges + * @throws UnsupportedOperationException if the modem does not support reporting on ciphering + * and integrity algorithms in use + * @hide + */ + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + + "android.Manifest.permission.MODIFY_PHONE_STATE)") + void setEnableNullCipherNotifications(boolean enable); + + /** + * Get whether notifications are enabled for null cipher or integrity algorithms in use by the + * cellular modem. + * + * @throws IllegalStateException if the Telephony process is not currently available + * @throws SecurityException if the caller does not have the required privileges + * @throws UnsupportedOperationException if the modem does not support reporting on ciphering + * and integrity algorithms in use + * @hide + */ + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + + "android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)") + boolean isNullCipherNotificationsEnabled(); } diff --git a/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java b/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java index 2bc056ee743f..fee1b25f04e5 100644 --- a/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java +++ b/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java @@ -16,6 +16,9 @@ package android.transparency.test.app; +import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; +import static android.Manifest.permission.QUERY_ALL_PACKAGES; + import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; @@ -27,6 +30,7 @@ import android.util.Log; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; +import com.android.compatibility.common.util.ShellIdentityUtils; import com.android.internal.os.IBinaryTransparencyService.AppInfo; import org.junit.Before; @@ -116,7 +120,13 @@ public class BinaryTransparencyTest { @Test public void testCollectAllSilentInstalledMbaInfo() { // Action - var appInfoList = mBt.collectAllSilentInstalledMbaInfo(new Bundle()); + var appInfoList = + ShellIdentityUtils.invokeMethodWithShellPermissions( + mBt, + (Bt) -> + mBt.collectAllSilentInstalledMbaInfo(new Bundle()), + QUERY_ALL_PACKAGES, + INTERACT_ACROSS_USERS_FULL); // Verify assertThat(appInfoList).isNotEmpty(); // because we just installed from the host side diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java index 80c1e5be3a32..217659ee4345 100644 --- a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java +++ b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java @@ -43,9 +43,7 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.List; -import java.util.Optional; import java.util.stream.Collectors; /** @@ -178,8 +176,14 @@ public class GraphicsActivity extends Activity { this.deviceFrameRate = deviceFrameRate; } + FrameRateTimeoutException(List<Float> expectedFrameRates, float deviceFrameRate) { + this.expectedFrameRates = expectedFrameRates; + this.deviceFrameRate = deviceFrameRate; + } + public float expectedFrameRate; public float deviceFrameRate; + public List<Float> expectedFrameRates; } public enum Api { @@ -502,31 +506,37 @@ public class GraphicsActivity extends Activity { } private void waitForStableFrameRate(TestSurface... surfaces) throws InterruptedException { - verifyCompatibleAndStableFrameRate(0, surfaces); + verifyFrameRates(List.of(), surfaces); } private void verifyExactAndStableFrameRate( float expectedFrameRate, TestSurface... surfaces) throws InterruptedException { - verifyFrameRate(expectedFrameRate, false, surfaces); + verifyFrameRate(List.of(expectedFrameRate), false, surfaces); } private void verifyCompatibleAndStableFrameRate( float expectedFrameRate, TestSurface... surfaces) throws InterruptedException { - verifyFrameRate(expectedFrameRate, true, surfaces); + verifyFrameRate(List.of(expectedFrameRate), true, surfaces); + } + + /** Verify stable frame rate at one of the expectedFrameRates. */ + private void verifyFrameRates(List<Float> expectedFrameRates, TestSurface... surfaces) + throws InterruptedException { + verifyFrameRate(expectedFrameRates, true, surfaces); } - // Set expectedFrameRate to 0.0 to verify only stable frame rate. - private void verifyFrameRate( - float expectedFrameRate, boolean multiplesAllowed, + // Set expectedFrameRates to empty to verify only stable frame rate. + private void verifyFrameRate(List<Float> expectedFrameRates, boolean multiplesAllowed, TestSurface... surfaces) throws InterruptedException { Log.i(TAG, "Verifying compatible and stable frame rate"); long nowNanos = System.nanoTime(); long gracePeriodEndTimeNanos = nowNanos + FRAME_RATE_SWITCH_GRACE_PERIOD_SECONDS * 1_000_000_000L; while (true) { - if (expectedFrameRate > FRAME_RATE_TOLERANCE) { // expectedFrameRate > 0 + if (expectedFrameRates.size() == 1) { + float expectedFrameRate = expectedFrameRates.get(0); // Wait until we switch to a compatible frame rate. Log.i(TAG, String.format( @@ -557,11 +567,25 @@ public class GraphicsActivity extends Activity { while (endTimeNanos > nowNanos) { int numModeChangedEvents = mModeChangedEvents.size(); if (waitForEvents(endTimeNanos, surfaces)) { - Log.i(TAG, - String.format("Stable frame rate %.2f verified", - multiplesAllowed ? mDisplayModeRefreshRate - : mDisplayRefreshRate)); - return; + // Verify any expected frame rate since there are multiple that will suffice. + // Mainly to account for running tests on real devices, where other non-test + // layers may affect the outcome. + if (expectedFrameRates.size() > 1) { + for (float expectedFrameRate : expectedFrameRates) { + if (isFrameRateMultiple(mDisplayModeRefreshRate, expectedFrameRate)) { + return; + } + } + // The frame rate is stable but it is not one of the expected frame rates. + throw new FrameRateTimeoutException( + expectedFrameRates, mDisplayModeRefreshRate); + } else { + Log.i(TAG, + String.format("Stable frame rate %.2f verified", + multiplesAllowed ? mDisplayModeRefreshRate + : mDisplayRefreshRate)); + return; + } } nowNanos = System.nanoTime(); if (mModeChangedEvents.size() > numModeChangedEvents) { @@ -623,12 +647,28 @@ public class GraphicsActivity extends Activity { // caused the timeout failure. Wait for a bit to see if we get notified // of a precondition violation, and if so, retry the test. Otherwise // fail. - assertTrue(String.format( - "Timed out waiting for a stable and compatible frame" - + " rate. expected=%.2f received=%.2f." - + " Stack trace: " + stackTrace, - exc.expectedFrameRate, exc.deviceFrameRate), - waitForPreconditionViolation()); + if (exc.expectedFrameRates.isEmpty()) { + assertTrue( + String.format( + "Timed out waiting for a stable and compatible" + + " frame rate." + + " expected=%.2f received=%.2f." + + " Stack trace: " + stackTrace, + exc.expectedFrameRate, exc.deviceFrameRate), + waitForPreconditionViolation()); + } else { + assertTrue( + String.format( + "Timed out waiting for a stable and compatible" + + " frame rate." + + " expected={%.2f} received=%.2f." + + " Stack trace: " + stackTrace, + exc.expectedFrameRates.stream() + .map(Object::toString) + .collect(Collectors.joining(", ")), + exc.deviceFrameRate), + waitForPreconditionViolation()); + } } if (!testPassed) { @@ -679,10 +719,15 @@ public class GraphicsActivity extends Activity { "**** Running testSurfaceControlFrameRateCompatibility with compatibility " + compatibility); - float expectedFrameRate = getExpectedFrameRateForCompatibility(compatibility); + List<Float> expectedFrameRates = getExpectedFrameRateForCompatibility(compatibility); + Log.i(TAG, + "Expected frame rates: " + + expectedFrameRates.stream() + .map(Object::toString) + .collect(Collectors.joining(", "))); int initialNumEvents = mModeChangedEvents.size(); surface.setFrameRate(30.f, compatibility); - verifyExactAndStableFrameRate(expectedFrameRate, surface); + verifyFrameRates(expectedFrameRates, surface); verifyModeSwitchesDontChangeResolution(initialNumEvents, mModeChangedEvents.size()); }); } @@ -699,10 +744,10 @@ public class GraphicsActivity extends Activity { runOneSurfaceTest((TestSurface surface) -> { Log.i(TAG, "**** Running testSurfaceControlFrameRateCategory for category " + category); - float expectedFrameRate = getExpectedFrameRateForCategory(category); + List<Float> expectedFrameRates = getExpectedFrameRateForCategory(category); int initialNumEvents = mModeChangedEvents.size(); surface.setFrameRateCategory(category); - verifyCompatibleAndStableFrameRate(expectedFrameRate, surface); + verifyFrameRates(expectedFrameRates, surface); verifyModeSwitchesDontChangeResolution(initialNumEvents, mModeChangedEvents.size()); }); } @@ -771,41 +816,41 @@ public class GraphicsActivity extends Activity { "frame rate strategy=" + parentStrategy); } - private float getExpectedFrameRateForCompatibility(int compatibility) { + private List<Float> getExpectedFrameRateForCompatibility(int compatibility) { assumeTrue("**** testSurfaceControlFrameRateCompatibility SKIPPED for compatibility " + compatibility, compatibility == Surface.FRAME_RATE_COMPATIBILITY_GTE); Display display = getDisplay(); - Optional<Float> expectedFrameRate = getRefreshRates(display.getMode(), display) - .stream() - .filter(rate -> rate >= 30.f) - .min(Comparator.naturalOrder()); + List<Float> expectedFrameRates = getRefreshRates(display.getMode(), display) + .stream() + .filter(rate -> rate >= 30.f) + .collect(Collectors.toList()); assumeTrue("**** testSurfaceControlFrameRateCompatibility SKIPPED because no refresh rate " + "is >= 30", - expectedFrameRate.isPresent()); - return expectedFrameRate.get(); + !expectedFrameRates.isEmpty()); + return expectedFrameRates; } - private float getExpectedFrameRateForCategory(int category) { + private List<Float> getExpectedFrameRateForCategory(int category) { Display display = getDisplay(); List<Float> frameRates = getRefreshRates(display.getMode(), display); if (category == Surface.FRAME_RATE_CATEGORY_DEFAULT) { // Max due to default vote and no other frame rate specifications. - return Collections.max(frameRates); + return List.of(Collections.max(frameRates)); } else if (category == Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE) { - return Collections.min(frameRates); + return frameRates; } FpsRange categoryRange = convertCategory(category); - Optional<Float> expectedFrameRate = frameRates.stream() - .filter(fps -> categoryRange.includes(fps)) - .min(Comparator.naturalOrder()); + List<Float> expectedFrameRates = frameRates.stream() + .filter(fps -> categoryRange.includes(fps)) + .collect(Collectors.toList()); assumeTrue("**** testSurfaceControlFrameRateCategory SKIPPED for category " + category, - expectedFrameRate.isPresent()); - return expectedFrameRate.get(); + !expectedFrameRates.isEmpty()); + return expectedFrameRates; } private FpsRange convertCategory(int category) { diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java index 29cbf01dc6da..e3988cd20199 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java @@ -24,8 +24,8 @@ import android.os.Bundle; import android.view.View; import android.widget.ToggleButton; +import androidx.window.embedding.ActivityEmbeddingController; import androidx.window.embedding.SplitAttributes; -import androidx.window.embedding.SplitAttributesCalculatorParams; import androidx.window.embedding.SplitController; /** @@ -66,7 +66,9 @@ public class ActivityEmbeddingSecondaryActivity extends Activity { @Override public void onClick(View v) { // This triggers a recalcuation of splitatributes. - mSplitController.invalidateTopVisibleSplitAttributes(); + ActivityEmbeddingController + .getInstance(ActivityEmbeddingSecondaryActivity.this) + .invalidateTopVisibleActivityStacks(); } }); findViewById(R.id.secondary_enter_pip_button).setOnClickListener( diff --git a/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java b/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java index be479f205ff2..1b0279273dc7 100644 --- a/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java +++ b/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java @@ -25,6 +25,7 @@ import android.platform.test.flag.junit.host.HostFlagsValueProvider; import android.security.Flags; import com.android.blockdevicewriter.BlockDeviceWriter; +import com.android.tradefed.device.DeviceNotAvailableException; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; @@ -52,7 +53,6 @@ public class FsVerityHostTest extends BaseHostJUnit4Test { private static final String TARGET_PACKAGE = "com.android.fsverity"; private static final String BASENAME = "test.file"; - private static final String TARGET_PATH = "/data/data/" + TARGET_PACKAGE + "/files/" + BASENAME; @Rule public final CheckFlagsRule mCheckFlagsRule = @@ -63,11 +63,11 @@ public class FsVerityHostTest extends BaseHostJUnit4Test { prepareTest(10000); ITestDevice device = getDevice(); - BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 0); - BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 8192); + BlockDeviceWriter.damageFileAgainstBlockDevice(device, getTargetFilePath(), 0); + BlockDeviceWriter.damageFileAgainstBlockDevice(device, getTargetFilePath(), 8192); BlockDeviceWriter.dropCaches(device); - verifyRead(TARGET_PATH, "0,2"); + verifyRead(getTargetFilePath(), "0,2"); } @Test @@ -75,12 +75,17 @@ public class FsVerityHostTest extends BaseHostJUnit4Test { prepareTest(128 * 4096 + 1); ITestDevice device = getDevice(); - BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 4096); - BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 100 * 4096); - BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 128 * 4096 + 1); + BlockDeviceWriter.damageFileAgainstBlockDevice(device, getTargetFilePath(), 4096); + BlockDeviceWriter.damageFileAgainstBlockDevice(device, getTargetFilePath(), 100 * 4096); + BlockDeviceWriter.damageFileAgainstBlockDevice(device, getTargetFilePath(), 128 * 4096 + 1); BlockDeviceWriter.dropCaches(device); - verifyRead(TARGET_PATH, "1,100,128"); + verifyRead(getTargetFilePath(), "1,100,128"); + } + + private String getTargetFilePath() throws DeviceNotAvailableException { + return "/data/user/" + getDevice().getCurrentUser() + "/" + TARGET_PACKAGE + "/files/" + + BASENAME; } private void prepareTest(int fileSize) throws Exception { @@ -97,7 +102,7 @@ public class FsVerityHostTest extends BaseHostJUnit4Test { options.setTestClassName(TARGET_PACKAGE + ".Helper"); options.setTestMethodName("verifyFileRead"); options.addInstrumentationArg("brokenBlockIndicesCsv", indicesCsv); - options.addInstrumentationArg("filePath", TARGET_PATH); + options.addInstrumentationArg("filePath", getTargetFilePath()); assertThat(runDeviceTests(options)).isTrue(); } } diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt index c1784f3b42e7..7b191f8388c5 100644 --- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt +++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt @@ -22,7 +22,6 @@ import android.content.ContextWrapper import android.hardware.display.DisplayViewport import android.hardware.input.InputManager import android.hardware.input.InputManagerGlobal -import android.os.IInputConstants import android.os.test.TestLooper import android.platform.test.annotations.Presubmit import android.provider.Settings @@ -295,14 +294,13 @@ class InputManagerServiceTests { localService.setPointerIconVisible(false, 10) verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL)) - localService.setPointerAcceleration(5f, 10) - verify(native).setPointerAcceleration(eq(5f)) + localService.setMousePointerAccelerationEnabled(false, 10) + verify(native).setMousePointerAccelerationEnabled(eq(false)) service.onDisplayRemoved(10) verify(native).displayRemoved(eq(10)) verify(native).setPointerIconType(eq(PointerIcon.TYPE_NOT_SPECIFIED)) - verify(native).setPointerAcceleration( - eq(IInputConstants.DEFAULT_POINTER_ACCELERATION.toFloat())) + verify(native).setMousePointerAccelerationEnabled(true) verifyNoMoreInteractions(native) // This call should not block because the virtual mouse pointer override was never removed. @@ -318,38 +316,38 @@ class InputManagerServiceTests { localService.setPointerIconVisible(false, 10) verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL)) - localService.setPointerAcceleration(5f, 10) - verify(native).setPointerAcceleration(eq(5f)) + localService.setMousePointerAccelerationEnabled(false, 10) + verify(native).setMousePointerAccelerationEnabled(eq(false)) localService.setPointerIconVisible(true, 10) verify(native).setPointerIconType(eq(PointerIcon.TYPE_NOT_SPECIFIED)) - localService.setPointerAcceleration(1f, 10) - verify(native).setPointerAcceleration(eq(1f)) + localService.setMousePointerAccelerationEnabled(true, 10) + verify(native).setMousePointerAccelerationEnabled(eq(true)) // Verify that setting properties on a different display is not propagated until the // pointer is moved to that display. localService.setPointerIconVisible(false, 20) - localService.setPointerAcceleration(6f, 20) + localService.setMousePointerAccelerationEnabled(false, 20) verifyNoMoreInteractions(native) clearInvocations(native) setVirtualMousePointerDisplayIdAndVerify(20) verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL)) - verify(native).setPointerAcceleration(eq(6f)) + verify(native).setMousePointerAccelerationEnabled(eq(false)) } @Test fun setAdditionalInputPropertiesBeforeOverride() { localService.setPointerIconVisible(false, 10) - localService.setPointerAcceleration(5f, 10) + localService.setMousePointerAccelerationEnabled(false, 10) verifyNoMoreInteractions(native) setVirtualMousePointerDisplayIdAndVerify(10) verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL)) - verify(native).setPointerAcceleration(eq(5f)) + verify(native).setMousePointerAccelerationEnabled(eq(false)) } @Test diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java index 692c8a8f0898..49665f7a3304 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java @@ -27,15 +27,18 @@ import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.vcn.VcnGatewayConnectionConfig.VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY; import static com.android.server.vcn.VcnGatewayConnection.DUMMY_ADDR; +import static com.android.server.vcn.VcnGatewayConnection.SAFEMODE_TIMEOUT_SECONDS; import static com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration; import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession; import static com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent; +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.CALLS_REAL_METHODS; @@ -55,6 +58,7 @@ import android.net.NetworkRequest; import android.net.TelephonyNetworkSpecifier; import android.net.vcn.VcnGatewayConnectionConfig; import android.net.vcn.VcnGatewayConnectionConfigTest; +import android.net.vcn.VcnManager; import android.net.vcn.VcnTransportInfo; import android.net.wifi.WifiInfo; import android.os.ParcelUuid; @@ -81,6 +85,7 @@ import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; /** Tests for TelephonySubscriptionTracker */ @RunWith(AndroidJUnit4.class) @@ -352,4 +357,71 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase { any(Executor.class), any(ConnectivityDiagnosticsCallback.class)); } + + private void verifyGetSafeModeTimeoutMs( + boolean isInTestMode, + boolean isConfigTimeoutSupported, + PersistableBundleWrapper carrierConfig, + long expectedTimeoutMs) + throws Exception { + doReturn(isInTestMode).when(mVcnContext).isInTestMode(); + doReturn(isConfigTimeoutSupported).when(mVcnContext).isFlagSafeModeTimeoutConfigEnabled(); + + final TelephonySubscriptionSnapshot snapshot = mock(TelephonySubscriptionSnapshot.class); + doReturn(carrierConfig).when(snapshot).getCarrierConfigForSubGrp(TEST_SUB_GRP); + + final long result = + VcnGatewayConnection.getSafeModeTimeoutMs(mVcnContext, snapshot, TEST_SUB_GRP); + + assertEquals(expectedTimeoutMs, result); + } + + @Test + public void testGetSafeModeTimeoutMs_configTimeoutUnsupported() throws Exception { + verifyGetSafeModeTimeoutMs( + false /* isInTestMode */, + false /* isConfigTimeoutSupported */, + null /* carrierConfig */, + TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS)); + } + + @Test + public void testGetSafeModeTimeoutMs_configTimeoutSupported() throws Exception { + final int carrierConfigTimeoutSeconds = 20; + final PersistableBundleWrapper carrierConfig = mock(PersistableBundleWrapper.class); + doReturn(carrierConfigTimeoutSeconds) + .when(carrierConfig) + .getInt(eq(VcnManager.VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY), anyInt()); + + verifyGetSafeModeTimeoutMs( + false /* isInTestMode */, + true /* isConfigTimeoutSupported */, + carrierConfig, + TimeUnit.SECONDS.toMillis(carrierConfigTimeoutSeconds)); + } + + @Test + public void testGetSafeModeTimeoutMs_configTimeoutSupported_carrierConfigNull() + throws Exception { + verifyGetSafeModeTimeoutMs( + false /* isInTestMode */, + true /* isConfigTimeoutSupported */, + null /* carrierConfig */, + TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS)); + } + + @Test + public void testGetSafeModeTimeoutMs_configTimeoutOverrideTestModeDefault() throws Exception { + final int carrierConfigTimeoutSeconds = 20; + final PersistableBundleWrapper carrierConfig = mock(PersistableBundleWrapper.class); + doReturn(carrierConfigTimeoutSeconds) + .when(carrierConfig) + .getInt(eq(VcnManager.VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY), anyInt()); + + verifyGetSafeModeTimeoutMs( + true /* isInTestMode */, + true /* isConfigTimeoutSupported */, + carrierConfig, + TimeUnit.SECONDS.toMillis(carrierConfigTimeoutSeconds)); + } } diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java index edced87427c8..4c7b25aaa7c3 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java @@ -67,6 +67,8 @@ import com.android.server.IpSecService; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.Vcn.VcnGatewayStatusCallback; import com.android.server.vcn.VcnGatewayConnection.VcnChildSessionCallback; +import com.android.server.vcn.VcnGatewayConnection.VcnIkeSession; +import com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent; import com.android.server.vcn.VcnGatewayConnection.VcnWakeLock; import com.android.server.vcn.routeselection.UnderlyingNetworkController; import com.android.server.vcn.routeselection.UnderlyingNetworkRecord; @@ -118,13 +120,7 @@ public class VcnGatewayConnectionTestBase { NetworkCapabilities networkCapabilities, LinkProperties linkProperties, boolean isBlocked) { - return new UnderlyingNetworkRecord( - network, - networkCapabilities, - linkProperties, - isBlocked, - false /* isSelected */, - 0 /* priorityClass */); + return new UnderlyingNetworkRecord(network, networkCapabilities, linkProperties, isBlocked); } protected static final String TEST_TCP_BUFFER_SIZES_1 = "1,2,3,4"; @@ -226,6 +222,7 @@ public class VcnGatewayConnectionTestBase { doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper(); doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider(); doReturn(mFeatureFlags).when(mVcnContext).getFeatureFlags(); + doReturn(true).when(mVcnContext).isFlagSafeModeTimeoutConfigEnabled(); doReturn(mUnderlyingNetworkController) .when(mDeps) diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java new file mode 100644 index 000000000000..bf84bbeeedad --- /dev/null +++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.vcn.routeselection; + +import static com.android.server.vcn.VcnTestUtils.setupSystemService; +import static com.android.server.vcn.routeselection.UnderlyingNetworkControllerTest.getLinkPropertiesWithName; + +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.TelephonyNetworkSpecifier; +import android.os.ParcelUuid; +import android.os.test.TestLooper; +import android.telephony.TelephonyManager; + +import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; +import com.android.server.vcn.VcnContext; +import com.android.server.vcn.VcnNetworkProvider; + +import org.junit.Before; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Set; +import java.util.UUID; + +public abstract class NetworkEvaluationTestBase { + protected static final String SSID = "TestWifi"; + protected static final String SSID_OTHER = "TestWifiOther"; + protected static final String PLMN_ID = "123456"; + protected static final String PLMN_ID_OTHER = "234567"; + + protected static final int SUB_ID = 1; + protected static final int WIFI_RSSI = -60; + protected static final int WIFI_RSSI_HIGH = -50; + protected static final int WIFI_RSSI_LOW = -80; + protected static final int CARRIER_ID = 1; + protected static final int CARRIER_ID_OTHER = 2; + + protected static final int LINK_UPSTREAM_BANDWIDTH_KBPS = 1024; + protected static final int LINK_DOWNSTREAM_BANDWIDTH_KBPS = 2048; + + protected static final int TEST_MIN_UPSTREAM_BANDWIDTH_KBPS = 100; + protected static final int TEST_MIN_DOWNSTREAM_BANDWIDTH_KBPS = 200; + + protected static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0)); + + protected static final NetworkCapabilities WIFI_NETWORK_CAPABILITIES = + new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .setSignalStrength(WIFI_RSSI) + .setSsid(SSID) + .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS) + .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS) + .build(); + + protected static final TelephonyNetworkSpecifier TEL_NETWORK_SPECIFIER = + new TelephonyNetworkSpecifier.Builder().setSubscriptionId(SUB_ID).build(); + protected static final NetworkCapabilities CELL_NETWORK_CAPABILITIES = + new NetworkCapabilities.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN) + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .setSubscriptionIds(Set.of(SUB_ID)) + .setNetworkSpecifier(TEL_NETWORK_SPECIFIER) + .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS) + .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS) + .build(); + + protected static final LinkProperties LINK_PROPERTIES = getLinkPropertiesWithName("test_iface"); + + @Mock protected Network mNetwork; + @Mock protected TelephonySubscriptionSnapshot mSubscriptionSnapshot; + @Mock protected TelephonyManager mTelephonyManager; + + protected TestLooper mTestLooper; + protected VcnContext mVcnContext; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + final Context mockContext = mock(Context.class); + mTestLooper = new TestLooper(); + mVcnContext = + spy( + new VcnContext( + mockContext, + mTestLooper.getLooper(), + mock(VcnNetworkProvider.class), + false /* isInTestMode */)); + doNothing().when(mVcnContext).ensureRunningOnLooperThread(); + + setupSystemService( + mockContext, mTelephonyManager, Context.TELEPHONY_SERVICE, TelephonyManager.class); + when(mTelephonyManager.createForSubscriptionId(SUB_ID)).thenReturn(mTelephonyManager); + when(mTelephonyManager.getNetworkOperator()).thenReturn(PLMN_ID); + when(mTelephonyManager.getSimSpecificCarrierId()).thenReturn(CARRIER_ID); + } +} diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java index 226604108522..dbf2f514fe89 100644 --- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java +++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java @@ -24,152 +24,48 @@ import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_ENTR import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS; import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS; -import static com.android.server.vcn.VcnTestUtils.setupSystemService; import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_FALLBACK; import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_INVALID; import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesCellPriorityRule; import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesPriorityRule; import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesWifiPriorityRule; -import static com.android.server.vcn.routeselection.UnderlyingNetworkControllerTest.getLinkPropertiesWithName; import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; -import android.content.Context; -import android.net.LinkProperties; -import android.net.Network; import android.net.NetworkCapabilities; -import android.net.TelephonyNetworkSpecifier; import android.net.vcn.VcnCellUnderlyingNetworkTemplate; import android.net.vcn.VcnGatewayConnectionConfig; import android.net.vcn.VcnManager; import android.net.vcn.VcnUnderlyingNetworkTemplate; import android.net.vcn.VcnWifiUnderlyingNetworkTemplate; -import android.os.ParcelUuid; import android.os.PersistableBundle; -import android.os.test.TestLooper; -import android.telephony.TelephonyManager; import android.util.ArraySet; -import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; -import com.android.server.vcn.VcnContext; -import com.android.server.vcn.VcnNetworkProvider; - import org.junit.Before; import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; import java.util.Collections; import java.util.List; import java.util.Set; -import java.util.UUID; - -public class NetworkPriorityClassifierTest { - private static final String SSID = "TestWifi"; - private static final String SSID_OTHER = "TestWifiOther"; - private static final String PLMN_ID = "123456"; - private static final String PLMN_ID_OTHER = "234567"; - - private static final int SUB_ID = 1; - private static final int WIFI_RSSI = -60; - private static final int WIFI_RSSI_HIGH = -50; - private static final int WIFI_RSSI_LOW = -80; - private static final int CARRIER_ID = 1; - private static final int CARRIER_ID_OTHER = 2; - - private static final int LINK_UPSTREAM_BANDWIDTH_KBPS = 1024; - private static final int LINK_DOWNSTREAM_BANDWIDTH_KBPS = 2048; - - private static final int TEST_MIN_UPSTREAM_BANDWIDTH_KBPS = 100; - private static final int TEST_MIN_DOWNSTREAM_BANDWIDTH_KBPS = 200; - - private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0)); - - private static final NetworkCapabilities WIFI_NETWORK_CAPABILITIES = - new NetworkCapabilities.Builder() - .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) - .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - .setSignalStrength(WIFI_RSSI) - .setSsid(SSID) - .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS) - .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS) - .build(); - - private static final TelephonyNetworkSpecifier TEL_NETWORK_SPECIFIER = - new TelephonyNetworkSpecifier.Builder().setSubscriptionId(SUB_ID).build(); - private static final NetworkCapabilities CELL_NETWORK_CAPABILITIES = - new NetworkCapabilities.Builder() - .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN) - .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) - .setSubscriptionIds(Set.of(SUB_ID)) - .setNetworkSpecifier(TEL_NETWORK_SPECIFIER) - .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS) - .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS) - .build(); - - private static final LinkProperties LINK_PROPERTIES = getLinkPropertiesWithName("test_iface"); - - @Mock private Network mNetwork; - @Mock private TelephonySubscriptionSnapshot mSubscriptionSnapshot; - @Mock private TelephonyManager mTelephonyManager; - - private TestLooper mTestLooper; - private VcnContext mVcnContext; + +public class NetworkPriorityClassifierTest extends NetworkEvaluationTestBase { private UnderlyingNetworkRecord mWifiNetworkRecord; private UnderlyingNetworkRecord mCellNetworkRecord; @Before public void setUp() { - MockitoAnnotations.initMocks(this); - - final Context mockContext = mock(Context.class); - mTestLooper = new TestLooper(); - mVcnContext = - spy( - new VcnContext( - mockContext, - mTestLooper.getLooper(), - mock(VcnNetworkProvider.class), - false /* isInTestMode */)); - doNothing().when(mVcnContext).ensureRunningOnLooperThread(); - - setupSystemService( - mockContext, mTelephonyManager, Context.TELEPHONY_SERVICE, TelephonyManager.class); - when(mTelephonyManager.createForSubscriptionId(SUB_ID)).thenReturn(mTelephonyManager); - when(mTelephonyManager.getNetworkOperator()).thenReturn(PLMN_ID); - when(mTelephonyManager.getSimSpecificCarrierId()).thenReturn(CARRIER_ID); - - mWifiNetworkRecord = - getTestNetworkRecord( - WIFI_NETWORK_CAPABILITIES, - VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES); - mCellNetworkRecord = - getTestNetworkRecord( - CELL_NETWORK_CAPABILITIES, - VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES); - } - - private UnderlyingNetworkRecord getTestNetworkRecord( - NetworkCapabilities nc, List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates) { - return new UnderlyingNetworkRecord( - mNetwork, - nc, - LINK_PROPERTIES, - false /* isBlocked */, - mVcnContext, - underlyingNetworkTemplates, - SUB_GROUP, - mSubscriptionSnapshot, - null /* currentlySelected */, - null /* carrierConfig */); + super.setUp(); + + mWifiNetworkRecord = getTestNetworkRecord(WIFI_NETWORK_CAPABILITIES); + mCellNetworkRecord = getTestNetworkRecord(CELL_NETWORK_CAPABILITIES); + } + + private UnderlyingNetworkRecord getTestNetworkRecord(NetworkCapabilities nc) { + return new UnderlyingNetworkRecord(mNetwork, nc, LINK_PROPERTIES, false /* isBlocked */); } @Test @@ -186,14 +82,14 @@ public class NetworkPriorityClassifierTest { mWifiNetworkRecord, SUB_GROUP, mSubscriptionSnapshot, - null /* currentlySelecetd */, + false /* isSelected */, null /* carrierConfig */)); } private void verifyMatchesPriorityRuleForUpstreamBandwidth( int entryUpstreamBandwidth, int exitUpstreamBandwidth, - UnderlyingNetworkRecord currentlySelected, + boolean isSelected, boolean expectMatch) { final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority = new VcnWifiUnderlyingNetworkTemplate.Builder() @@ -208,14 +104,14 @@ public class NetworkPriorityClassifierTest { mWifiNetworkRecord, SUB_GROUP, mSubscriptionSnapshot, - currentlySelected, + isSelected, null /* carrierConfig */)); } private void verifyMatchesPriorityRuleForDownstreamBandwidth( int entryDownstreamBandwidth, int exitDownstreamBandwidth, - UnderlyingNetworkRecord currentlySelected, + boolean isSelected, boolean expectMatch) { final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority = new VcnWifiUnderlyingNetworkTemplate.Builder() @@ -231,7 +127,7 @@ public class NetworkPriorityClassifierTest { mWifiNetworkRecord, SUB_GROUP, mSubscriptionSnapshot, - currentlySelected, + isSelected, null /* carrierConfig */)); } @@ -240,7 +136,7 @@ public class NetworkPriorityClassifierTest { verifyMatchesPriorityRuleForUpstreamBandwidth( TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS, TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS, - null /* currentlySelected */, + false /* isSelected */, true); } @@ -249,7 +145,7 @@ public class NetworkPriorityClassifierTest { verifyMatchesPriorityRuleForUpstreamBandwidth( LINK_UPSTREAM_BANDWIDTH_KBPS + 1, LINK_UPSTREAM_BANDWIDTH_KBPS + 1, - null /* currentlySelected */, + false /* isSelected */, false); } @@ -258,7 +154,7 @@ public class NetworkPriorityClassifierTest { verifyMatchesPriorityRuleForDownstreamBandwidth( TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS, TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS, - null /* currentlySelected */, + false /* isSelected */, true); } @@ -267,7 +163,7 @@ public class NetworkPriorityClassifierTest { verifyMatchesPriorityRuleForDownstreamBandwidth( LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1, LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1, - null /* currentlySelected */, + false /* isSelected */, false); } @@ -276,7 +172,7 @@ public class NetworkPriorityClassifierTest { verifyMatchesPriorityRuleForUpstreamBandwidth( TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS, TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS, - mWifiNetworkRecord, + true /* isSelected */, true); } @@ -285,7 +181,7 @@ public class NetworkPriorityClassifierTest { verifyMatchesPriorityRuleForUpstreamBandwidth( LINK_UPSTREAM_BANDWIDTH_KBPS + 1, LINK_UPSTREAM_BANDWIDTH_KBPS + 1, - mWifiNetworkRecord, + true /* isSelected */, false); } @@ -294,7 +190,7 @@ public class NetworkPriorityClassifierTest { verifyMatchesPriorityRuleForDownstreamBandwidth( TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS, TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS, - mWifiNetworkRecord, + true /* isSelected */, true); } @@ -303,7 +199,7 @@ public class NetworkPriorityClassifierTest { verifyMatchesPriorityRuleForDownstreamBandwidth( LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1, LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1, - mWifiNetworkRecord, + true /* isSelected */, false); } @@ -318,14 +214,12 @@ public class NetworkPriorityClassifierTest { TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS, TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS) .build(); - final UnderlyingNetworkRecord selectedNetworkRecord = - isSelectedNetwork ? mWifiNetworkRecord : null; assertEquals( expectMatch, checkMatchesWifiPriorityRule( wifiNetworkPriority, mWifiNetworkRecord, - selectedNetworkRecord, + isSelectedNetwork, carrierConfig == null ? null : new PersistableBundleWrapper(carrierConfig))); @@ -381,7 +275,7 @@ public class NetworkPriorityClassifierTest { checkMatchesWifiPriorityRule( wifiNetworkPriority, mWifiNetworkRecord, - null /* currentlySelecetd */, + false /* isSelected */, null /* carrierConfig */)); } @@ -516,7 +410,7 @@ public class NetworkPriorityClassifierTest { mCellNetworkRecord, SUB_GROUP, mSubscriptionSnapshot, - null /* currentlySelected */, + false /* isSelected */, null /* carrierConfig */)); } @@ -543,7 +437,16 @@ public class NetworkPriorityClassifierTest { @Test public void testCalculatePriorityClass() throws Exception { - assertEquals(2, mCellNetworkRecord.priorityClass); + final int priorityClass = + NetworkPriorityClassifier.calculatePriorityClass( + mVcnContext, + mCellNetworkRecord, + VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES, + SUB_GROUP, + mSubscriptionSnapshot, + false /* isSelected */, + null /* carrierConfig */); + assertEquals(2, priorityClass); } private void checkCalculatePriorityClassFailToMatchAny( @@ -561,10 +464,19 @@ public class NetworkPriorityClassifierTest { ncBuilder.addCapability(NET_CAPABILITY_INTERNET); } - final UnderlyingNetworkRecord nonDunNetworkRecord = - getTestNetworkRecord(ncBuilder.build(), templatesRequireDun); + final UnderlyingNetworkRecord nonDunNetworkRecord = getTestNetworkRecord(ncBuilder.build()); + + final int priorityClass = + NetworkPriorityClassifier.calculatePriorityClass( + mVcnContext, + nonDunNetworkRecord, + templatesRequireDun, + SUB_GROUP, + mSubscriptionSnapshot, + false /* isSelected */, + null /* carrierConfig */); - assertEquals(expectedPriorityClass, nonDunNetworkRecord.priorityClass); + assertEquals(expectedPriorityClass, priorityClass); } @Test diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java index 2941fdea20bb..992f10275739 100644 --- a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java +++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java @@ -29,13 +29,12 @@ import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.WI import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.WIFI_EXIT_RSSI_THRESHOLD_DEFAULT; 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.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -67,6 +66,7 @@ import android.util.ArraySet; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.VcnContext; import com.android.server.vcn.VcnNetworkProvider; +import com.android.server.vcn.routeselection.UnderlyingNetworkController.Dependencies; import com.android.server.vcn.routeselection.UnderlyingNetworkController.NetworkBringupCallback; import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkControllerCallback; import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkListener; @@ -77,6 +77,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.Spy; import java.util.ArrayList; import java.util.Arrays; @@ -154,10 +155,13 @@ public class UnderlyingNetworkControllerTest { @Mock private UnderlyingNetworkControllerCallback mNetworkControllerCb; @Mock private Network mNetwork; + @Spy private Dependencies mDependencies = new Dependencies(); + @Captor private ArgumentCaptor<UnderlyingNetworkListener> mUnderlyingNetworkListenerCaptor; private TestLooper mTestLooper; private VcnContext mVcnContext; + private UnderlyingNetworkEvaluator mNetworkEvaluator; private UnderlyingNetworkController mUnderlyingNetworkController; @Before @@ -189,13 +193,28 @@ public class UnderlyingNetworkControllerTest { when(mSubscriptionSnapshot.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(INITIAL_SUB_IDS); + mNetworkEvaluator = + spy( + new UnderlyingNetworkEvaluator( + mVcnContext, + mNetwork, + VcnGatewayConnectionConfigTest.buildTestConfig() + .getVcnUnderlyingNetworkPriorities(), + SUB_GROUP, + mSubscriptionSnapshot, + null)); + doReturn(mNetworkEvaluator) + .when(mDependencies) + .newUnderlyingNetworkEvaluator(any(), any(), any(), any(), any(), any()); + mUnderlyingNetworkController = new UnderlyingNetworkController( mVcnContext, VcnGatewayConnectionConfigTest.buildTestConfig(), SUB_GROUP, mSubscriptionSnapshot, - mNetworkControllerCb); + mNetworkControllerCb, + mDependencies); } private void resetVcnContext() { @@ -489,13 +508,7 @@ public class UnderlyingNetworkControllerTest { NetworkCapabilities networkCapabilities, LinkProperties linkProperties, boolean isBlocked) { - return new UnderlyingNetworkRecord( - network, - networkCapabilities, - linkProperties, - isBlocked, - false /* isSelected */, - 0 /* priorityClass */); + return new UnderlyingNetworkRecord(network, networkCapabilities, linkProperties, isBlocked); } @Test @@ -515,24 +528,12 @@ public class UnderlyingNetworkControllerTest { UnderlyingNetworkRecord recordC = new UnderlyingNetworkRecord( mNetwork, - INITIAL_NETWORK_CAPABILITIES, - INITIAL_LINK_PROPERTIES, - false /* isBlocked */, - true /* isSelected */, - -1 /* priorityClass */); - UnderlyingNetworkRecord recordD = - getTestNetworkRecord( - mNetwork, UPDATED_NETWORK_CAPABILITIES, UPDATED_LINK_PROPERTIES, false /* isBlocked */); assertEquals(recordA, recordB); - assertEquals(recordA, recordC); - assertNotEquals(recordA, recordD); - - assertTrue(UnderlyingNetworkRecord.isEqualIncludingPriorities(recordA, recordB)); - assertFalse(UnderlyingNetworkRecord.isEqualIncludingPriorities(recordA, recordC)); + assertNotEquals(recordA, recordC); } @Test @@ -540,6 +541,19 @@ public class UnderlyingNetworkControllerTest { verifyRegistrationOnAvailableAndGetCallback(); } + @Test + public void testUpdateSubscriptionSnapshotAndCarrierConfig() { + verifyRegistrationOnAvailableAndGetCallback(); + + TelephonySubscriptionSnapshot subscriptionUpdate = + mock(TelephonySubscriptionSnapshot.class); + when(subscriptionUpdate.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(UPDATED_SUB_IDS); + + mUnderlyingNetworkController.updateSubscriptionSnapshot(subscriptionUpdate); + + verify(mNetworkEvaluator).reevaluate(any(), any(), any(), any()); + } + private UnderlyingNetworkListener verifyRegistrationOnAvailableAndGetCallback() { return verifyRegistrationOnAvailableAndGetCallback(INITIAL_NETWORK_CAPABILITIES); } @@ -583,6 +597,7 @@ public class UnderlyingNetworkControllerTest { INITIAL_LINK_PROPERTIES, false /* isBlocked */); verifyOnSelectedUnderlyingNetworkChanged(expectedRecord); + verify(mNetworkEvaluator).setIsSelected(eq(true), any(), any(), any(), any()); return cb; } @@ -713,7 +728,8 @@ public class UnderlyingNetworkControllerTest { VcnGatewayConnectionConfigTest.buildTestConfig(networkTemplates), SUB_GROUP, mSubscriptionSnapshot, - mNetworkControllerCb); + mNetworkControllerCb, + mDependencies); verify(cm) .registerNetworkCallback( @@ -724,30 +740,43 @@ public class UnderlyingNetworkControllerTest { return mUnderlyingNetworkListenerCaptor.getValue(); } - private UnderlyingNetworkRecord bringupNetworkAndGetRecord( + private UnderlyingNetworkEvaluator bringupNetworkAndGetEvaluator( UnderlyingNetworkListener cb, NetworkCapabilities requestNetworkCaps, - List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, - UnderlyingNetworkRecord currentlySelected) { + List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates) { final Network network = mock(Network.class); final NetworkCapabilities responseNetworkCaps = buildResponseNwCaps(requestNetworkCaps, INITIAL_SUB_IDS); + final UnderlyingNetworkEvaluator evaluator = + spy( + new UnderlyingNetworkEvaluator( + mVcnContext, + network, + underlyingNetworkTemplates, + SUB_GROUP, + mSubscriptionSnapshot, + null)); + doReturn(evaluator) + .when(mDependencies) + .newUnderlyingNetworkEvaluator(any(), any(), any(), any(), any(), any()); cb.onAvailable(network); cb.onCapabilitiesChanged(network, responseNetworkCaps); cb.onLinkPropertiesChanged(network, INITIAL_LINK_PROPERTIES); cb.onBlockedStatusChanged(network, false /* isFalse */); - return new UnderlyingNetworkRecord( - network, - responseNetworkCaps, - INITIAL_LINK_PROPERTIES, - false /* isBlocked */, - mVcnContext, - underlyingNetworkTemplates, - SUB_GROUP, - mSubscriptionSnapshot, - currentlySelected, - null /* carrierConfig */); + + return evaluator; + } + + private void verifySelectNetwork(UnderlyingNetworkEvaluator expectedEvaluator) { + verifyOnSelectedUnderlyingNetworkChanged(expectedEvaluator.getNetworkRecord()); + verify(expectedEvaluator).setIsSelected(eq(true), any(), any(), any(), any()); + } + + private void verifyNeverSelectNetwork(UnderlyingNetworkEvaluator expectedEvaluator) { + verify(mNetworkControllerCb, never()) + .onSelectedUnderlyingNetworkChanged(eq(expectedEvaluator.getNetworkRecord())); + verify(expectedEvaluator, never()).setIsSelected(eq(true), any(), any(), any(), any()); } @Test @@ -759,19 +788,15 @@ public class UnderlyingNetworkControllerTest { UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates); // Bring up CBS network - final UnderlyingNetworkRecord cbsNetworkRecord = - bringupNetworkAndGetRecord( - cb, - CBS_NETWORK_CAPABILITIES, - networkTemplates, - null /* currentlySelected */); - verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(cbsNetworkRecord)); + final UnderlyingNetworkEvaluator cbsNetworkEvaluator = + bringupNetworkAndGetEvaluator(cb, CBS_NETWORK_CAPABILITIES, networkTemplates); + verifySelectNetwork(cbsNetworkEvaluator); // Bring up DUN network - final UnderlyingNetworkRecord dunNetworkRecord = - bringupNetworkAndGetRecord( - cb, DUN_NETWORK_CAPABILITIES, networkTemplates, cbsNetworkRecord); - verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(dunNetworkRecord)); + final UnderlyingNetworkEvaluator dunNetworkEvaluator = + bringupNetworkAndGetEvaluator(cb, DUN_NETWORK_CAPABILITIES, networkTemplates); + verifySelectNetwork(dunNetworkEvaluator); + verify(cbsNetworkEvaluator).setIsSelected(eq(false), any(), any(), any(), any()); } @Test @@ -783,20 +808,14 @@ public class UnderlyingNetworkControllerTest { UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates); // Bring up DUN network - final UnderlyingNetworkRecord dunNetworkRecord = - bringupNetworkAndGetRecord( - cb, - DUN_NETWORK_CAPABILITIES, - networkTemplates, - null /* currentlySelected */); - verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(dunNetworkRecord)); + final UnderlyingNetworkEvaluator dunNetworkEvaluator = + bringupNetworkAndGetEvaluator(cb, DUN_NETWORK_CAPABILITIES, networkTemplates); + verifySelectNetwork(dunNetworkEvaluator); // Bring up CBS network - final UnderlyingNetworkRecord cbsNetworkRecord = - bringupNetworkAndGetRecord( - cb, CBS_NETWORK_CAPABILITIES, networkTemplates, dunNetworkRecord); - verify(mNetworkControllerCb, never()) - .onSelectedUnderlyingNetworkChanged(eq(cbsNetworkRecord)); + final UnderlyingNetworkEvaluator cbsNetworkEvaluator = + bringupNetworkAndGetEvaluator(cb, CBS_NETWORK_CAPABILITIES, networkTemplates); + verifyNeverSelectNetwork(cbsNetworkEvaluator); } @Test @@ -808,13 +827,9 @@ public class UnderlyingNetworkControllerTest { UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates); // Bring up an Internet network without DUN capability - final UnderlyingNetworkRecord networkRecord = - bringupNetworkAndGetRecord( - cb, - INITIAL_NETWORK_CAPABILITIES, - networkTemplates, - null /* currentlySelected */); - verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(networkRecord)); + final UnderlyingNetworkEvaluator evaluator = + bringupNetworkAndGetEvaluator(cb, INITIAL_NETWORK_CAPABILITIES, networkTemplates); + verifySelectNetwork(evaluator); } @Test @@ -825,10 +840,8 @@ public class UnderlyingNetworkControllerTest { new VcnCellUnderlyingNetworkTemplate.Builder().setDun(MATCH_REQUIRED).build()); UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates); - bringupNetworkAndGetRecord( - cb, CBS_NETWORK_CAPABILITIES, networkTemplates, null /* currentlySelected */); - - verify(mNetworkControllerCb, never()) - .onSelectedUnderlyingNetworkChanged(any(UnderlyingNetworkRecord.class)); + final UnderlyingNetworkEvaluator evaluator = + bringupNetworkAndGetEvaluator(cb, CBS_NETWORK_CAPABILITIES, networkTemplates); + verifyNeverSelectNetwork(evaluator); } } diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java new file mode 100644 index 000000000000..a4567ddc20a1 --- /dev/null +++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.vcn.routeselection; + +import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_INVALID; +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; + +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 android.net.vcn.VcnGatewayConnectionConfig; +import android.os.PersistableBundle; + +import org.junit.Before; +import org.junit.Test; + +public class UnderlyingNetworkEvaluatorTest extends NetworkEvaluationTestBase { + private PersistableBundleWrapper mCarrierConfig; + + @Before + public void setUp() { + super.setUp(); + mCarrierConfig = new PersistableBundleWrapper(new PersistableBundle()); + } + + private UnderlyingNetworkEvaluator newUnderlyingNetworkEvaluator() { + return new UnderlyingNetworkEvaluator( + mVcnContext, + mNetwork, + VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES, + SUB_GROUP, + mSubscriptionSnapshot, + mCarrierConfig); + } + + @Test + public void testInitializedEvaluator() throws Exception { + final UnderlyingNetworkEvaluator evaluator = newUnderlyingNetworkEvaluator(); + + assertFalse(evaluator.isValid()); + assertEquals(mNetwork, evaluator.getNetwork()); + assertEquals(PRIORITY_INVALID, evaluator.getPriorityClass()); + + try { + evaluator.getNetworkRecord(); + fail("Expected to fail because evaluator is not valid"); + } catch (Exception expected) { + } + } + + @Test + public void testValidEvaluator() { + final UnderlyingNetworkEvaluator evaluator = newUnderlyingNetworkEvaluator(); + evaluator.setNetworkCapabilities( + CELL_NETWORK_CAPABILITIES, + VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES, + SUB_GROUP, + mSubscriptionSnapshot, + mCarrierConfig); + evaluator.setLinkProperties( + LINK_PROPERTIES, + VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES, + SUB_GROUP, + mSubscriptionSnapshot, + mCarrierConfig); + evaluator.setIsBlocked( + false /* isBlocked */, + VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES, + SUB_GROUP, + mSubscriptionSnapshot, + mCarrierConfig); + + final UnderlyingNetworkRecord expectedRecord = + new UnderlyingNetworkRecord( + mNetwork, + CELL_NETWORK_CAPABILITIES, + LINK_PROPERTIES, + false /* isBlocked */); + + assertTrue(evaluator.isValid()); + assertEquals(mNetwork, evaluator.getNetwork()); + assertEquals(2, evaluator.getPriorityClass()); + assertEquals(expectedRecord, evaluator.getNetworkRecord()); + } +} |