diff options
207 files changed, 8666 insertions, 2397 deletions
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp index 0712c0adcd..28e5ee2ca9 100644 --- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp @@ -240,11 +240,11 @@ TEST_F(ZippedBugreportGenerationTest, Is1MBMBinSize) { EXPECT_GE(st.st_size, 1000000 /* 1MB */); } -TEST_F(ZippedBugreportGenerationTest, TakesBetween30And300Seconds) { - EXPECT_GE(duration, 30s) << "Expected completion in more than 30s. Actual time " - << duration.count() << " s."; +TEST_F(ZippedBugreportGenerationTest, TakesBetween20And300Seconds) { + EXPECT_GE(duration, 20s) << "Expected completion in more than 20s. Actual time " + << duration.count() << " ms."; EXPECT_LE(duration, 300s) << "Expected completion in less than 300s. Actual time " - << duration.count() << " s."; + << duration.count() << " ms."; } /** diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp index 677d6c78ea..49c1318945 100644 --- a/cmds/dumpsys/tests/dumpsys_test.cpp +++ b/cmds/dumpsys/tests/dumpsys_test.cpp @@ -61,6 +61,10 @@ class ServiceManagerMock : public IServiceManager { MOCK_METHOD1(getDeclaredInstances, Vector<String16>(const String16&)); MOCK_METHOD1(updatableViaApex, std::optional<String16>(const String16&)); MOCK_METHOD1(getConnectionInfo, std::optional<ConnectionInfo>(const String16&)); + MOCK_METHOD2(registerForNotifications, status_t(const String16&, + const sp<LocalRegistrationCallback>&)); + MOCK_METHOD2(unregisterForNotifications, status_t(const String16&, + const sp<LocalRegistrationCallback>&)); protected: MOCK_METHOD0(onAsBinder, IBinder*()); }; diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp index b6f42ad172..c796da6c46 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -244,7 +244,7 @@ bool clear_primary_reference_profile(const std::string& package_name, // The location is the profile name for primary apks or the dex path for secondary dex files. bool clear_primary_current_profiles(const std::string& package_name, const std::string& location) { bool success = true; - // For secondary dex files, we don't really need the user but we use it for sanity checks. + // For secondary dex files, we don't really need the user but we use it for validity checks. std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr); for (auto user : users) { success &= clear_current_profile(package_name, location, user, /*is_secondary_dex*/false); @@ -468,7 +468,7 @@ static void open_profile_files(uid_t uid, const std::string& package_name, *reference_profile_fd = open_reference_profile(uid, package_name, location, /*read_write*/ true, is_secondary_dex); - // For secondary dex files, we don't really need the user but we use it for sanity checks. + // For secondary dex files, we don't really need the user but we use it for validity checks. // Note: the user owning the dex file should be the current user. std::vector<userid_t> users; if (is_secondary_dex){ diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index c4ecd070c1..0f8a732345 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -829,7 +829,7 @@ void remove_path_xattr(const std::string& path, const char* inode_xattr) { * to top level directories (i.e. have ".."). */ static int validate_path(const std::string& dir, const std::string& path, int maxSubdirs) { - // Argument sanity checking + // Argument check if (dir.find('/') != 0 || dir.rfind('/') != dir.size() - 1 || dir.find("..") != std::string::npos) { LOG(ERROR) << "Invalid directory " << dir; diff --git a/cmds/ip-up-vpn/Android.bp b/cmds/ip-up-vpn/Android.bp new file mode 100644 index 0000000000..c746f7fde3 --- /dev/null +++ b/cmds/ip-up-vpn/Android.bp @@ -0,0 +1,31 @@ +// Copyright 2011 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_binary { + name: "ip-up-vpn", + + srcs: ["ip-up-vpn.c"], + cflags: [ + "-Wall", + "-Werror", + ], + shared_libs: [ + "libcutils", + "liblog", + ], +} diff --git a/cmds/ip-up-vpn/Android.mk b/cmds/ip-up-vpn/Android.mk deleted file mode 100644 index 396ae9db04..0000000000 --- a/cmds/ip-up-vpn/Android.mk +++ /dev/null @@ -1,30 +0,0 @@ -# -# Copyright (C) 2011 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := ip-up-vpn.c -LOCAL_CFLAGS := -Wall -Werror -LOCAL_SHARED_LIBRARIES := libcutils liblog -LOCAL_MODULE := ip-up-vpn -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE -LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/ppp -LOCAL_MODULE_TAGS := optional - -include $(BUILD_EXECUTABLE) diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp index fe417a362f..d5ca725eb9 100644 --- a/cmds/service/service.cpp +++ b/cmds/service/service.cpp @@ -21,6 +21,7 @@ #include <cutils/ashmem.h> #include <getopt.h> +#include <libgen.h> #include <stdlib.h> #include <stdio.h> #include <string.h> diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp index 80c0548fca..32922ca24c 100644 --- a/cmds/servicemanager/Android.bp +++ b/cmds/servicemanager/Android.bp @@ -47,6 +47,14 @@ cc_binary { } cc_binary { + name: "servicemanager.microdroid", + defaults: ["servicemanager_defaults"], + init_rc: ["servicemanager.microdroid.rc"], + srcs: ["main.cpp"], + bootstrap: true, +} + +cc_binary { name: "servicemanager.recovery", stem: "servicemanager", recovery: true, diff --git a/cmds/servicemanager/servicemanager.microdroid.rc b/cmds/servicemanager/servicemanager.microdroid.rc new file mode 100644 index 0000000000..e01f132c64 --- /dev/null +++ b/cmds/servicemanager/servicemanager.microdroid.rc @@ -0,0 +1,8 @@ +service servicemanager /system/bin/servicemanager.microdroid + class core + user system + group system readproc + critical + onrestart restart apexd + task_profiles ServiceCapacityLow + shutdown critical diff --git a/data/etc/Android.bp b/data/etc/Android.bp index 1be3a69a7d..5003151b51 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -119,6 +119,12 @@ prebuilt_etc { } prebuilt_etc { + name: "android.hardware.sensor.dynamic.head_tracker.prebuilt.xml", + src: "android.hardware.sensor.dynamic.head_tracker.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + +prebuilt_etc { name: "android.hardware.sensor.gyroscope.prebuilt.xml", src: "android.hardware.sensor.gyroscope.xml", defaults: ["frameworks_native_data_etc_defaults"], @@ -245,6 +251,12 @@ prebuilt_etc { } prebuilt_etc { + name: "go_handheld_core_hardware.prebuilt.xml", + src: "go_handheld_core_hardware.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + +prebuilt_etc { name: "handheld_core_hardware.prebuilt.xml", src: "handheld_core_hardware.xml", defaults: ["frameworks_native_data_etc_defaults"], diff --git a/data/etc/android.hardware.sensor.accelerometer_limited_axes.xml b/data/etc/android.hardware.sensor.accelerometer_limited_axes.xml new file mode 100644 index 0000000000..6a43d5dc5f --- /dev/null +++ b/data/etc/android.hardware.sensor.accelerometer_limited_axes.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<!-- Feature for devices with a limited axes accelerometer sensor. --> +<permissions> + <feature name="android.hardware.sensor.accelerometer_limited_axes" /> +</permissions> diff --git a/data/etc/android.hardware.sensor.accelerometer_limited_axes_uncalibrated.xml b/data/etc/android.hardware.sensor.accelerometer_limited_axes_uncalibrated.xml new file mode 100644 index 0000000000..4c6755c45a --- /dev/null +++ b/data/etc/android.hardware.sensor.accelerometer_limited_axes_uncalibrated.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<!-- Feature for devices with an uncalibrated limited axes accelerometer sensor. --> +<permissions> + <feature name="android.hardware.sensor.accelerometer_limited_axes_uncalibrated" /> +</permissions> diff --git a/data/etc/android.hardware.sensor.dynamic.head_tracker.xml b/data/etc/android.hardware.sensor.dynamic.head_tracker.xml new file mode 100644 index 0000000000..ece58c9a72 --- /dev/null +++ b/data/etc/android.hardware.sensor.dynamic.head_tracker.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<!-- Feature for devices with a head tracker sensor. --> +<permissions> + <feature name="android.hardware.sensor.dynamic.head_tracker" /> +</permissions>
\ No newline at end of file diff --git a/data/etc/android.hardware.sensor.gyroscope_limited_axes.xml b/data/etc/android.hardware.sensor.gyroscope_limited_axes.xml new file mode 100644 index 0000000000..45c6547fe1 --- /dev/null +++ b/data/etc/android.hardware.sensor.gyroscope_limited_axes.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<!-- Feature for devices with a limited axes gyroscope. --> +<permissions> + <feature name="android.hardware.sensor.gyroscope_limited_axes" /> +</permissions> diff --git a/data/etc/android.hardware.sensor.gyroscope_limited_axes_uncalibrated.xml b/data/etc/android.hardware.sensor.gyroscope_limited_axes_uncalibrated.xml new file mode 100644 index 0000000000..2ca8364677 --- /dev/null +++ b/data/etc/android.hardware.sensor.gyroscope_limited_axes_uncalibrated.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<!-- Feature for devices with an uncalibrated limited axes gyroscope. --> +<permissions> + <feature name="android.hardware.sensor.gyroscope_limited_axes_uncalibrated" /> +</permissions> diff --git a/data/etc/android.hardware.vulkan.version-1_3.xml b/data/etc/android.hardware.vulkan.version-1_3.xml new file mode 100644 index 0000000000..4ecea7bc64 --- /dev/null +++ b/data/etc/android.hardware.vulkan.version-1_3.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 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. +--> + +<!-- This is the standard feature indicating that the device has a Vulkan + driver that supports API version 1.3 (0x00403000) --> +<permissions> + <feature name="android.hardware.vulkan.version" version="4206592" /> +</permissions> diff --git a/headers/Android.bp b/headers/Android.bp index 7481a230c3..cb18837527 100644 --- a/headers/Android.bp +++ b/headers/Android.bp @@ -28,6 +28,11 @@ cc_library_headers { "libstagefright_foundation_headers", ], min_sdk_version: "29", + apex_available: [ + "//apex_available:platform", + "com.android.media", + "com.android.media.swcodec", + ], host_supported: true, target: { diff --git a/include/android/bitmap.h b/include/android/bitmap.h index 6704a1ddf2..35f87f96ae 100644 --- a/include/android/bitmap.h +++ b/include/android/bitmap.h @@ -68,6 +68,8 @@ enum AndroidBitmapFormat { ANDROID_BITMAP_FORMAT_A_8 = 8, /** Each component is stored as a half float. **/ ANDROID_BITMAP_FORMAT_RGBA_F16 = 9, + /** Red: 10 bits, Green: 10 bits, Blue: 10 bits, Alpha: 2 bits. **/ + ANDROID_BITMAP_FORMAT_RGBA_1010102 = 10, }; /** Bitmap alpha format */ diff --git a/include/android/choreographer.h b/include/android/choreographer.h index 6f579ca0af..98f0eec8cc 100644 --- a/include/android/choreographer.h +++ b/include/android/choreographer.h @@ -39,6 +39,12 @@ struct AChoreographer; */ typedef struct AChoreographer AChoreographer; + +/** + * The identifier of a frame timeline. + */ +typedef int64_t AVsyncId; + struct AChoreographerFrameCallbackData; /** * Opaque type that provides access to an AChoreographerFrameCallbackData object. @@ -203,7 +209,7 @@ size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex( /** * The vsync ID token used to map Choreographer data. */ -int64_t AChoreographerFrameCallbackData_getFrameTimelineVsyncId( +AVsyncId AChoreographerFrameCallbackData_getFrameTimelineVsyncId( const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33); /** diff --git a/include/android/input.h b/include/android/input.h index fbd61b53f4..e6ad943f55 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -877,6 +877,7 @@ enum { * Keyboard types. * * Refer to the documentation on android.view.InputDevice for more details. + * Note: When adding a new keyboard type here InputDeviceInfo::setKeyboardType needs to be updated. */ enum { /** none */ diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h new file mode 100644 index 0000000000..5fa47f64be --- /dev/null +++ b/include/android/performance_hint.h @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_NATIVE_PERFORMANCE_HINT_H +#define ANDROID_NATIVE_PERFORMANCE_HINT_H + +#include <sys/cdefs.h> + +/****************************************************************** + * + * IMPORTANT NOTICE: + * + * This file is part of Android's set of stable system headers + * exposed by the Android NDK (Native Development Kit). + * + * Third-party source AND binary code relies on the definitions + * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES. + * + * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES) + * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS + * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY + * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES + */ + +#include <android/api-level.h> +#include <stdint.h> + +__BEGIN_DECLS + +struct APerformanceHintManager; +struct APerformanceHintSession; + +/** + * An opaque type representing a handle to a performance hint manager. + * It must be released after use. + * + * <p>To use:<ul> + * <li>Obtain the performance hint manager instance by calling + * {@link APerformanceHint_getManager} function.</li> + * <li>Create an {@link APerformanceHintSession} with + * {@link APerformanceHint_createSession}.</li> + * <li>Get the preferred update rate in nanoseconds with + * {@link APerformanceHint_getPreferredUpdateRateNanos}.</li> + */ +typedef struct APerformanceHintManager APerformanceHintManager; + +/** + * An opaque type representing a handle to a performance hint session. + * A session can only be acquired from a {@link APerformanceHintManager} + * with {@link APerformanceHint_getPreferredUpdateRateNanos}. It must be + * freed with {@link APerformanceHint_closeSession} after use. + * + * A Session represents a group of threads with an inter-related workload such that hints for + * their performance should be considered as a unit. The threads in a given session should be + * long-life and not created or destroyed dynamically. + * + * <p>Each session is expected to have a periodic workload with a target duration for each + * cycle. The cycle duration is likely greater than the target work duration to allow other + * parts of the pipeline to run within the available budget. For example, a renderer thread may + * work at 60hz in order to produce frames at the display's frame but have a target work + * duration of only 6ms.</p> + * + * <p>After each cycle of work, the client is expected to use + * {@link APerformanceHint_reportActualWorkDuration} to report the actual time taken to + * complete.</p> + * + * <p>To use:<ul> + * <li>Update a sessions target duration for each cycle of work + * with {@link APerformanceHint_updateTargetWorkDuration}.</li> + * <li>Report the actual duration for the last cycle of work with + * {@link APerformanceHint_reportActualWorkDuration}.</li> + * <li>Release the session instance with + * {@link APerformanceHint_closeSession}.</li></ul></p> + */ +typedef struct APerformanceHintSession APerformanceHintSession; + +/** + * Acquire an instance of the performance hint manager. + * + * @return manager instance on success, nullptr on failure. + */ +APerformanceHintManager* APerformanceHint_getManager() __INTRODUCED_IN(__ANDROID_API_T__); + +/** + * Creates a session for the given set of threads and sets their initial target work + * duration. + * @param manager The performance hint manager instance. + * @param threadIds The list of threads to be associated with this session. They must be part of + * this app's thread group. + * @param size the size of threadIds. + * @param initialTargetWorkDurationNanos The desired duration in nanoseconds for the new session. + * This must be positive. + * @return manager instance on success, nullptr on failure. + */ +APerformanceHintSession* APerformanceHint_createSession( + APerformanceHintManager* manager, + const int32_t* threadIds, size_t size, + int64_t initialTargetWorkDurationNanos) __INTRODUCED_IN(__ANDROID_API_T__); + +/** + * Get preferred update rate information for this device. + * + * @param manager The performance hint manager instance. + * @return the preferred update rate supported by device software. + */ +int64_t APerformanceHint_getPreferredUpdateRateNanos( + APerformanceHintManager* manager) __INTRODUCED_IN(__ANDROID_API_T__); + +/** + * Updates this session's target duration for each cycle of work. + * + * @param session The performance hint session instance to update. + * @param targetDurationNanos the new desired duration in nanoseconds. This must be positive. + * @return 0 on success + * EINVAL if targetDurationNanos is not positive. + * EPIPE if communication with the system service has failed. + */ +int APerformanceHint_updateTargetWorkDuration( + APerformanceHintSession* session, + int64_t targetDurationNanos) __INTRODUCED_IN(__ANDROID_API_T__); + +/** + * Reports the actual duration for the last cycle of work. + * + * <p>The system will attempt to adjust the core placement of the threads within the thread + * group and/or the frequency of the core on which they are run to bring the actual duration + * close to the target duration.</p> + * + * @param session The performance hint session instance to update. + * @param actualDurationNanos how long the thread group took to complete its last task in + * nanoseconds. This must be positive. + * @return 0 on success + * EINVAL if actualDurationNanos is not positive. + * EPIPE if communication with the system service has failed. + */ +int APerformanceHint_reportActualWorkDuration( + APerformanceHintSession* session, + int64_t actualDurationNanos) __INTRODUCED_IN(__ANDROID_API_T__); + +/** + * Release the performance hint manager pointer acquired via + * {@link APerformanceHint_createSession}. + * + * @param session The performance hint session instance to release. + */ +void APerformanceHint_closeSession( + APerformanceHintSession* session) __INTRODUCED_IN(__ANDROID_API_T__); + +__END_DECLS + +#endif // ANDROID_NATIVE_PERFORMANCE_HINT_H diff --git a/include/android/sensor.h b/include/android/sensor.h index 45e8afc7de..7e86d3fbb8 100644 --- a/include/android/sensor.h +++ b/include/android/sensor.h @@ -263,6 +263,44 @@ enum { * Measures the orientation and rotational velocity of a user's head. */ ASENSOR_TYPE_HEAD_TRACKER = 37, + /** + * {@link ASENSOR_TYPE_ACCELEROMETER_LIMITED_AXES} + * reporting-mode: continuous + * + * The first three values are in SI units (m/s^2) and measure the acceleration of the device + * minus the force of gravity. The last three values indicate which acceleration axes are + * supported. A value of 1.0 means supported and a value of 0 means not supported. + */ + ASENSOR_TYPE_ACCELEROMETER_LIMITED_AXES = 38, + /** + * {@link ASENSOR_TYPE_GYROSCOPE_LIMITED_AXES} + * reporting-mode: continuous + * + * The first three values are in radians/second and measure the rate of rotation around the X, + * Y and Z axis. The last three values indicate which rotation axes are supported. A value of + * 1.0 means supported and a value of 0 means not supported. + */ + ASENSOR_TYPE_GYROSCOPE_LIMITED_AXES = 39, + /** + * {@link ASENSOR_TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED} + * reporting-mode: continuous + * + * The first three values are in SI units (m/s^2) and measure the acceleration of the device + * minus the force of gravity. The middle three values represent the estimated bias for each + * axis. The last three values indicate which acceleration axes are supported. A value of 1.0 + * means supported and a value of 0 means not supported. + */ + ASENSOR_TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED = 40, + /** + * {@link ASENSOR_TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED} + * reporting-mode: continuous + * + * The first three values are in radians/second and measure the rate of rotation around the X, + * Y and Z axis. The middle three values represent the estimated drift around each axis in + * rad/s. The last three values indicate which rotation axes are supported. A value of 1.0 means + * supported and a value of 0 means not supported. + */ + ASENSOR_TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED = 41, }; /** @@ -479,6 +517,52 @@ typedef struct AHeadTrackerEvent { int32_t discontinuity_count; } AHeadTrackerEvent; +typedef struct ALimitedAxesImuEvent { + union { + float calib[3]; + struct { + float x; + float y; + float z; + }; + }; + union { + float supported[3]; + struct { + float x_supported; + float y_supported; + float z_supported; + }; + }; +} ALimitedAxesImuEvent; + +typedef struct ALimitedAxesImuUncalibratedEvent { + union { + float uncalib[3]; + struct { + float x_uncalib; + float y_uncalib; + float z_uncalib; + }; + }; + union { + float bias[3]; + struct { + float x_bias; + float y_bias; + float z_bias; + }; + }; + union { + float supported[3]; + struct { + float x_supported; + float y_supported; + float z_supported; + }; + }; +} ALimitedAxesImuUncalibratedEvent; + /** * Information that describes a sensor event, refer to * <a href="/reference/android/hardware/SensorEvent">SensorEvent</a> for additional @@ -516,6 +600,8 @@ typedef struct ASensorEvent { ADynamicSensorEvent dynamic_sensor_meta; AAdditionalInfoEvent additional_info; AHeadTrackerEvent head_tracker; + ALimitedAxesImuEvent limited_axes_imu; + ALimitedAxesImuUncalibratedEvent limited_axes_imu_uncalibrated; }; union { uint64_t data[8]; diff --git a/include/android/surface_control.h b/include/android/surface_control.h index 3a131045e8..9a36ecb537 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -28,6 +28,7 @@ #include <sys/cdefs.h> +#include <android/choreographer.h> #include <android/data_space.h> #include <android/hardware_buffer.h> #include <android/hdr_metadata.h> @@ -596,13 +597,23 @@ void ASurfaceTransaction_setEnableBackPressure(ASurfaceTransaction* transaction, __INTRODUCED_IN(31); /** - * Sets the frame timeline to use. + * Sets the frame timeline to use in Surface Flinger. + * + * A frame timeline should be chosen based on what frame deadline the application + * can meet when rendering the frame and the application's desired present time. + * By setting a frame timeline, Surface Flinger tries to present the frame at the corresponding + * expected present time. + * + * To receive frame timelines, a callback must be posted to Choreographer using + * AChoreographer_postExtendedFrameCallback(). The \a vsnycId can then be extracted from the + * callback payload using AChoreographerFrameCallbackData_getFrameTimelineVsyncId(). * * \param vsyncId The vsync ID received from AChoreographer, setting the frame's present target to - * the corresponding expected present time and deadline from the frame to be rendered. + * the corresponding expected present time and deadline from the frame to be rendered. A stale or + * invalid value will be ignored. */ void ASurfaceTransaction_setFrameTimeline(ASurfaceTransaction* transaction, - int64_t vsyncId) __INTRODUCED_IN(33); + AVsyncId vsyncId) __INTRODUCED_IN(33); __END_DECLS diff --git a/include/ftl/Flags.h b/include/ftl/Flags.h index ae708313fd..932af2d9f9 100644 --- a/include/ftl/Flags.h +++ b/include/ftl/Flags.h @@ -209,12 +209,12 @@ namespace flag_operators { template <typename F, typename = std::enable_if_t<ftl::is_scoped_enum_v<F>>> inline Flags<F> operator~(F f) { - return static_cast<F>(~ftl::enum_cast(f)); + return static_cast<F>(~ftl::to_underlying(f)); } template <typename F, typename = std::enable_if_t<ftl::is_scoped_enum_v<F>>> Flags<F> operator|(F lhs, F rhs) { - return static_cast<F>(ftl::enum_cast(lhs) | ftl::enum_cast(rhs)); + return static_cast<F>(ftl::to_underlying(lhs) | ftl::to_underlying(rhs)); } } // namespace flag_operators diff --git a/include/ftl/array_traits.h b/include/ftl/details/array_traits.h index 1265fa15fe..16e63eca74 100644 --- a/include/ftl/array_traits.h +++ b/include/ftl/details/array_traits.h @@ -21,9 +21,9 @@ #include <new> #include <type_traits> -#define FTL_ARRAY_TRAIT(T, U) using U = typename ArrayTraits<T>::U +#define FTL_ARRAY_TRAIT(T, U) using U = typename details::ArrayTraits<T>::U -namespace android::ftl { +namespace android::ftl::details { template <typename T> struct ArrayTraits { @@ -132,4 +132,4 @@ struct ArrayComparators { } }; -} // namespace android::ftl +} // namespace android::ftl::details diff --git a/include/ftl/enum.h b/include/ftl/enum.h index dfe3a0976b..5234c051b1 100644 --- a/include/ftl/enum.h +++ b/include/ftl/enum.h @@ -87,11 +87,13 @@ inline constexpr bool is_scoped_enum_v = is_scoped_enum<T>::value; // Shorthand for casting an enumerator to its integral value. // +// TODO: Replace with std::to_underlying in C++23. +// // enum class E { A, B, C }; -// static_assert(ftl::enum_cast(E::B) == 1); +// static_assert(ftl::to_underlying(E::B) == 1); // template <typename E> -constexpr auto enum_cast(E v) { +constexpr auto to_underlying(E v) { return static_cast<std::underlying_type_t<E>>(v); } @@ -137,19 +139,19 @@ struct enum_end { template <typename E> struct enum_end<E, std::void_t<decltype(E::ftl_last)>> { - static constexpr E value = E{enum_cast(E::ftl_last) + 1}; + static constexpr E value = E{to_underlying(E::ftl_last) + 1}; }; template <typename E> inline constexpr E enum_end_v = enum_end<E>::value; template <typename E> -inline constexpr E enum_last_v = E{enum_cast(enum_end_v<E>) - 1}; +inline constexpr E enum_last_v = E{to_underlying(enum_end_v<E>) - 1}; template <typename E> struct enum_size { - static constexpr auto kBegin = enum_cast(enum_begin_v<E>); - static constexpr auto kEnd = enum_cast(enum_end_v<E>); + static constexpr auto kBegin = to_underlying(enum_begin_v<E>); + static constexpr auto kEnd = to_underlying(enum_end_v<E>); static_assert(kBegin < kEnd, "Invalid range"); static constexpr std::size_t value = kEnd - kBegin; @@ -174,7 +176,7 @@ struct EnumRange; template <typename E, template <E> class F, typename T, T... Vs> struct EnumRange<E, F, std::integer_sequence<T, Vs...>> { - static constexpr auto kBegin = enum_cast(enum_begin_v<E>); + static constexpr auto kBegin = to_underlying(enum_begin_v<E>); static constexpr auto kSize = enum_size_v<E>; using R = decltype(F<E{}>::value); @@ -194,7 +196,7 @@ struct FlagName { using E = decltype(I); using U = std::underlying_type_t<E>; - static constexpr E V{U{1} << enum_cast(I)}; + static constexpr E V{U{1} << to_underlying(I)}; static constexpr auto value = ftl_enum<E, V>(); }; @@ -237,10 +239,10 @@ constexpr std::string_view enum_name() { // template <typename E> constexpr std::optional<std::string_view> enum_name(E v) { - const auto value = enum_cast(v); + const auto value = to_underlying(v); - constexpr auto kBegin = enum_cast(enum_begin_v<E>); - constexpr auto kLast = enum_cast(enum_last_v<E>); + constexpr auto kBegin = to_underlying(enum_begin_v<E>); + constexpr auto kLast = to_underlying(enum_last_v<E>); if (value < kBegin || value > kLast) return {}; constexpr auto kRange = details::EnumRange<E, details::EnumName>{}; @@ -256,7 +258,7 @@ constexpr std::optional<std::string_view> enum_name(E v) { // template <typename E> constexpr std::optional<std::string_view> flag_name(E v) { - const auto value = enum_cast(v); + const auto value = to_underlying(v); // TODO: Replace with std::popcount and std::countr_zero in C++20. if (__builtin_popcountl(value) != 1) return {}; @@ -277,7 +279,7 @@ inline std::string enum_string(E v) { if (const auto name = enum_name(v)) { return std::string(*name); } - return to_string(enum_cast(v)); + return to_string(to_underlying(v)); } // Returns a stringified flag enumerator, or its integral value if not named. @@ -293,7 +295,7 @@ inline std::string flag_string(E v) { return std::string(*name); } constexpr auto radix = sizeof(E) == 1 ? Radix::kBin : Radix::kHex; - return to_string(enum_cast(v), radix); + return to_string(to_underlying(v), radix); } } // namespace android::ftl diff --git a/include/ftl/small_vector.h b/include/ftl/small_vector.h index 65a953670d..03587e35ac 100644 --- a/include/ftl/small_vector.h +++ b/include/ftl/small_vector.h @@ -16,7 +16,7 @@ #pragma once -#include <ftl/array_traits.h> +#include <ftl/details/array_traits.h> #include <ftl/static_vector.h> #include <algorithm> @@ -73,7 +73,7 @@ struct is_small_vector; // assert(strings[2] == "???"); // template <typename T, std::size_t N> -class SmallVector final : ArrayTraits<T>, ArrayComparators<SmallVector> { +class SmallVector final : details::ArrayTraits<T>, details::ArrayComparators<SmallVector> { using Static = StaticVector<T, N>; using Dynamic = SmallVector<T, 0>; @@ -266,12 +266,12 @@ class SmallVector final : ArrayTraits<T>, ArrayComparators<SmallVector> { // Partial specialization without static storage. template <typename T> -class SmallVector<T, 0> final : ArrayTraits<T>, - ArrayIterators<SmallVector<T, 0>, T>, +class SmallVector<T, 0> final : details::ArrayTraits<T>, + details::ArrayIterators<SmallVector<T, 0>, T>, std::vector<T> { - using ArrayTraits<T>::construct_at; + using details::ArrayTraits<T>::construct_at; - using Iter = ArrayIterators<SmallVector, T>; + using Iter = details::ArrayIterators<SmallVector, T>; using Impl = std::vector<T>; friend Iter; diff --git a/include/ftl/static_vector.h b/include/ftl/static_vector.h index cd7b92a758..b7f8c29dec 100644 --- a/include/ftl/static_vector.h +++ b/include/ftl/static_vector.h @@ -16,7 +16,7 @@ #pragma once -#include <ftl/array_traits.h> +#include <ftl/details/array_traits.h> #include <ftl/initializer_list.h> #include <algorithm> @@ -73,14 +73,14 @@ constexpr struct IteratorRangeTag { // assert(strings[2] == "???"); // template <typename T, std::size_t N> -class StaticVector final : ArrayTraits<T>, - ArrayIterators<StaticVector<T, N>, T>, - ArrayComparators<StaticVector> { +class StaticVector final : details::ArrayTraits<T>, + details::ArrayIterators<StaticVector<T, N>, T>, + details::ArrayComparators<StaticVector> { static_assert(N > 0); - using ArrayTraits<T>::construct_at; + using details::ArrayTraits<T>::construct_at; - using Iter = ArrayIterators<StaticVector, T>; + using Iter = details::ArrayIterators<StaticVector, T>; friend Iter; // There is ambiguity when constructing from two iterator-like elements like pointers: diff --git a/include/input/Input.h b/include/input/Input.h index f4147a0409..e421dee275 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -164,7 +164,7 @@ enum { * (We want at least 10 but some touch controllers obstensibly configured for 10 pointers * will occasionally emit 11. There is not much harm making this constant bigger.) */ -#define MAX_POINTERS 16 +static constexpr size_t MAX_POINTERS = 16; /* * Maximum number of samples supported per motion event. @@ -534,7 +534,7 @@ public: inline int32_t getActionMasked() const { return getActionMasked(mAction); } - static int32_t getActionIndex(int32_t action) { + static uint8_t getActionIndex(int32_t action) { return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; } diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index 22aae196c6..c4f03c9119 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -235,7 +235,7 @@ public: void addBatteryInfo(const InputDeviceBatteryInfo& info); void addLightInfo(const InputDeviceLightInfo& info); - inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; } + void setKeyboardType(int32_t keyboardType); inline int32_t getKeyboardType() const { return mKeyboardType; } inline void setKeyCharacterMap(const std::shared_ptr<KeyCharacterMap> value) { diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index edcb615491..5f9a37d69c 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -500,24 +500,6 @@ public: status_t sendTimeline(int32_t inputEventId, std::array<nsecs_t, GraphicsTimeline::SIZE> timeline); - /* Returns true if there is a deferred event waiting. - * - * Should be called after calling consume() to determine whether the consumer - * has a deferred event to be processed. Deferred events are somewhat special in - * that they have already been removed from the input channel. If the input channel - * becomes empty, the client may need to do extra work to ensure that it processes - * the deferred event despite the fact that the input channel's file descriptor - * is not readable. - * - * One option is simply to call consume() in a loop until it returns WOULD_BLOCK. - * This guarantees that all deferred events will be processed. - * - * Alternately, the caller can call hasDeferredEvent() to determine whether there is - * a deferred event waiting and then ensure that its event loop wakes up at least - * one more time to consume the deferred event. - */ - bool hasDeferredEvent() const; - /* Returns true if there is a pending batch. * * Should be called after calling consume() with consumeBatches == false to determine diff --git a/include/private/performance_hint_private.h b/include/private/performance_hint_private.h index 5832bf49bd..f27f5f150f 100644 --- a/include/private/performance_hint_private.h +++ b/include/private/performance_hint_private.h @@ -17,124 +17,8 @@ #ifndef ANDROID_PRIVATE_NATIVE_PERFORMANCE_HINT_PRIVATE_H #define ANDROID_PRIVATE_NATIVE_PERFORMANCE_HINT_PRIVATE_H -#include <stdint.h> - __BEGIN_DECLS -struct APerformanceHintManager; -struct APerformanceHintSession; - -/** - * An opaque type representing a handle to a performance hint manager. - * It must be released after use. - * - * <p>To use:<ul> - * <li>Obtain the performance hint manager instance by calling - * {@link APerformanceHint_getManager} function.</li> - * <li>Create an {@link APerformanceHintSession} with - * {@link APerformanceHint_createSession}.</li> - * <li>Get the preferred update rate in nanoseconds with - * {@link APerformanceHint_getPreferredUpdateRateNanos}.</li> - */ -typedef struct APerformanceHintManager APerformanceHintManager; - -/** - * An opaque type representing a handle to a performance hint session. - * A session can only be acquired from a {@link APerformanceHintManager} - * with {@link APerformanceHint_getPreferredUpdateRateNanos}. It must be - * freed with {@link APerformanceHint_closeSession} after use. - * - * A Session represents a group of threads with an inter-related workload such that hints for - * their performance should be considered as a unit. The threads in a given session should be - * long-life and not created or destroyed dynamically. - * - * <p>Each session is expected to have a periodic workload with a target duration for each - * cycle. The cycle duration is likely greater than the target work duration to allow other - * parts of the pipeline to run within the available budget. For example, a renderer thread may - * work at 60hz in order to produce frames at the display's frame but have a target work - * duration of only 6ms.</p> - * - * <p>After each cycle of work, the client is expected to use - * {@link APerformanceHint_reportActualWorkDuration} to report the actual time taken to - * complete.</p> - * - * <p>To use:<ul> - * <li>Update a sessions target duration for each cycle of work - * with {@link APerformanceHint_updateTargetWorkDuration}.</li> - * <li>Report the actual duration for the last cycle of work with - * {@link APerformanceHint_reportActualWorkDuration}.</li> - * <li>Release the session instance with - * {@link APerformanceHint_closeSession}.</li></ul></p> - */ -typedef struct APerformanceHintSession APerformanceHintSession; - -/** - * Acquire an instance of the performance hint manager. - * - * @return manager instance on success, nullptr on failure. - */ -APerformanceHintManager* APerformanceHint_getManager(); - -/** - * Creates a session for the given set of threads and sets their initial target work - * duration. - * @param manager The performance hint manager instance. - * @param threadIds The list of threads to be associated with this session. They must be part of - * this app's thread group. - * @param size the size of threadIds. - * @param initialTargetWorkDurationNanos The desired duration in nanoseconds for the new session. - * This must be positive. - * @return manager instance on success, nullptr on failure. - */ -APerformanceHintSession* APerformanceHint_createSession(APerformanceHintManager* manager, - const int32_t* threadIds, size_t size, - int64_t initialTargetWorkDurationNanos); - -/** - * Get preferred update rate information for this device. - * - * @param manager The performance hint manager instance. - * @return the preferred update rate supported by device software. - */ -int64_t APerformanceHint_getPreferredUpdateRateNanos(APerformanceHintManager* manager); - -/** - * Updates this session's target duration for each cycle of work. - * - * @param session The performance hint session instance to update. - * @param targetDurationNanos the new desired duration in nanoseconds. This must be positive. - * @return 0 on success - * EINVAL if targetDurationNanos is not positive. - * EPIPE if communication with the system service has failed. - */ -int APerformanceHint_updateTargetWorkDuration(APerformanceHintSession* session, - int64_t targetDurationNanos); - -/** - * Reports the actual duration for the last cycle of work. - * - * <p>The system will attempt to adjust the core placement of the threads within the thread - * group and/or the frequency of the core on which they are run to bring the actual duration - * close to the target duration.</p> - * - * @param session The performance hint session instance to update. - * @param actualDurationNanos how long the thread group took to complete its last task in - * nanoseconds. This must be positive. - * @return 0 on success - * EINVAL if actualDurationNanos is not positive. - * EPIPE if communication with the system service has failed. - */ -int APerformanceHint_reportActualWorkDuration(APerformanceHintSession* session, - int64_t actualDurationNanos); - -/** - * Release the performance hint manager pointer acquired via - * {@link APerformanceHint_createSession}. - * - * @param session The performance hint session instance to release. - */ -void APerformanceHint_closeSession(APerformanceHintSession* session); - /** * For testing only. */ diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp index bb40f5146e..41b34605f7 100644 --- a/libs/arect/Android.bp +++ b/libs/arect/Android.bp @@ -57,4 +57,11 @@ cc_library_static { }, }, min_sdk_version: "29", + // static link, so it won't straddle a module boundary at runtime. + apex_available: [ + "//apex_available:platform", + "com.android.media", + "com.android.media.swcodec", + ], + } diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index 81e61daae1..ea2f8d2274 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -43,6 +43,8 @@ namespace android { +using AidlRegistrationCallback = IServiceManager::LocalRegistrationCallback; + using AidlServiceManager = android::os::IServiceManager; using android::binder::Status; @@ -79,7 +81,24 @@ public: Vector<String16> getDeclaredInstances(const String16& interface) override; std::optional<String16> updatableViaApex(const String16& name) override; std::optional<IServiceManager::ConnectionInfo> getConnectionInfo(const String16& name) override; + class RegistrationWaiter : public android::os::BnServiceCallback { + public: + explicit RegistrationWaiter(const sp<AidlRegistrationCallback>& callback) + : mImpl(callback) {} + Status onRegistration(const std::string& name, const sp<IBinder>& binder) override { + mImpl->onServiceRegistration(String16(name.c_str()), binder); + return Status::ok(); + } + + private: + sp<AidlRegistrationCallback> mImpl; + }; + + status_t registerForNotifications(const String16& service, + const sp<AidlRegistrationCallback>& cb) override; + status_t unregisterForNotifications(const String16& service, + const sp<AidlRegistrationCallback>& cb) override; // for legacy ABI const String16& getInterfaceDescriptor() const override { return mTheRealServiceManager->getInterfaceDescriptor(); @@ -90,6 +109,17 @@ public: protected: sp<AidlServiceManager> mTheRealServiceManager; + // AidlRegistrationCallback -> services that its been registered for + // notifications. + using LocalRegistrationAndWaiter = + std::pair<sp<LocalRegistrationCallback>, sp<RegistrationWaiter>>; + using ServiceCallbackMap = std::map<std::string, std::vector<LocalRegistrationAndWaiter>>; + ServiceCallbackMap mNameToRegistrationCallback; + std::mutex mNameToRegistrationLock; + + void removeRegistrationCallbackLocked(const sp<AidlRegistrationCallback>& cb, + ServiceCallbackMap::iterator* it, + sp<RegistrationWaiter>* waiter); // Directly get the service in a way that, for lazy services, requests the service to be started // if it is not currently started. This way, calls directly to ServiceManagerShim::getService @@ -442,6 +472,77 @@ std::optional<IServiceManager::ConnectionInfo> ServiceManagerShim::getConnection : std::nullopt; } +status_t ServiceManagerShim::registerForNotifications(const String16& name, + const sp<AidlRegistrationCallback>& cb) { + if (cb == nullptr) { + ALOGE("%s: null cb passed", __FUNCTION__); + return BAD_VALUE; + } + std::string nameStr = String8(name).c_str(); + sp<RegistrationWaiter> registrationWaiter = sp<RegistrationWaiter>::make(cb); + std::lock_guard<std::mutex> lock(mNameToRegistrationLock); + if (Status status = + mTheRealServiceManager->registerForNotifications(nameStr, registrationWaiter); + !status.isOk()) { + ALOGW("Failed to registerForNotifications for %s: %s", nameStr.c_str(), + status.toString8().c_str()); + return UNKNOWN_ERROR; + } + mNameToRegistrationCallback[nameStr].push_back(std::make_pair(cb, registrationWaiter)); + return OK; +} + +void ServiceManagerShim::removeRegistrationCallbackLocked(const sp<AidlRegistrationCallback>& cb, + ServiceCallbackMap::iterator* it, + sp<RegistrationWaiter>* waiter) { + std::vector<LocalRegistrationAndWaiter>& localRegistrationAndWaiters = (*it)->second; + for (auto lit = localRegistrationAndWaiters.begin(); + lit != localRegistrationAndWaiters.end();) { + if (lit->first == cb) { + if (waiter) { + *waiter = lit->second; + } + lit = localRegistrationAndWaiters.erase(lit); + } else { + ++lit; + } + } + + if (localRegistrationAndWaiters.empty()) { + mNameToRegistrationCallback.erase(*it); + } +} + +status_t ServiceManagerShim::unregisterForNotifications(const String16& name, + const sp<AidlRegistrationCallback>& cb) { + if (cb == nullptr) { + ALOGE("%s: null cb passed", __FUNCTION__); + return BAD_VALUE; + } + std::string nameStr = String8(name).c_str(); + std::lock_guard<std::mutex> lock(mNameToRegistrationLock); + auto it = mNameToRegistrationCallback.find(nameStr); + sp<RegistrationWaiter> registrationWaiter; + if (it != mNameToRegistrationCallback.end()) { + removeRegistrationCallbackLocked(cb, &it, ®istrationWaiter); + } else { + ALOGE("%s no callback registered for notifications on %s", __FUNCTION__, nameStr.c_str()); + return BAD_VALUE; + } + if (registrationWaiter == nullptr) { + ALOGE("%s Callback passed wasn't used to register for notifications", __FUNCTION__); + return BAD_VALUE; + } + if (Status status = mTheRealServiceManager->unregisterForNotifications(String8(name).c_str(), + registrationWaiter); + !status.isOk()) { + ALOGW("Failed to get service manager to unregisterForNotifications for %s: %s", + String8(name).c_str(), status.toString8().c_str()); + return UNKNOWN_ERROR; + } + return OK; +} + #ifndef __ANDROID__ // ServiceManagerShim for host. Implements the old libbinder android::IServiceManager API. // The internal implementation of the AIDL interface android::os::IServiceManager calls into diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp index 6286c9c7bc..4ddbce71a8 100644 --- a/libs/binder/RpcState.cpp +++ b/libs/binder/RpcState.cpp @@ -310,9 +310,9 @@ RpcState::CommandData::CommandData(size_t size) : mSize(size) { } status_t RpcState::rpcSend(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, const char* what, iovec* iovs, - size_t niovs, const std::function<status_t()>& altPoll) { - for (size_t i = 0; i < niovs; i++) { + const sp<RpcSession>& session, const char* what, iovec* iovs, int niovs, + const std::function<status_t()>& altPoll) { + for (int i = 0; i < niovs; i++) { LOG_RPC_DETAIL("Sending %s on RpcTransport %p: %s", what, connection->rpcTransport.get(), android::base::HexString(iovs[i].iov_base, iovs[i].iov_len).c_str()); } @@ -321,7 +321,7 @@ status_t RpcState::rpcSend(const sp<RpcSession::RpcConnection>& connection, connection->rpcTransport->interruptableWriteFully(session->mShutdownTrigger.get(), iovs, niovs, altPoll); status != OK) { - LOG_RPC_DETAIL("Failed to write %s (%zu iovs) on RpcTransport %p, error: %s", what, niovs, + LOG_RPC_DETAIL("Failed to write %s (%d iovs) on RpcTransport %p, error: %s", what, niovs, connection->rpcTransport.get(), statusToString(status).c_str()); (void)session->shutdownAndWait(false); return status; @@ -331,19 +331,18 @@ status_t RpcState::rpcSend(const sp<RpcSession::RpcConnection>& connection, } status_t RpcState::rpcRec(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, const char* what, iovec* iovs, - size_t niovs) { + const sp<RpcSession>& session, const char* what, iovec* iovs, int niovs) { if (status_t status = connection->rpcTransport->interruptableReadFully(session->mShutdownTrigger.get(), iovs, niovs, {}); status != OK) { - LOG_RPC_DETAIL("Failed to read %s (%zu iovs) on RpcTransport %p, error: %s", what, niovs, + LOG_RPC_DETAIL("Failed to read %s (%d iovs) on RpcTransport %p, error: %s", what, niovs, connection->rpcTransport.get(), statusToString(status).c_str()); (void)session->shutdownAndWait(false); return status; } - for (size_t i = 0; i < niovs; i++) { + for (int i = 0; i < niovs; i++) { LOG_RPC_DETAIL("Received %s on RpcTransport %p: %s", what, connection->rpcTransport.get(), android::base::HexString(iovs[i].iov_base, iovs[i].iov_len).c_str()); } diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h index 5cad394a2f..f4a08942b3 100644 --- a/libs/binder/RpcState.h +++ b/libs/binder/RpcState.h @@ -180,11 +180,10 @@ private: [[nodiscard]] status_t rpcSend(const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, const char* what, iovec* iovs, - size_t niovs, - const std::function<status_t()>& altPoll = nullptr); + int niovs, const std::function<status_t()>& altPoll = nullptr); [[nodiscard]] status_t rpcRec(const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, const char* what, iovec* iovs, - size_t niovs); + int niovs); [[nodiscard]] status_t waitForReply(const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, Parcel* reply); diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp index 2182e1868e..636e5d06e5 100644 --- a/libs/binder/RpcTransportRaw.cpp +++ b/libs/binder/RpcTransportRaw.cpp @@ -44,11 +44,15 @@ public: } template <typename SendOrReceive> - status_t interruptableReadOrWrite(FdTrigger* fdTrigger, iovec* iovs, size_t niovs, + status_t interruptableReadOrWrite(FdTrigger* fdTrigger, iovec* iovs, int niovs, SendOrReceive sendOrReceiveFun, const char* funName, int16_t event, const std::function<status_t()>& altPoll) { MAYBE_WAIT_IN_FLAKE_MODE; + if (niovs < 0) { + return BAD_VALUE; + } + // Since we didn't poll, we need to manually check to see if it was triggered. Otherwise, we // may never know we should be shutting down. if (fdTrigger->isTriggered()) { @@ -74,7 +78,9 @@ public: while (true) { msghdr msg{ .msg_iov = iovs, - .msg_iovlen = niovs, + // posix uses int, glibc uses size_t. niovs is a + // non-negative int and can be cast to either. + .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs), }; ssize_t processSize = TEMP_FAILURE_RETRY(sendOrReceiveFun(mSocket.get(), &msg, MSG_NOSIGNAL)); @@ -128,13 +134,13 @@ public: } } - status_t interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, size_t niovs, + status_t interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, int niovs, const std::function<status_t()>& altPoll) override { return interruptableReadOrWrite(fdTrigger, iovs, niovs, sendmsg, "sendmsg", POLLOUT, altPoll); } - status_t interruptableReadFully(FdTrigger* fdTrigger, iovec* iovs, size_t niovs, + status_t interruptableReadFully(FdTrigger* fdTrigger, iovec* iovs, int niovs, const std::function<status_t()>& altPoll) override { return interruptableReadOrWrite(fdTrigger, iovs, niovs, recvmsg, "recvmsg", POLLIN, altPoll); diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp index c05ea1512f..3936204bdf 100644 --- a/libs/binder/RpcTransportTls.cpp +++ b/libs/binder/RpcTransportTls.cpp @@ -275,9 +275,9 @@ public: RpcTransportTls(android::base::unique_fd socket, Ssl ssl) : mSocket(std::move(socket)), mSsl(std::move(ssl)) {} Result<size_t> peek(void* buf, size_t size) override; - status_t interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, size_t niovs, + status_t interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, int niovs, const std::function<status_t()>& altPoll) override; - status_t interruptableReadFully(FdTrigger* fdTrigger, iovec* iovs, size_t niovs, + status_t interruptableReadFully(FdTrigger* fdTrigger, iovec* iovs, int niovs, const std::function<status_t()>& altPoll) override; private: @@ -303,16 +303,18 @@ Result<size_t> RpcTransportTls::peek(void* buf, size_t size) { return ret; } -status_t RpcTransportTls::interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, size_t niovs, +status_t RpcTransportTls::interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, int niovs, const std::function<status_t()>& altPoll) { MAYBE_WAIT_IN_FLAKE_MODE; + if (niovs < 0) return BAD_VALUE; + // Before doing any I/O, check trigger once. This ensures the trigger is checked at least // once. The trigger is also checked via triggerablePoll() after every SSL_write(). if (fdTrigger->isTriggered()) return DEAD_OBJECT; size_t size = 0; - for (size_t i = 0; i < niovs; i++) { + for (int i = 0; i < niovs; i++) { const iovec& iov = iovs[i]; if (iov.iov_len == 0) { continue; @@ -343,16 +345,18 @@ status_t RpcTransportTls::interruptableWriteFully(FdTrigger* fdTrigger, iovec* i return OK; } -status_t RpcTransportTls::interruptableReadFully(FdTrigger* fdTrigger, iovec* iovs, size_t niovs, +status_t RpcTransportTls::interruptableReadFully(FdTrigger* fdTrigger, iovec* iovs, int niovs, const std::function<status_t()>& altPoll) { MAYBE_WAIT_IN_FLAKE_MODE; + if (niovs < 0) return BAD_VALUE; + // Before doing any I/O, check trigger once. This ensures the trigger is checked at least // once. The trigger is also checked via triggerablePoll() after every SSL_write(). if (fdTrigger->isTriggered()) return DEAD_OBJECT; size_t size = 0; - for (size_t i = 0; i < niovs; i++) { + for (int i = 0; i < niovs; i++) { const iovec& iov = iovs[i]; if (iov.iov_len == 0) { continue; diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h index 240e3c2b26..ea40db8ffa 100644 --- a/libs/binder/include/binder/IServiceManager.h +++ b/libs/binder/include/binder/IServiceManager.h @@ -115,6 +115,17 @@ public: unsigned int port; }; virtual std::optional<ConnectionInfo> getConnectionInfo(const String16& name) = 0; + + struct LocalRegistrationCallback : public virtual RefBase { + virtual void onServiceRegistration(const String16& instance, const sp<IBinder>& binder) = 0; + virtual ~LocalRegistrationCallback() {} + }; + + virtual status_t registerForNotifications(const String16& name, + const sp<LocalRegistrationCallback>& callback) = 0; + + virtual status_t unregisterForNotifications(const String16& name, + const sp<LocalRegistrationCallback>& callback) = 0; }; sp<IServiceManager> defaultServiceManager(); diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h index 348bfebf15..ade2d94609 100644 --- a/libs/binder/include/binder/RpcTransport.h +++ b/libs/binder/include/binder/RpcTransport.h @@ -58,10 +58,10 @@ public: * error - interrupted (failure or trigger) */ [[nodiscard]] virtual status_t interruptableWriteFully( - FdTrigger *fdTrigger, iovec *iovs, size_t niovs, + FdTrigger *fdTrigger, iovec *iovs, int niovs, const std::function<status_t()> &altPoll) = 0; [[nodiscard]] virtual status_t interruptableReadFully( - FdTrigger *fdTrigger, iovec *iovs, size_t niovs, + FdTrigger *fdTrigger, iovec *iovs, int niovs, const std::function<status_t()> &altPoll) = 0; protected: diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp index 90cbf9d45d..355b3b4b89 100644 --- a/libs/binder/rust/Android.bp +++ b/libs/binder/rust/Android.bp @@ -44,6 +44,7 @@ rust_library { "libtokio", ], host_supported: true, + vendor_available: true, target: { darwin: { enabled: false, @@ -52,8 +53,10 @@ rust_library { apex_available: [ "//apex_available:platform", "com.android.compos", + "com.android.uwb", "com.android.virt", ], + min_sdk_version: "Tiramisu", } rust_library { diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs index 7895a7293d..467e51e276 100644 --- a/libs/binder/rust/src/binder.rs +++ b/libs/binder/rust/src/binder.rs @@ -192,9 +192,6 @@ pub trait IBinderInternal: IBinder { /// Is this object still alive? fn is_binder_alive(&self) -> bool; - /// Send a ping transaction to this object - fn ping_binder(&mut self) -> Result<()>; - /// Indicate that the service intends to receive caller security contexts. #[cfg(not(android_vndk))] fn set_requesting_sid(&mut self, enable: bool); @@ -270,6 +267,9 @@ pub trait IBinder { /// The recipient will no longer be called if this object /// dies. fn unlink_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()>; + + /// Send a ping transaction to this object + fn ping_binder(&mut self) -> Result<()>; } /// Opaque reference to the type of a Binder interface. diff --git a/libs/binder/rust/src/parcel/parcelable_holder.rs b/libs/binder/rust/src/parcel/parcelable_holder.rs index bc70ea6c30..d58e839ad4 100644 --- a/libs/binder/rust/src/parcel/parcelable_holder.rs +++ b/libs/binder/rust/src/parcel/parcelable_holder.rs @@ -16,8 +16,10 @@ use crate::binder::Stability; use crate::error::StatusCode; -use crate::parcel::{BorrowedParcel, Parcel, Parcelable}; -use crate::{impl_deserialize_for_parcelable, impl_serialize_for_parcelable}; +use crate::parcel::{ + BorrowedParcel, Deserialize, Parcel, Parcelable, Serialize, NON_NULL_PARCELABLE_FLAG, + NULL_PARCELABLE_FLAG, +}; use downcast_rs::{impl_downcast, DowncastSync}; use std::any::Any; @@ -53,12 +55,6 @@ enum ParcelableHolderData { Parcel(Parcel), } -impl Default for ParcelableHolderData { - fn default() -> Self { - ParcelableHolderData::Empty - } -} - /// A container that can hold any arbitrary `Parcelable`. /// /// This type is currently used for AIDL parcelable fields. @@ -66,7 +62,7 @@ impl Default for ParcelableHolderData { /// `ParcelableHolder` is currently not thread-safe (neither /// `Send` nor `Sync`), mainly because it internally contains /// a `Parcel` which in turn is not thread-safe. -#[derive(Debug, Default)] +#[derive(Debug)] pub struct ParcelableHolder { // This is a `Mutex` because of `get_parcelable` // which takes `&self` for consistency with C++. @@ -176,8 +172,25 @@ impl ParcelableHolder { } } -impl_serialize_for_parcelable!(ParcelableHolder); -impl_deserialize_for_parcelable!(ParcelableHolder); +impl Serialize for ParcelableHolder { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<(), StatusCode> { + parcel.write(&NON_NULL_PARCELABLE_FLAG)?; + self.write_to_parcel(parcel) + } +} + +impl Deserialize for ParcelableHolder { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self, StatusCode> { + let status: i32 = parcel.read()?; + if status == NULL_PARCELABLE_FLAG { + Err(StatusCode::UNEXPECTED_NULL) + } else { + let mut parcelable = ParcelableHolder::new(Default::default()); + parcelable.read_from_parcel(parcel)?; + Ok(parcelable) + } + } +} impl Parcelable for ParcelableHolder { fn write_to_parcel(&self, parcel: &mut BorrowedParcel<'_>) -> Result<(), StatusCode> { diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs index 760d862c53..12bfde755e 100644 --- a/libs/binder/rust/src/proxy.rs +++ b/libs/binder/rust/src/proxy.rs @@ -312,17 +312,6 @@ impl<T: AsNative<sys::AIBinder>> IBinderInternal for T { } } - fn ping_binder(&mut self) -> Result<()> { - let status = unsafe { - // Safety: `SpIBinder` guarantees that `self` always contains a - // valid pointer to an `AIBinder`. - // - // This call does not affect ownership of its pointer parameter. - sys::AIBinder_ping(self.as_native_mut()) - }; - status_result(status) - } - #[cfg(not(android_vndk))] fn set_requesting_sid(&mut self, enable: bool) { unsafe { sys::AIBinder_setRequestingSid(self.as_native_mut(), enable) }; @@ -412,6 +401,17 @@ impl<T: AsNative<sys::AIBinder>> IBinder for T { ) }) } + + fn ping_binder(&mut self) -> Result<()> { + let status = unsafe { + // Safety: `SpIBinder` guarantees that `self` always contains a + // valid pointer to an `AIBinder`. + // + // This call does not affect ownership of its pointer parameter. + sys::AIBinder_ping(self.as_native_mut()) + }; + status_result(status) + } } impl Serialize for SpIBinder { diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index 63a4b2cc68..700940a529 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -1220,6 +1220,19 @@ TEST_F(BinderLibTest, GotSid) { EXPECT_THAT(server->transact(BINDER_LIB_TEST_CAN_GET_SID, data, nullptr), StatusEq(OK)); } +TEST(ServiceNotifications, Unregister) { + auto sm = defaultServiceManager(); + using LocalRegistrationCallback = IServiceManager::LocalRegistrationCallback; + class LocalRegistrationCallbackImpl : public virtual LocalRegistrationCallback { + void onServiceRegistration(const String16 &, const sp<IBinder> &) override {} + virtual ~LocalRegistrationCallbackImpl() {} + }; + sp<LocalRegistrationCallback> cb = sp<LocalRegistrationCallbackImpl>::make(); + + EXPECT_EQ(sm->registerForNotifications(String16("RogerRafa"), cb), OK); + EXPECT_EQ(sm->unregisterForNotifications(String16("RogerRafa"), cb), OK); +} + class BinderLibRpcTestBase : public BinderLibTest { public: void SetUp() override { diff --git a/libs/binder/tests/rpc_fuzzer/corpus/transact_on_binder b/libs/binder/tests/rpc_fuzzer/corpus/transact_on_binder Binary files differnew file mode 100644 index 0000000000..ae081e659f --- /dev/null +++ b/libs/binder/tests/rpc_fuzzer/corpus/transact_on_binder diff --git a/libs/binderthreadstate/Android.bp b/libs/binderthreadstate/Android.bp index 0a82463feb..48606136eb 100644 --- a/libs/binderthreadstate/Android.bp +++ b/libs/binderthreadstate/Android.bp @@ -31,7 +31,7 @@ cc_library_static { target: { darwin: { enabled: false, - } + }, }, shared_libs: [ @@ -46,6 +46,11 @@ cc_library_static { "-Werror", ], min_sdk_version: "29", + // static link, so it won't straddle a module boundary at runtime. + apex_available: [ + "//apex_available:platform", + "com.android.media.swcodec", + ], } hidl_package_root { diff --git a/libs/cputimeinstate/Android.bp b/libs/cputimeinstate/Android.bp index 1fd2c62772..4f63194f03 100644 --- a/libs/cputimeinstate/Android.bp +++ b/libs/cputimeinstate/Android.bp @@ -14,6 +14,7 @@ cc_library { "libbase", "libbpf_bcc", "libbpf_android", + "libbpf_minimal", "liblog", "libnetdutils" ], @@ -33,6 +34,7 @@ cc_test { "libbase", "libbpf_bcc", "libbpf_android", + "libbpf_minimal", "libtimeinstate", "libnetdutils", ], @@ -43,4 +45,5 @@ cc_test { "-Wextra", ], require_root: true, + test_suites: ["general-tests"], } diff --git a/libs/cputimeinstate/TEST_MAPPING b/libs/cputimeinstate/TEST_MAPPING new file mode 100644 index 0000000000..4781520755 --- /dev/null +++ b/libs/cputimeinstate/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "libtimeinstate_test" + } + ] +} diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp index 2112b10ebc..1513ecafc8 100644 --- a/libs/cputimeinstate/testtimeinstate.cpp +++ b/libs/cputimeinstate/testtimeinstate.cpp @@ -27,6 +27,7 @@ #include <gtest/gtest.h> +#include <android-base/properties.h> #include <android-base/unique_fd.h> #include <bpf/BpfMap.h> #include <cputimeinstate.h> @@ -40,24 +41,31 @@ static constexpr uint64_t NSEC_PER_YEAR = NSEC_PER_SEC * 60 * 60 * 24 * 365; using std::vector; -TEST(TimeInStateTest, IsTrackingSupported) { - isTrackingUidTimesSupported(); - SUCCEED(); -} +class TimeInStateTest : public testing::Test { + protected: + TimeInStateTest() {}; + + void SetUp() { + if (!isTrackingUidTimesSupported() || + !android::base::GetBoolProperty("sys.init.perf_lsm_hooks", false)) { + GTEST_SKIP(); + } + } +}; -TEST(TimeInStateTest, TotalTimeInState) { +TEST_F(TimeInStateTest, TotalTimeInState) { auto times = getTotalCpuFreqTimes(); ASSERT_TRUE(times.has_value()); EXPECT_FALSE(times->empty()); } -TEST(TimeInStateTest, SingleUidTimeInState) { +TEST_F(TimeInStateTest, SingleUidTimeInState) { auto times = getUidCpuFreqTimes(0); ASSERT_TRUE(times.has_value()); EXPECT_FALSE(times->empty()); } -TEST(TimeInStateTest, SingleUidConcurrentTimes) { +TEST_F(TimeInStateTest, SingleUidConcurrentTimes) { auto concurrentTimes = getUidConcurrentTimes(0); ASSERT_TRUE(concurrentTimes.has_value()); ASSERT_FALSE(concurrentTimes->active.empty()); @@ -117,7 +125,7 @@ static void TestUidTimesConsistent(const std::vector<std::vector<uint64_t>> &tim EXPECT_EQ(activeSum, policySum); } -TEST(TimeInStateTest, SingleUidTimesConsistent) { +TEST_F(TimeInStateTest, SingleUidTimesConsistent) { auto times = getUidCpuFreqTimes(0); ASSERT_TRUE(times.has_value()); @@ -127,7 +135,7 @@ TEST(TimeInStateTest, SingleUidTimesConsistent) { ASSERT_NO_FATAL_FAILURE(TestUidTimesConsistent(*times, *concurrentTimes)); } -TEST(TimeInStateTest, AllUidTimeInState) { +TEST_F(TimeInStateTest, AllUidTimeInState) { uint64_t zero = 0; auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)}; for (const auto &map : maps) { @@ -163,7 +171,7 @@ void TestCheckUpdate(const std::vector<std::vector<uint64_t>> &before, ASSERT_LE(sumAfter - sumBefore, NSEC_PER_SEC); } -TEST(TimeInStateTest, AllUidUpdatedTimeInState) { +TEST_F(TimeInStateTest, AllUidUpdatedTimeInState) { uint64_t lastUpdate = 0; auto map1 = getUidsUpdatedCpuFreqTimes(&lastUpdate); ASSERT_TRUE(map1.has_value()); @@ -197,7 +205,7 @@ TEST(TimeInStateTest, AllUidUpdatedTimeInState) { } } -TEST(TimeInStateTest, TotalAndAllUidTimeInStateConsistent) { +TEST_F(TimeInStateTest, TotalAndAllUidTimeInStateConsistent) { auto allUid = getUidsCpuFreqTimes(); auto total = getTotalCpuFreqTimes(); @@ -222,7 +230,7 @@ TEST(TimeInStateTest, TotalAndAllUidTimeInStateConsistent) { } } -TEST(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) { +TEST_F(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) { uint64_t zero = 0; auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)}; for (const auto &map : maps) { @@ -246,7 +254,7 @@ TEST(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) { } } -TEST(TimeInStateTest, AllUidConcurrentTimes) { +TEST_F(TimeInStateTest, AllUidConcurrentTimes) { uint64_t zero = 0; auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)}; for (const auto &map : maps) { @@ -264,7 +272,7 @@ TEST(TimeInStateTest, AllUidConcurrentTimes) { } } -TEST(TimeInStateTest, AllUidUpdatedConcurrentTimes) { +TEST_F(TimeInStateTest, AllUidUpdatedConcurrentTimes) { uint64_t lastUpdate = 0; auto map1 = getUidsUpdatedConcurrentTimes(&lastUpdate); ASSERT_TRUE(map1.has_value()); @@ -299,7 +307,7 @@ TEST(TimeInStateTest, AllUidUpdatedConcurrentTimes) { } } -TEST(TimeInStateTest, SingleAndAllUidConcurrentTimesConsistent) { +TEST_F(TimeInStateTest, SingleAndAllUidConcurrentTimesConsistent) { uint64_t zero = 0; auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)}; for (const auto &map : maps) { @@ -328,7 +336,7 @@ void TestCheckDelta(uint64_t before, uint64_t after) { ASSERT_LE(after - before, NSEC_PER_SEC * 2 * get_nprocs_conf()); } -TEST(TimeInStateTest, TotalTimeInStateMonotonic) { +TEST_F(TimeInStateTest, TotalTimeInStateMonotonic) { auto before = getTotalCpuFreqTimes(); ASSERT_TRUE(before.has_value()); sleep(1); @@ -344,7 +352,7 @@ TEST(TimeInStateTest, TotalTimeInStateMonotonic) { } } -TEST(TimeInStateTest, AllUidTimeInStateMonotonic) { +TEST_F(TimeInStateTest, AllUidTimeInStateMonotonic) { auto map1 = getUidsCpuFreqTimes(); ASSERT_TRUE(map1.has_value()); sleep(1); @@ -365,7 +373,7 @@ TEST(TimeInStateTest, AllUidTimeInStateMonotonic) { } } -TEST(TimeInStateTest, AllUidConcurrentTimesMonotonic) { +TEST_F(TimeInStateTest, AllUidConcurrentTimesMonotonic) { auto map1 = getUidsConcurrentTimes(); ASSERT_TRUE(map1.has_value()); ASSERT_FALSE(map1->empty()); @@ -393,7 +401,7 @@ TEST(TimeInStateTest, AllUidConcurrentTimesMonotonic) { } } -TEST(TimeInStateTest, AllUidTimeInStateSanityCheck) { +TEST_F(TimeInStateTest, AllUidTimeInStateSanityCheck) { uint64_t zero = 0; auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)}; for (const auto &map : maps) { @@ -414,7 +422,7 @@ TEST(TimeInStateTest, AllUidTimeInStateSanityCheck) { } } -TEST(TimeInStateTest, AllUidConcurrentTimesSanityCheck) { +TEST_F(TimeInStateTest, AllUidConcurrentTimesSanityCheck) { uint64_t zero = 0; auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)}; for (const auto &concurrentMap : maps) { @@ -441,7 +449,7 @@ TEST(TimeInStateTest, AllUidConcurrentTimesSanityCheck) { } } -TEST(TimeInStateTest, AllUidConcurrentTimesFailsOnInvalidBucket) { +TEST_F(TimeInStateTest, AllUidConcurrentTimesFailsOnInvalidBucket) { uint32_t uid = 0; { // Find an unused UID @@ -463,7 +471,7 @@ TEST(TimeInStateTest, AllUidConcurrentTimesFailsOnInvalidBucket) { ASSERT_FALSE(deleteMapEntry(fd, &key)); } -TEST(TimeInStateTest, AllUidTimesConsistent) { +TEST_F(TimeInStateTest, AllUidTimesConsistent) { auto tisMap = getUidsCpuFreqTimes(); ASSERT_TRUE(tisMap.has_value()); @@ -481,7 +489,7 @@ TEST(TimeInStateTest, AllUidTimesConsistent) { } } -TEST(TimeInStateTest, RemoveUid) { +TEST_F(TimeInStateTest, RemoveUid) { uint32_t uid = 0; { // Find an unused UID @@ -547,7 +555,7 @@ TEST(TimeInStateTest, RemoveUid) { ASSERT_EQ(allConcurrentTimes->find(uid), allConcurrentTimes->end()); } -TEST(TimeInStateTest, GetCpuFreqs) { +TEST_F(TimeInStateTest, GetCpuFreqs) { auto freqs = getCpuFreqs(); ASSERT_TRUE(freqs.has_value()); @@ -583,7 +591,7 @@ void *testThread(void *) { return nullptr; } -TEST(TimeInStateTest, GetAggregatedTaskCpuFreqTimes) { +TEST_F(TimeInStateTest, GetAggregatedTaskCpuFreqTimes) { uint64_t startTimeNs = timeNanos(); sem_init(&pingsem, 0, 1); diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/ServiceManager.cpp index 9f0754b891..61e4a98846 100644 --- a/libs/fakeservicemanager/ServiceManager.cpp +++ b/libs/fakeservicemanager/ServiceManager.cpp @@ -84,4 +84,14 @@ std::optional<IServiceManager::ConnectionInfo> ServiceManager::getConnectionInfo return std::nullopt; } +status_t ServiceManager::registerForNotifications(const String16&, + const sp<LocalRegistrationCallback>&) { + return INVALID_OPERATION; +} + +status_t ServiceManager::unregisterForNotifications(const String16&, + const sp<LocalRegistrationCallback>&) { + return INVALID_OPERATION; +} + } // namespace android diff --git a/libs/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/ServiceManager.h index b1496ba50b..6d6e008c4f 100644 --- a/libs/fakeservicemanager/ServiceManager.h +++ b/libs/fakeservicemanager/ServiceManager.h @@ -53,6 +53,11 @@ public: std::optional<IServiceManager::ConnectionInfo> getConnectionInfo(const String16& name) override; + status_t registerForNotifications(const String16& name, + const sp<LocalRegistrationCallback>& callback) override; + + status_t unregisterForNotifications(const String16& name, + const sp<LocalRegistrationCallback>& callback) override; private: std::map<String16, sp<IBinder>> mNameToService; }; diff --git a/libs/ftl/enum_test.cpp b/libs/ftl/enum_test.cpp index 1fd43abbc6..d8ce7a5e7b 100644 --- a/libs/ftl/enum_test.cpp +++ b/libs/ftl/enum_test.cpp @@ -72,7 +72,7 @@ enum class Planet : std::uint8_t { kNeptune }; -constexpr Planet kPluto{ftl::enum_cast(Planet::kNeptune) + 1}; // Honorable mention. +constexpr Planet kPluto{ftl::to_underlying(Planet::kNeptune) + 1}; // Honorable mention. static_assert(ftl::enum_begin_v<Planet> == Planet::kMercury); static_assert(ftl::enum_last_v<Planet> == Planet::kNeptune); diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index b7594df2fb..fb9ed22a33 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -495,27 +495,6 @@ public: return result; } - status_t getPreferredBootDisplayMode(const sp<IBinder>& display, - ui::DisplayModeId* displayModeId) override { - Parcel data, reply; - status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - if (result != NO_ERROR) { - ALOGE("getPreferredBootDisplayMode failed to writeInterfaceToken: %d", result); - return result; - } - result = data.writeStrongBinder(display); - if (result != NO_ERROR) { - ALOGE("getPreferredBootDisplayMode failed to writeStrongBinder: %d", result); - return result; - } - result = remote()->transact(BnSurfaceComposer::GET_PREFERRED_BOOT_DISPLAY_MODE, data, - &reply); - if (result == NO_ERROR) { - reply.writeInt32(*displayModeId); - } - return result; - } - void setAutoLowLatencyMode(const sp<IBinder>& display, bool on) override { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); @@ -1659,21 +1638,6 @@ status_t BnSurfaceComposer::onTransact( } return clearBootDisplayMode(display); } - case GET_PREFERRED_BOOT_DISPLAY_MODE: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IBinder> display = nullptr; - status_t result = data.readStrongBinder(&display); - if (result != NO_ERROR) { - ALOGE("getPreferredBootDisplayMode failed to readStrongBinder: %d", result); - return result; - } - ui::DisplayModeId displayModeId; - result = getPreferredBootDisplayMode(display, &displayModeId); - if (result == NO_ERROR) { - reply->writeInt32(displayModeId); - } - return result; - } case SET_AUTO_LOW_LATENCY_MODE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> display = nullptr; diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 27d86bb4e2..eec4a874b9 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -340,6 +340,79 @@ void DisplayState::merge(const DisplayState& other) { } } +void layer_state_t::sanitize(int32_t permissions) { + // TODO: b/109894387 + // + // SurfaceFlinger's renderer is not prepared to handle cropping in the face of arbitrary + // rotation. To see the problem observe that if we have a square parent, and a child + // of the same size, then we rotate the child 45 degrees around its center, the child + // must now be cropped to a non rectangular 8 sided region. + // + // Of course we can fix this in the future. For now, we are lucky, SurfaceControl is + // private API, and arbitrary rotation is used in limited use cases, for instance: + // - WindowManager only uses rotation in one case, which is on a top level layer in which + // cropping is not an issue. + // - Launcher, as a privileged app, uses this to transition an application to PiP + // (picture-in-picture) mode. + // + // However given that abuse of rotation matrices could lead to surfaces extending outside + // of cropped areas, we need to prevent non-root clients without permission + // ACCESS_SURFACE_FLINGER nor ROTATE_SURFACE_FLINGER + // (a.k.a. everyone except WindowManager / tests / Launcher) from setting non rectangle + // preserving transformations. + if (what & eMatrixChanged) { + if (!(permissions & Permission::ROTATE_SURFACE_FLINGER)) { + ui::Transform t; + t.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy); + if (!t.preserveRects()) { + what &= ~eMatrixChanged; + ALOGE("Stripped non rect preserving matrix in sanitize"); + } + } + } + + if (what & eFlagsChanged) { + if ((flags & eLayerIsDisplayDecoration) && + !(permissions & Permission::INTERNAL_SYSTEM_WINDOW)) { + flags &= ~eLayerIsDisplayDecoration; + ALOGE("Stripped attempt to set LayerIsDisplayDecoration in sanitize"); + } + } + + if (what & layer_state_t::eInputInfoChanged) { + if (!(permissions & Permission::ACCESS_SURFACE_FLINGER)) { + what &= ~eInputInfoChanged; + ALOGE("Stripped attempt to set eInputInfoChanged in sanitize"); + } + } + if (what & layer_state_t::eTrustedOverlayChanged) { + if (!(permissions & Permission::ACCESS_SURFACE_FLINGER)) { + what &= ~eTrustedOverlayChanged; + ALOGE("Stripped attempt to set eTrustedOverlay in sanitize"); + } + } + if (what & layer_state_t::eDropInputModeChanged) { + if (!(permissions & Permission::ACCESS_SURFACE_FLINGER)) { + what &= ~eDropInputModeChanged; + ALOGE("Stripped attempt to set eDropInputModeChanged in sanitize"); + } + } + if (what & layer_state_t::eFrameRateSelectionPriority) { + if (!(permissions & Permission::ACCESS_SURFACE_FLINGER)) { + what &= ~eFrameRateSelectionPriority; + ALOGE("Stripped attempt to set eFrameRateSelectionPriority in sanitize"); + } + } + if (what & layer_state_t::eFrameRateChanged) { + if (!ValidateFrameRate(frameRate, frameRateCompatibility, + changeFrameRateStrategy, + "layer_state_t::sanitize", + permissions & Permission::ACCESS_SURFACE_FLINGER)) { + what &= ~eFrameRateChanged; // logged in ValidateFrameRate + } + } +} + void layer_state_t::merge(const layer_state_t& other) { if (other.what & ePositionChanged) { what |= ePositionChanged; diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 31456cda7e..91b2fb1c3b 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -565,6 +565,13 @@ SurfaceComposerClient::Transaction::Transaction(const Transaction& other) mListenerCallbacks = other.mListenerCallbacks; } +void SurfaceComposerClient::Transaction::sanitize() { + for (auto & [handle, composerState] : mComposerStates) { + composerState.state.sanitize(0 /* permissionMask */); + } + mInputWindowCommands.clear(); +} + std::unique_ptr<SurfaceComposerClient::Transaction> SurfaceComposerClient::Transaction::createFromParcel(const Parcel* parcel) { auto transaction = std::make_unique<Transaction>(); @@ -646,7 +653,6 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel if (composerState.read(*parcel) == BAD_VALUE) { return BAD_VALUE; } - composerStates[surfaceControlHandle] = composerState; } @@ -2088,12 +2094,6 @@ status_t SurfaceComposerClient::clearBootDisplayMode(const sp<IBinder>& display) return ComposerService::getComposerService()->clearBootDisplayMode(display); } -status_t SurfaceComposerClient::getPreferredBootDisplayMode(const sp<IBinder>& display, - ui::DisplayModeId* displayModeId) { - return ComposerService::getComposerService()->getPreferredBootDisplayMode(display, - displayModeId); -} - status_t SurfaceComposerClient::setOverrideFrameRate(uid_t uid, float frameRate) { return ComposerService::getComposerService()->setOverrideFrameRate(uid, frameRate); } diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp index 8d356aaaa1..1c7b270527 100644 --- a/libs/gui/WindowInfo.cpp +++ b/libs/gui/WindowInfo.cpp @@ -52,7 +52,8 @@ bool WindowInfo::interceptsStylus() const { } bool WindowInfo::overlaps(const WindowInfo* other) const { - return frameLeft < other->frameRight && frameRight > other->frameLeft && + const bool nonEmpty = (frameRight - frameLeft > 0) || (frameBottom - frameTop > 0); + return nonEmpty && frameLeft < other->frameRight && frameRight > other->frameLeft && frameTop < other->frameBottom && frameBottom > other->frameTop; } diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index fb4fb7e2da..4b5cee256a 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -235,12 +235,6 @@ public: virtual status_t clearBootDisplayMode(const sp<IBinder>& display) = 0; /** - * Gets the display mode in which the device boots if there is no user-preferred display mode. - */ - virtual status_t getPreferredBootDisplayMode(const sp<IBinder>& display, - ui::DisplayModeId*) = 0; - - /** * Gets whether boot time display mode operations are supported on the device. * * outSupport @@ -684,7 +678,6 @@ public: GET_BOOT_DISPLAY_MODE_SUPPORT, SET_BOOT_DISPLAY_MODE, CLEAR_BOOT_DISPLAY_MODE, - GET_PREFERRED_BOOT_DISPLAY_MODE, SET_OVERRIDE_FRAME_RATE, // Always append new enum to the end. }; diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 968ace93b9..cd6afd29a0 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -113,6 +113,12 @@ public: * Used to communicate layer information between SurfaceFlinger and its clients. */ struct layer_state_t { + enum Permission { + ACCESS_SURFACE_FLINGER = 0x1, + ROTATE_SURFACE_FLINGER = 0x2, + INTERNAL_SYSTEM_WINDOW = 0x4, + }; + enum { eLayerHidden = 0x01, // SURFACE_HIDDEN in SurfaceControl.java eLayerOpaque = 0x02, // SURFACE_OPAQUE @@ -136,7 +142,7 @@ struct layer_state_t { eLayerStackChanged = 0x00000080, /* unused 0x00000400, */ eShadowRadiusChanged = 0x00000800, - eLayerCreated = 0x00001000, + /* unused 0x00001000, */ eBufferCropChanged = 0x00002000, eRelativeLayerChanged = 0x00004000, eReparent = 0x00008000, @@ -181,6 +187,7 @@ struct layer_state_t { status_t read(const Parcel& input); bool hasBufferChanges() const; bool hasValidBuffer() const; + void sanitize(int32_t permissions); struct matrix22_t { float dsdx{0}; diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 4f928781d9..61eeab35e5 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -174,8 +174,7 @@ public: static status_t setBootDisplayMode(const sp<IBinder>& display, ui::DisplayModeId); // Clears the user-preferred display mode static status_t clearBootDisplayMode(const sp<IBinder>& display); - // Gets the display mode in which the device boots if there is no user-preferred display mode - static status_t getPreferredBootDisplayMode(const sp<IBinder>& display, ui::DisplayModeId*); + // Sets the frame rate of a particular app (uid). This is currently called // by GameManager. static status_t setOverrideFrameRate(uid_t uid, float frameRate); @@ -624,6 +623,14 @@ public: void setAnimationTransaction(); void setEarlyWakeupStart(); void setEarlyWakeupEnd(); + + /** + * Strip the transaction of all permissioned requests, required when + * accepting transactions across process boundaries. + * + * TODO (b/213644870): Remove all permissioned things from Transaction + */ + void sanitize(); }; status_t clearLayerFrameStats(const sp<IBinder>& token) const; diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index 6f1263bb89..06a0acae49 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -546,7 +546,10 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets) { } TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) { + std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); + bgSurface->showAt(100, 100); + // In case we pass the very big inset without any checking. fgSurface->mInputInfo.surfaceInset = INT32_MAX; fgSurface->showAt(100, 100); @@ -554,8 +557,8 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) { fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); }); // expect no crash for overflow, and inset size to be clamped to surface size - injectTap(202, 202); - fgSurface->expectTap(1, 1); + injectTap(112, 124); + bgSurface->expectTap(12, 24); } // Ensure we ignore transparent region when getting screen bounds when positioning input frame. @@ -987,6 +990,107 @@ TEST_F(InputSurfacesTest, drop_input_policy) { EXPECT_EQ(surface->consumeEvent(100), nullptr); } +TEST_F(InputSurfacesTest, layer_with_empty_crop_cannot_be_focused) { + std::unique_ptr<InputSurface> bufferSurface = + InputSurface::makeBufferInputSurface(mComposerClient, 100, 100); + + bufferSurface->showAt(50, 50, Rect::EMPTY_RECT); + + bufferSurface->requestFocus(); + EXPECT_EQ(bufferSurface->consumeEvent(100), nullptr); + + bufferSurface->showAt(50, 50, Rect::INVALID_RECT); + + bufferSurface->requestFocus(); + EXPECT_EQ(bufferSurface->consumeEvent(100), nullptr); +} + +TEST_F(InputSurfacesTest, layer_with_valid_crop_can_be_focused) { + std::unique_ptr<InputSurface> bufferSurface = + InputSurface::makeBufferInputSurface(mComposerClient, 100, 100); + + bufferSurface->showAt(50, 50, Rect{0, 0, 100, 100}); + + bufferSurface->requestFocus(); + bufferSurface->assertFocusChange(true); +} + +/** + * If a cropped layer's touchable region is replaced with a null crop, it should receive input in + * its own crop. + */ +TEST_F(InputSurfacesTest, cropped_container_replaces_touchable_region_with_null_crop) { + std::unique_ptr<InputSurface> parentContainer = + InputSurface::makeContainerInputSurface(mComposerClient, 0, 0); + std::unique_ptr<InputSurface> containerSurface = + InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); + containerSurface->doTransaction( + [&](auto &t, auto &sc) { t.reparent(sc, parentContainer->mSurfaceControl); }); + containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true; + containerSurface->mInputInfo.touchableRegionCropHandle = nullptr; + parentContainer->showAt(10, 10, Rect(0, 0, 20, 20)); + containerSurface->showAt(10, 10, Rect(0, 0, 5, 5)); + + // Receives events inside its own crop + injectTap(21, 21); + containerSurface->expectTap(1, 1); // Event is in layer space + + // Does not receive events outside its crop + injectTap(26, 26); + EXPECT_EQ(containerSurface->consumeEvent(100), nullptr); +} + +/** + * If an un-cropped layer's touchable region is replaced with a null crop, it should receive input + * in its parent's touchable region. The input events should be in the layer's coordinate space. + */ +TEST_F(InputSurfacesTest, uncropped_container_replaces_touchable_region_with_null_crop) { + std::unique_ptr<InputSurface> parentContainer = + InputSurface::makeContainerInputSurface(mComposerClient, 0, 0); + std::unique_ptr<InputSurface> containerSurface = + InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); + containerSurface->doTransaction( + [&](auto &t, auto &sc) { t.reparent(sc, parentContainer->mSurfaceControl); }); + containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true; + containerSurface->mInputInfo.touchableRegionCropHandle = nullptr; + parentContainer->showAt(10, 10, Rect(0, 0, 20, 20)); + containerSurface->showAt(10, 10, Rect::INVALID_RECT); + + // Receives events inside parent bounds + injectTap(21, 21); + containerSurface->expectTap(1, 1); // Event is in layer space + + // Does not receive events outside parent bounds + injectTap(31, 31); + EXPECT_EQ(containerSurface->consumeEvent(100), nullptr); +} + +/** + * If a layer's touchable region is replaced with a layer crop, it should receive input in the crop + * layer's bounds. The input events should be in the layer's coordinate space. + */ +TEST_F(InputSurfacesTest, replace_touchable_region_with_crop) { + std::unique_ptr<InputSurface> cropLayer = + InputSurface::makeContainerInputSurface(mComposerClient, 0, 0); + cropLayer->showAt(50, 50, Rect(0, 0, 20, 20)); + + std::unique_ptr<InputSurface> containerSurface = + InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); + containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true; + containerSurface->mInputInfo.touchableRegionCropHandle = + cropLayer->mSurfaceControl->getHandle(); + containerSurface->showAt(10, 10, Rect::INVALID_RECT); + + // Receives events inside crop layer bounds + injectTap(51, 51); + containerSurface->expectTap(41, 41); // Event is in layer space + + // Does not receive events outside crop layer bounds + injectTap(21, 21); + injectTap(71, 71); + EXPECT_EQ(containerSurface->consumeEvent(100), nullptr); +} + class MultiDisplayTests : public InputSurfacesTest { public: MultiDisplayTests() : InputSurfacesTest() { ProcessState::self()->startThreadPool(); } diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 0ebd11cf32..120ed48d49 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -762,10 +762,6 @@ public: return NO_ERROR; } status_t clearBootDisplayMode(const sp<IBinder>& /*display*/) override { return NO_ERROR; } - status_t getPreferredBootDisplayMode(const sp<IBinder>& /*display*/, - ui::DisplayModeId* /*id*/) override { - return NO_ERROR; - } void setAutoLowLatencyMode(const sp<IBinder>& /*display*/, bool /*on*/) override {} void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {} diff --git a/libs/input/Android.bp b/libs/input/Android.bp index e73c3b8518..930d8194d5 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -35,6 +35,7 @@ filegroup { cc_library { name: "libinput", + cpp_std: "c++20", host_supported: true, cflags: [ "-Wall", diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index ac84627b3f..0bee1b6f2a 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -252,6 +252,13 @@ void InputDeviceInfo::addLightInfo(const InputDeviceLightInfo& info) { mLights.insert_or_assign(info.id, info); } +void InputDeviceInfo::setKeyboardType(int32_t keyboardType) { + static_assert(AINPUT_KEYBOARD_TYPE_NONE < AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC); + static_assert(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC < AINPUT_KEYBOARD_TYPE_ALPHABETIC); + // There can be multiple subdevices with different keyboard types, set it to the highest type + mKeyboardType = std::max(mKeyboardType, keyboardType); +} + std::vector<InputDeviceSensorInfo> InputDeviceInfo::getSensors() { std::vector<InputDeviceSensorInfo> infos; infos.reserve(mSensors.size()); diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index a065ce25f7..61950522ff 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -1318,10 +1318,6 @@ status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) return result; } -bool InputConsumer::hasDeferredEvent() const { - return mMsgDeferred; -} - bool InputConsumer::hasPendingBatch() const { return !mBatches.empty(); } diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl index 829bbdd0b7..265cbf0c0b 100644 --- a/libs/input/android/os/IInputConstants.aidl +++ b/libs/input/android/os/IInputConstants.aidl @@ -95,4 +95,7 @@ interface IInputConstants */ INTERCEPTS_STYLUS = 0x00000040, } + + /* The default pointer acceleration value. */ + const int DEFAULT_POINTER_ACCELERATION = 3; } diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index b6a94764e5..1c8658b69a 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -115,12 +115,9 @@ void TestHeaderSize() { static_assert(sizeof(InputMessage::Header) == 8); } -/** - * We cannot use the Body::size() method here because it is not static for - * the Motion type, where "pointerCount" variable affects the size and can change at runtime. - */ void TestBodySize() { static_assert(sizeof(InputMessage::Body::Key) == 96); + static_assert(sizeof(InputMessage::Body::Motion::Pointer) == 136); static_assert(sizeof(InputMessage::Body::Motion) == offsetof(InputMessage::Body::Motion, pointers) + sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS); @@ -132,6 +129,38 @@ void TestBodySize() { // Timeline static_assert(GraphicsTimeline::SIZE == 2); static_assert(sizeof(InputMessage::Body::Timeline) == 24); + + /** + * We cannot use the Body::size() method here because it is not static for + * the Motion type, where "pointerCount" variable affects the size and can change at runtime. + */ + static_assert(sizeof(InputMessage::Body) == + offsetof(InputMessage::Body::Motion, pointers) + + sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS); + static_assert(sizeof(InputMessage::Body) == 160 + 136 * 16); + static_assert(sizeof(InputMessage::Body) == 2336); +} + +/** + * In general, we are sending a variable-length message across the socket, because the number of + * pointers varies. When we receive the message, we still need to allocate enough memory for the + * entire InputMessage struct. This size is, therefore, the worst case scenario. However, it is + * still helpful to compute to get an idea of the sizes that are involved. + */ +void TestWorstCaseInputMessageSize() { + static_assert(sizeof(InputMessage) == /*header*/ 8 + /*body*/ 2336); + static_assert(sizeof(InputMessage) == 2344); +} + +/** + * Assuming a single pointer, how big is the message that we are sending across the socket? + */ +void CalculateSinglePointerInputMessageSize() { + constexpr size_t pointerCount = 1; + constexpr size_t bodySize = offsetof(InputMessage::Body::Motion, pointers) + + sizeof(InputMessage::Body::Motion::Pointer) * pointerCount; + static_assert(bodySize == 160 + 136); + static_assert(bodySize == 296); // For the total message size, add the small header } // --- VerifiedInputEvent --- diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp index 84daea09f0..d90ee57322 100644 --- a/libs/nativedisplay/AChoreographer.cpp +++ b/libs/nativedisplay/AChoreographer.cpp @@ -552,7 +552,7 @@ size_t AChoreographerFrameCallbackData_routeGetPreferredFrameTimelineIndex( const AChoreographerFrameCallbackData* data) { return AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(data); } -int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId( +AVsyncId AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId( const AChoreographerFrameCallbackData* data, size_t index) { return AChoreographerFrameCallbackData_getFrameTimelineVsyncId(data, index); } @@ -644,7 +644,7 @@ size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex( "Data is only valid in callback"); return frameCallbackData->preferredFrameTimelineIndex; } -int64_t AChoreographerFrameCallbackData_getFrameTimelineVsyncId( +AVsyncId AChoreographerFrameCallbackData_getFrameTimelineVsyncId( const AChoreographerFrameCallbackData* data, size_t index) { const ChoreographerFrameCallbackDataImpl* frameCallbackData = AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data); diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp index 6288194714..76b85d6002 100644 --- a/libs/nativedisplay/ADisplay.cpp +++ b/libs/nativedisplay/ADisplay.cpp @@ -50,11 +50,6 @@ struct DisplayConfigImpl { int32_t height{0}; /** - * The display density. - */ - float density{0}; - - /** * The refresh rate of the display configuration, in frames per second. */ float fps{0.0}; @@ -168,8 +163,8 @@ int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) { const ui::DisplayMode& mode = modes[j]; modesPerDisplay[i].emplace_back( DisplayConfigImpl{static_cast<size_t>(mode.id), mode.resolution.getWidth(), - mode.resolution.getHeight(), staticInfo.density, - mode.refreshRate, mode.sfVsyncOffset, mode.appVsyncOffset}); + mode.resolution.getHeight(), mode.refreshRate, + mode.sfVsyncOffset, mode.appVsyncOffset}); } } @@ -283,12 +278,6 @@ int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig) { return NAME_NOT_FOUND; } -float ADisplayConfig_getDensity(ADisplayConfig* config) { - CHECK_NOT_NULL(config); - - return reinterpret_cast<DisplayConfigImpl*>(config)->density; -} - int32_t ADisplayConfig_getWidth(ADisplayConfig* config) { CHECK_NOT_NULL(config); diff --git a/libs/nativedisplay/include-private/private/android/choreographer.h b/libs/nativedisplay/include-private/private/android/choreographer.h index 0a1fcbeb94..d650c26605 100644 --- a/libs/nativedisplay/include-private/private/android/choreographer.h +++ b/libs/nativedisplay/include-private/private/android/choreographer.h @@ -65,7 +65,7 @@ size_t AChoreographerFrameCallbackData_routeGetFrameTimelinesLength( const AChoreographerFrameCallbackData* data); size_t AChoreographerFrameCallbackData_routeGetPreferredFrameTimelineIndex( const AChoreographerFrameCallbackData* data); -int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId( +AVsyncId AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId( const AChoreographerFrameCallbackData* data, size_t index); int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTimeNanos( const AChoreographerFrameCallbackData* data, size_t index); diff --git a/libs/nativedisplay/include/apex/display.h b/libs/nativedisplay/include/apex/display.h index bd94b5523e..0f449028ac 100644 --- a/libs/nativedisplay/include/apex/display.h +++ b/libs/nativedisplay/include/apex/display.h @@ -107,11 +107,6 @@ void ADisplay_getPreferredWideColorFormat(ADisplay* display, ADataSpace* outData int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig); /** - * Queries the density for a given display configuration. - */ -float ADisplayConfig_getDensity(ADisplayConfig* config); - -/** * Queries the width in pixels for a given display configuration. */ int32_t ADisplayConfig_getWidth(ADisplayConfig* config); diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt index b1b6498fe9..657931342d 100644 --- a/libs/nativedisplay/libnativedisplay.map.txt +++ b/libs/nativedisplay/libnativedisplay.map.txt @@ -50,7 +50,6 @@ LIBNATIVEDISPLAY_PLATFORM { android::ADisplay_getDisplayType*; android::ADisplay_getPreferredWideColorFormat*; android::ADisplay_getCurrentConfig*; - android::ADisplayConfig_getDensity*; android::ADisplayConfig_getWidth*; android::ADisplayConfig_getHeight*; android::ADisplayConfig_getFps*; diff --git a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp index 6882ea3090..0128859ca6 100644 --- a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp +++ b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp @@ -593,6 +593,10 @@ status_t EGLConsumer::doGLFenceWaitLocked(SurfaceTexture& st) const { } void EGLConsumer::onFreeBufferLocked(int slotIndex) { + if (mEglSlots[slotIndex].mEglImage != nullptr && + mEglSlots[slotIndex].mEglImage == mCurrentTextureImage) { + mCurrentTextureImage.clear(); + } mEglSlots[slotIndex].mEglImage.clear(); } diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp index cb3361b431..2578ee8467 100644 --- a/libs/nativewindow/AHardwareBuffer.cpp +++ b/libs/nativewindow/AHardwareBuffer.cpp @@ -509,10 +509,6 @@ bool AHardwareBuffer_isValidDescription(const AHardwareBuffer_Desc* desc, bool l ALOGE_IF(log, "AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA requires AHARDWAREBUFFER_FORMAT_BLOB"); return false; } - if (desc->usage & AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER) { - ALOGE_IF(log, "AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER requires AHARDWAREBUFFER_FORMAT_BLOB"); - return false; - } } if ((desc->usage & (AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) && diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h index b4cab39bd9..2c51ccd334 100644 --- a/libs/renderengine/include/renderengine/DisplaySettings.h +++ b/libs/renderengine/include/renderengine/DisplaySettings.h @@ -43,6 +43,9 @@ struct DisplaySettings { // Maximum luminance pulled from the display's HDR capabilities. float maxLuminance = 1.0f; + // Current luminance of the display + float currentLuminanceNits = -1.f; + // Output dataspace that will be populated if wide color gamut is used, or // DataSpace::UNKNOWN otherwise. ui::Dataspace outputDataspace = ui::Dataspace::UNKNOWN; diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index cc90946753..763b82d043 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -656,6 +656,7 @@ sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader( parameters.layerDimmingRatio, 1.f)); return createLinearEffectShader(parameters.shader, effect, runtimeEffect, colorTransform, parameters.display.maxLuminance, + parameters.display.currentLuminanceNits, parameters.layer.source.buffer.maxLuminanceNits); } return parameters.shader; @@ -1103,6 +1104,15 @@ void SkiaGLRenderEngine::drawLayersInternal( paint.setDither(true); } paint.setAlphaf(layer.alpha); + + if (imageTextureRef->colorType() == kAlpha_8_SkColorType) { + LOG_ALWAYS_FATAL_IF(layer.disableBlending, "Cannot disableBlending with A8"); + float matrix[] = { 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, -1, 1 }; + paint.setColorFilter(SkColorFilters::Matrix(matrix)); + } } else { ATRACE_NAME("DrawColor"); const auto color = layer.source.solidColor; @@ -1124,7 +1134,11 @@ void SkiaGLRenderEngine::drawLayersInternal( paint.setBlendMode(SkBlendMode::kSrc); } - paint.setColorFilter(displayColorTransform); + // A color filter will have been set for an A8 buffer. Do not replace + // it with the displayColorTransform, which shouldn't affect A8. + if (!paint.getColorFilter()) { + paint.setColorFilter(displayColorTransform); + } if (!roundRectClip.isEmpty()) { canvas->clipRRect(roundRectClip, true); diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp index 36305aeea8..6077c2e7da 100644 --- a/libs/renderengine/skia/filters/LinearEffect.cpp +++ b/libs/renderengine/skia/filters/LinearEffect.cpp @@ -44,14 +44,15 @@ sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader, const shaders::LinearEffect& linearEffect, sk_sp<SkRuntimeEffect> runtimeEffect, const mat4& colorTransform, float maxDisplayLuminance, - float maxLuminance) { + float currentDisplayLuminanceNits, float maxLuminance) { ATRACE_CALL(); SkRuntimeShaderBuilder effectBuilder(runtimeEffect); effectBuilder.child("child") = shader; - const auto uniforms = shaders::buildLinearEffectUniforms(linearEffect, colorTransform, - maxDisplayLuminance, maxLuminance); + const auto uniforms = + shaders::buildLinearEffectUniforms(linearEffect, colorTransform, maxDisplayLuminance, + currentDisplayLuminanceNits, maxLuminance); for (const auto& uniform : uniforms) { effectBuilder.uniform(uniform.name.c_str()).set(uniform.value.data(), uniform.value.size()); diff --git a/libs/renderengine/skia/filters/LinearEffect.h b/libs/renderengine/skia/filters/LinearEffect.h index 8eb6670150..e0a556b11a 100644 --- a/libs/renderengine/skia/filters/LinearEffect.h +++ b/libs/renderengine/skia/filters/LinearEffect.h @@ -37,13 +37,14 @@ sk_sp<SkRuntimeEffect> buildRuntimeEffect(const shaders::LinearEffect& linearEff // matrix transforming from linear XYZ to linear RGB immediately before OETF. // We also provide additional HDR metadata upon creating the shader: // * The max display luminance is the max luminance of the physical display in nits +// * The current luminance of the physical display in nits // * The max luminance is provided as the max luminance for the buffer, either from the SMPTE 2086 // or as the max light level from the CTA 861.3 standard. sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> inputShader, const shaders::LinearEffect& linearEffect, sk_sp<SkRuntimeEffect> runtimeEffect, const mat4& colorTransform, float maxDisplayLuminance, - float maxLuminance); + float currentDisplayLuminanceNits, float maxLuminance); } // namespace skia } // namespace renderengine } // namespace android diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index 2a25b0bfeb..e197150afe 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -49,6 +49,50 @@ constexpr bool WRITE_BUFFER_TO_FILE_ON_FAILURE = false; namespace android { namespace renderengine { +namespace { + +double EOTF_PQ(double channel) { + float m1 = (2610.0 / 4096.0) / 4.0; + float m2 = (2523.0 / 4096.0) * 128.0; + float c1 = (3424.0 / 4096.0); + float c2 = (2413.0 / 4096.0) * 32.0; + float c3 = (2392.0 / 4096.0) * 32.0; + + float tmp = std::pow(std::clamp(channel, 0.0, 1.0), 1.0 / m2); + tmp = std::fmax(tmp - c1, 0.0) / (c2 - c3 * tmp); + return std::pow(tmp, 1.0 / m1); +} + +vec3 EOTF_PQ(vec3 color) { + return vec3(EOTF_PQ(color.r), EOTF_PQ(color.g), EOTF_PQ(color.b)); +} + +double EOTF_HLG(double channel) { + const float a = 0.17883277; + const float b = 0.28466892; + const float c = 0.55991073; + return channel <= 0.5 ? channel * channel / 3.0 : (exp((channel - c) / a) + b) / 12.0; +} + +vec3 EOTF_HLG(vec3 color) { + return vec3(EOTF_HLG(color.r), EOTF_HLG(color.g), EOTF_HLG(color.b)); +} + +double OETF_sRGB(double channel) { + return channel <= 0.0031308 ? channel * 12.92 : (pow(channel, 1.0 / 2.4) * 1.055) - 0.055; +} + +int sign(float in) { + return in >= 0.0 ? 1 : -1; +} + +vec3 OETF_sRGB(vec3 linear) { + return vec3(sign(linear.r) * OETF_sRGB(linear.r), sign(linear.g) * OETF_sRGB(linear.g), + sign(linear.b) * OETF_sRGB(linear.b)); +} + +} // namespace + class RenderEngineFactory { public: virtual ~RenderEngineFactory() = default; @@ -218,14 +262,35 @@ public: uint8_t* pixels; buffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, reinterpret_cast<void**>(&pixels)); - pixels[0] = color.r; - pixels[1] = color.g; - pixels[2] = color.b; - pixels[3] = color.a; + for (uint32_t j = 0; j < height; j++) { + uint8_t* dst = pixels + (buffer->getBuffer()->getStride() * j * 4); + for (uint32_t i = 0; i < width; i++) { + dst[0] = color.r; + dst[1] = color.g; + dst[2] = color.b; + dst[3] = color.a; + dst += 4; + } + } buffer->getBuffer()->unlock(); return buffer; } + std::shared_ptr<renderengine::ExternalTexture> allocateR8Buffer(int width, int height) { + auto buffer = new GraphicBuffer(width, height, android::PIXEL_FORMAT_R_8, 1, + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | + GRALLOC_USAGE_HW_TEXTURE, + "r8"); + if (buffer->initCheck() != 0) { + // Devices are not required to support R8. + return nullptr; + } + return std::make_shared< + renderengine::impl::ExternalTexture>(std::move(buffer), *mRE, + renderengine::impl::ExternalTexture::Usage:: + READABLE); + } + RenderEngineTest() { const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); @@ -577,6 +642,12 @@ public: const renderengine::ShadowSettings& shadow, const ubyte4& backgroundColor); + // Tonemaps grey values from sourceDataspace -> Display P3 and checks that GPU and CPU + // implementations are identical Also implicitly checks that the injected tonemap shader + // compiles + void tonemap(ui::Dataspace sourceDataspace, std::function<vec3(vec3)> eotf, + std::function<vec3(vec3, float)> scaleOotf); + void initializeRenderEngine(); std::unique_ptr<renderengine::RenderEngine> mRE; @@ -1397,6 +1468,119 @@ void RenderEngineTest::drawShadowWithoutCaster(const FloatRect& castingBounds, invokeDraw(settings, layers); } +void RenderEngineTest::tonemap(ui::Dataspace sourceDataspace, std::function<vec3(vec3)> eotf, + std::function<vec3(vec3, float)> scaleOotf) { + constexpr int32_t kGreyLevels = 256; + + const auto rect = Rect(0, 0, kGreyLevels, 1); + + constexpr float kMaxLuminance = 750.f; + constexpr float kCurrentLuminanceNits = 500.f; + const renderengine::DisplaySettings display{ + .physicalDisplay = rect, + .clip = rect, + .maxLuminance = kMaxLuminance, + .currentLuminanceNits = kCurrentLuminanceNits, + .outputDataspace = ui::Dataspace::DISPLAY_P3, + }; + + auto buf = std::make_shared< + renderengine::impl:: + ExternalTexture>(new GraphicBuffer(kGreyLevels, 1, HAL_PIXEL_FORMAT_RGBA_8888, + 1, + GRALLOC_USAGE_SW_READ_OFTEN | + GRALLOC_USAGE_SW_WRITE_OFTEN | + GRALLOC_USAGE_HW_RENDER | + GRALLOC_USAGE_HW_TEXTURE, + "input"), + *mRE, + renderengine::impl::ExternalTexture::Usage::READABLE | + renderengine::impl::ExternalTexture::Usage::WRITEABLE); + ASSERT_EQ(0, buf->getBuffer()->initCheck()); + { + uint8_t* pixels; + buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, + reinterpret_cast<void**>(&pixels)); + + uint8_t color = 0; + for (int32_t j = 0; j < buf->getBuffer()->getHeight(); j++) { + uint8_t* dest = pixels + (buf->getBuffer()->getStride() * j * 4); + for (int32_t i = 0; i < buf->getBuffer()->getWidth(); i++) { + dest[0] = color; + dest[1] = color; + dest[2] = color; + dest[3] = 255; + color++; + dest += 4; + } + } + buf->getBuffer()->unlock(); + } + + mBuffer = std::make_shared< + renderengine::impl:: + ExternalTexture>(new GraphicBuffer(kGreyLevels, 1, HAL_PIXEL_FORMAT_RGBA_8888, + 1, + GRALLOC_USAGE_SW_READ_OFTEN | + GRALLOC_USAGE_SW_WRITE_OFTEN | + GRALLOC_USAGE_HW_RENDER | + GRALLOC_USAGE_HW_TEXTURE, + "output"), + *mRE, + renderengine::impl::ExternalTexture::Usage::READABLE | + renderengine::impl::ExternalTexture::Usage::WRITEABLE); + ASSERT_EQ(0, mBuffer->getBuffer()->initCheck()); + + const renderengine::LayerSettings layer{.geometry.boundaries = rect.toFloatRect(), + .source = + renderengine::PixelSource{ + .buffer = + renderengine::Buffer{ + .buffer = + std::move(buf), + .usePremultipliedAlpha = + true, + }, + }, + .alpha = 1.0f, + .sourceDataspace = sourceDataspace}; + + std::vector<renderengine::LayerSettings> layers{layer}; + invokeDraw(display, layers); + + ColorSpace displayP3 = ColorSpace::DisplayP3(); + ColorSpace bt2020 = ColorSpace::BT2020(); + + tonemap::Metadata metadata{.displayMaxLuminance = 750.0f}; + + auto generator = [=](Point location) { + const double normColor = static_cast<double>(location.x) / (kGreyLevels - 1); + const vec3 rgb = vec3(normColor, normColor, normColor); + + const vec3 linearRGB = eotf(rgb); + + const vec3 xyz = bt2020.getRGBtoXYZ() * linearRGB; + + const vec3 scaledXYZ = scaleOotf(xyz, kCurrentLuminanceNits); + const double gain = + tonemap::getToneMapper() + ->lookupTonemapGain(static_cast<aidl::android::hardware::graphics::common:: + Dataspace>(sourceDataspace), + static_cast<aidl::android::hardware::graphics::common:: + Dataspace>( + ui::Dataspace::DISPLAY_P3), + scaleOotf(linearRGB, kCurrentLuminanceNits), scaledXYZ, + metadata); + const vec3 normalizedXYZ = scaledXYZ * gain / metadata.displayMaxLuminance; + + const vec3 targetRGB = OETF_sRGB(displayP3.getXYZtoRGB() * normalizedXYZ) * 255; + return ubyte4(static_cast<uint8_t>(targetRGB.r), static_cast<uint8_t>(targetRGB.g), + static_cast<uint8_t>(targetRGB.b), 255); + }; + + expectBufferColor(Rect(kGreyLevels, 1), generator, 2); +} + INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest, testing::Values(std::make_shared<GLESRenderEngineFactory>(), std::make_shared<GLESCMRenderEngineFactory>(), @@ -2391,155 +2575,107 @@ TEST_P(RenderEngineTest, test_isOpaque) { } } -double EOTF_PQ(double channel) { - float m1 = (2610.0 / 4096.0) / 4.0; - float m2 = (2523.0 / 4096.0) * 128.0; - float c1 = (3424.0 / 4096.0); - float c2 = (2413.0 / 4096.0) * 32.0; - float c3 = (2392.0 / 4096.0) * 32.0; - - float tmp = std::pow(std::clamp(channel, 0.0, 1.0), 1.0 / m2); - tmp = std::fmax(tmp - c1, 0.0) / (c2 - c3 * tmp); - return std::pow(tmp, 1.0 / m1); -} - -vec3 EOTF_PQ(vec3 color) { - return vec3(EOTF_PQ(color.r), EOTF_PQ(color.g), EOTF_PQ(color.b)); -} +TEST_P(RenderEngineTest, test_tonemapPQMatches) { + if (!GetParam()->useColorManagement()) { + GTEST_SKIP(); + } -double OETF_sRGB(double channel) { - return channel <= 0.0031308 ? channel * 12.92 : (pow(channel, 1.0 / 2.4) * 1.055) - 0.055; -} + if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) { + GTEST_SKIP(); + } -int sign(float in) { - return in >= 0.0 ? 1 : -1; -} + initializeRenderEngine(); -vec3 OETF_sRGB(vec3 linear) { - return vec3(sign(linear.r) * OETF_sRGB(linear.r), sign(linear.g) * OETF_sRGB(linear.g), - sign(linear.b) * OETF_sRGB(linear.b)); + tonemap( + static_cast<ui::Dataspace>(HAL_DATASPACE_STANDARD_BT2020 | + HAL_DATASPACE_TRANSFER_ST2084 | HAL_DATASPACE_RANGE_FULL), + [](vec3 color) { return EOTF_PQ(color); }, + [](vec3 color, float) { + static constexpr float kMaxPQLuminance = 10000.f; + return color * kMaxPQLuminance; + }); } -TEST_P(RenderEngineTest, test_tonemapPQMatches) { +TEST_P(RenderEngineTest, test_tonemapHLGMatches) { if (!GetParam()->useColorManagement()) { - return; + GTEST_SKIP(); } if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) { - return; + GTEST_SKIP(); } initializeRenderEngine(); - constexpr int32_t kGreyLevels = 256; + tonemap( + static_cast<ui::Dataspace>(HAL_DATASPACE_STANDARD_BT2020 | HAL_DATASPACE_TRANSFER_HLG | + HAL_DATASPACE_RANGE_FULL), + [](vec3 color) { return EOTF_HLG(color); }, + [](vec3 color, float currentLuminaceNits) { + static constexpr float kMaxHLGLuminance = 1000.f; + static const float kHLGGamma = 1.2 + 0.42 * std::log10(currentLuminaceNits / 1000); + return color * kMaxHLGLuminance * std::pow(color.y, kHLGGamma - 1); + }); +} - const auto rect = Rect(0, 0, kGreyLevels, 1); - const renderengine::DisplaySettings display{ - .physicalDisplay = rect, - .clip = rect, - .maxLuminance = 750.0f, - .outputDataspace = ui::Dataspace::DISPLAY_P3, - }; +TEST_P(RenderEngineTest, r8_behaves_as_mask) { + if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) { + return; + } - auto buf = std::make_shared< - renderengine::impl:: - ExternalTexture>(new GraphicBuffer(kGreyLevels, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, - GRALLOC_USAGE_SW_READ_OFTEN | - GRALLOC_USAGE_SW_WRITE_OFTEN | - GRALLOC_USAGE_HW_RENDER | - GRALLOC_USAGE_HW_TEXTURE, - "input"), - *mRE, - renderengine::impl::ExternalTexture::Usage::READABLE | - renderengine::impl::ExternalTexture::Usage::WRITEABLE); - ASSERT_EQ(0, buf->getBuffer()->initCheck()); + initializeRenderEngine(); + const auto r8Buffer = allocateR8Buffer(2, 1); + if (!r8Buffer) { + return; + } { uint8_t* pixels; - buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, - reinterpret_cast<void**>(&pixels)); - - uint8_t color = 0; - for (int32_t j = 0; j < buf->getBuffer()->getHeight(); j++) { - uint8_t* dest = pixels + (buf->getBuffer()->getStride() * j * 4); - for (int32_t i = 0; i < buf->getBuffer()->getWidth(); i++) { - dest[0] = color; - dest[1] = color; - dest[2] = color; - dest[3] = 255; - color++; - dest += 4; - } - } - buf->getBuffer()->unlock(); + r8Buffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, + reinterpret_cast<void**>(&pixels)); + // This will be drawn on top of a green buffer. We'll verify that 255 + // results in keeping the original green and 0 results in black. + pixels[0] = 0; + pixels[1] = 255; + r8Buffer->getBuffer()->unlock(); } - mBuffer = std::make_shared< - renderengine::impl:: - ExternalTexture>(new GraphicBuffer(kGreyLevels, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, - GRALLOC_USAGE_SW_READ_OFTEN | - GRALLOC_USAGE_SW_WRITE_OFTEN | - GRALLOC_USAGE_HW_RENDER | - GRALLOC_USAGE_HW_TEXTURE, - "output"), - *mRE, - renderengine::impl::ExternalTexture::Usage::READABLE | - renderengine::impl::ExternalTexture::Usage::WRITEABLE); - ASSERT_EQ(0, mBuffer->getBuffer()->initCheck()); + const auto rect = Rect(0, 0, 2, 1); + const renderengine::DisplaySettings display{ + .physicalDisplay = rect, + .clip = rect, + .outputDataspace = ui::Dataspace::SRGB, + }; - const renderengine::LayerSettings layer{ + const auto greenBuffer = allocateAndFillSourceBuffer(2, 1, ubyte4(0, 255, 0, 255)); + const renderengine::LayerSettings greenLayer{ .geometry.boundaries = rect.toFloatRect(), .source = renderengine::PixelSource{ .buffer = renderengine::Buffer{ - .buffer = std::move(buf), - .usePremultipliedAlpha = true, + .buffer = greenBuffer, + }, + }, + .alpha = 1.0f, + }; + const renderengine::LayerSettings r8Layer{ + .geometry.boundaries = rect.toFloatRect(), + .source = + renderengine::PixelSource{ + .buffer = + renderengine::Buffer{ + .buffer = r8Buffer, }, }, .alpha = 1.0f, - .sourceDataspace = static_cast<ui::Dataspace>(HAL_DATASPACE_STANDARD_BT2020 | - HAL_DATASPACE_TRANSFER_ST2084 | - HAL_DATASPACE_RANGE_FULL), }; - std::vector<renderengine::LayerSettings> layers{layer}; + std::vector<renderengine::LayerSettings> layers{greenLayer, r8Layer}; invokeDraw(display, layers); - ColorSpace displayP3 = ColorSpace::DisplayP3(); - ColorSpace bt2020 = ColorSpace::BT2020(); - - tonemap::Metadata metadata{.displayMaxLuminance = 750.0f}; - - auto generator = [=](Point location) { - const double normColor = static_cast<double>(location.x) / (kGreyLevels - 1); - const vec3 rgb = vec3(normColor, normColor, normColor); - - const vec3 linearRGB = EOTF_PQ(rgb); - - static constexpr float kMaxPQLuminance = 10000.f; - const vec3 xyz = bt2020.getRGBtoXYZ() * linearRGB * kMaxPQLuminance; - const double gain = - tonemap::getToneMapper() - ->lookupTonemapGain(static_cast<aidl::android::hardware::graphics::common:: - Dataspace>( - HAL_DATASPACE_STANDARD_BT2020 | - HAL_DATASPACE_TRANSFER_ST2084 | - HAL_DATASPACE_RANGE_FULL), - static_cast<aidl::android::hardware::graphics::common:: - Dataspace>( - ui::Dataspace::DISPLAY_P3), - linearRGB * 10000.0, xyz, metadata); - const vec3 scaledXYZ = xyz * gain / metadata.displayMaxLuminance; - - const vec3 targetRGB = OETF_sRGB(displayP3.getXYZtoRGB() * scaledXYZ) * 255; - return ubyte4(static_cast<uint8_t>(targetRGB.r), static_cast<uint8_t>(targetRGB.g), - static_cast<uint8_t>(targetRGB.b), 255); - }; - - expectBufferColor(Rect(kGreyLevels, 1), generator, 2); + expectBufferColor(Rect(0, 0, 1, 1), 0, 0, 0, 255); + expectBufferColor(Rect(1, 0, 2, 1), 0, 255, 0, 255); } } // namespace renderengine } // namespace android diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp index da88e8541a..5b4631a251 100644 --- a/libs/sensor/Sensor.cpp +++ b/libs/sensor/Sensor.cpp @@ -280,6 +280,22 @@ Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersi mStringType = SENSOR_STRING_TYPE_HEAD_TRACKER; mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; break; + case SENSOR_TYPE_ACCELEROMETER_LIMITED_AXES: + mStringType = SENSOR_STRING_TYPE_ACCELEROMETER_LIMITED_AXES; + mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; + break; + case SENSOR_TYPE_GYROSCOPE_LIMITED_AXES: + mStringType = SENSOR_STRING_TYPE_GYROSCOPE_LIMITED_AXES; + mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; + break; + case SENSOR_TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED: + mStringType = SENSOR_STRING_TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED; + mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; + break; + case SENSOR_TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED: + mStringType = SENSOR_STRING_TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED; + mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; + break; default: // Only pipe the stringType, requiredPermission and flags for custom sensors. if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.stringType) { diff --git a/libs/shaders/include/shaders/shaders.h b/libs/shaders/include/shaders/shaders.h index 712a27a3eb..43828ccd05 100644 --- a/libs/shaders/include/shaders/shaders.h +++ b/libs/shaders/include/shaders/shaders.h @@ -100,6 +100,7 @@ std::string buildLinearEffectSkSL(const LinearEffect& linearEffect); std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(const LinearEffect& linearEffect, const mat4& colorTransform, float maxDisplayLuminance, + float currentDisplayLuminanceNits, float maxLuminance); } // namespace android::shaders diff --git a/libs/shaders/shaders.cpp b/libs/shaders/shaders.cpp index 6019c4ac28..03da3ecd62 100644 --- a/libs/shaders/shaders.cpp +++ b/libs/shaders/shaders.cpp @@ -18,6 +18,7 @@ #include <tonemap/tonemap.h> +#include <cmath> #include <optional> #include <math/mat4.h> @@ -26,12 +27,13 @@ namespace android::shaders { -static aidl::android::hardware::graphics::common::Dataspace toAidlDataspace( - ui::Dataspace dataspace) { +namespace { + +aidl::android::hardware::graphics::common::Dataspace toAidlDataspace(ui::Dataspace dataspace) { return static_cast<aidl::android::hardware::graphics::common::Dataspace>(dataspace); } -static void generateEOTF(ui::Dataspace dataspace, std::string& shader) { +void generateEOTF(ui::Dataspace dataspace, std::string& shader) { switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) { case HAL_DATASPACE_TRANSFER_ST2084: shader.append(R"( @@ -156,7 +158,7 @@ static void generateEOTF(ui::Dataspace dataspace, std::string& shader) { } } -static void generateXYZTransforms(std::string& shader) { +void generateXYZTransforms(std::string& shader) { shader.append(R"( uniform float4x4 in_rgbToXyz; uniform float4x4 in_xyzToRgb; @@ -171,8 +173,8 @@ static void generateXYZTransforms(std::string& shader) { } // Conversion from relative light to absolute light (maps from [0, 1] to [0, maxNits]) -static void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace, - ui::Dataspace outputDataspace, std::string& shader) { +void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace, + std::string& shader) { switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) { case HAL_DATASPACE_TRANSFER_ST2084: shader.append(R"( @@ -183,8 +185,9 @@ static void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace, break; case HAL_DATASPACE_TRANSFER_HLG: shader.append(R"( + uniform float in_hlgGamma; float3 ScaleLuminance(float3 xyz) { - return xyz * 1000.0 * pow(xyz.y, 0.2); + return xyz * 1000.0 * pow(xyz.y, in_hlgGamma - 1); } )"); break; @@ -225,8 +228,10 @@ static void generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace, break; case HAL_DATASPACE_TRANSFER_HLG: shader.append(R"( + uniform float in_hlgGamma; float3 NormalizeLuminance(float3 xyz) { - return xyz / 1000.0 * pow(xyz.y / 1000.0, -0.2 / 1.2); + return xyz / 1000.0 * + pow(xyz.y / 1000.0, (1 - in_hlgGamma) / (in_hlgGamma)); } )"); break; @@ -240,8 +245,8 @@ static void generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace, } } -static void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace, - std::string& shader) { +void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace, + std::string& shader) { shader.append(tonemap::getToneMapper() ->generateTonemapGainShaderSkSL(toAidlDataspace(inputDataspace), toAidlDataspace(outputDataspace)) @@ -262,7 +267,7 @@ static void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDatas )"); } -static void generateOETF(ui::Dataspace dataspace, std::string& shader) { +void generateOETF(ui::Dataspace dataspace, std::string& shader) { switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) { case HAL_DATASPACE_TRANSFER_ST2084: shader.append(R"( @@ -384,7 +389,7 @@ static void generateOETF(ui::Dataspace dataspace, std::string& shader) { } } -static void generateEffectiveOOTF(bool undoPremultipliedAlpha, std::string& shader) { +void generateEffectiveOOTF(bool undoPremultipliedAlpha, std::string& shader) { shader.append(R"( uniform shader child; half4 main(float2 xy) { @@ -412,7 +417,7 @@ static void generateEffectiveOOTF(bool undoPremultipliedAlpha, std::string& shad } // please keep in sync with toSkColorSpace function in renderengine/skia/ColorSpaces.cpp -static ColorSpace toColorSpace(ui::Dataspace dataspace) { +ColorSpace toColorSpace(ui::Dataspace dataspace) { switch (dataspace & HAL_DATASPACE_STANDARD_MASK) { case HAL_DATASPACE_STANDARD_BT709: return ColorSpace::sRGB(); @@ -438,6 +443,21 @@ static ColorSpace toColorSpace(ui::Dataspace dataspace) { } } +template <typename T, std::enable_if_t<std::is_trivially_copyable<T>::value, bool> = true> +std::vector<uint8_t> buildUniformValue(T value) { + std::vector<uint8_t> result; + result.resize(sizeof(value)); + std::memcpy(result.data(), &value, sizeof(value)); + return result; +} + +// Refer to BT2100-2 +float computeHlgGamma(float currentDisplayBrightnessNits) { + return 1.2 + 0.42 * std::log10(currentDisplayBrightnessNits / 1000); +} + +} // namespace + std::string buildLinearEffectSkSL(const LinearEffect& linearEffect) { std::string shaderString; generateEOTF(linearEffect.fakeInputDataspace == ui::Dataspace::UNKNOWN @@ -451,18 +471,11 @@ std::string buildLinearEffectSkSL(const LinearEffect& linearEffect) { return shaderString; } -template <typename T, std::enable_if_t<std::is_trivially_copyable<T>::value, bool> = true> -std::vector<uint8_t> buildUniformValue(T value) { - std::vector<uint8_t> result; - result.resize(sizeof(value)); - std::memcpy(result.data(), &value, sizeof(value)); - return result; -} - // Generates a list of uniforms to set on the LinearEffect shader above. std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(const LinearEffect& linearEffect, const mat4& colorTransform, float maxDisplayLuminance, + float currentDisplayLuminanceNits, float maxLuminance) { std::vector<tonemap::ShaderUniform> uniforms; if (linearEffect.inputDataspace == linearEffect.outputDataspace) { @@ -479,6 +492,12 @@ std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(const LinearEffect colorTransform * mat4(outputColorSpace.getXYZtoRGB()))}); } + if ((linearEffect.inputDataspace & HAL_DATASPACE_TRANSFER_MASK) == HAL_DATASPACE_TRANSFER_HLG) { + uniforms.push_back( + {.name = "in_hlgGamma", + .value = buildUniformValue<float>(computeHlgGamma(currentDisplayLuminanceNits))}); + } + tonemap::Metadata metadata{.displayMaxLuminance = maxDisplayLuminance, // If the input luminance is unknown, use display luminance (aka, // no-op any luminance changes) diff --git a/libs/tonemap/include/tonemap/tonemap.h b/libs/tonemap/include/tonemap/tonemap.h index bd7b72d186..b9abf8cd52 100644 --- a/libs/tonemap/include/tonemap/tonemap.h +++ b/libs/tonemap/include/tonemap/tonemap.h @@ -42,7 +42,9 @@ struct ShaderUniform { // This metadata should not be used for manipulating the source code of the shader program directly, // as otherwise caching by other system of these shaders may break. struct Metadata { + // The maximum luminance of the display in nits float displayMaxLuminance = 0.0; + // The maximum luminance of the content in nits float contentMaxLuminance = 0.0; }; diff --git a/libs/tonemap/tests/tonemap_test.cpp b/libs/tonemap/tests/tonemap_test.cpp index 7a7958f58f..1d46482627 100644 --- a/libs/tonemap/tests/tonemap_test.cpp +++ b/libs/tonemap/tests/tonemap_test.cpp @@ -61,7 +61,7 @@ TEST_F(TonemapTest, generateShaderSkSLUniforms_containsDefaultUniforms) { EXPECT_GT(contentLumFloat, 0); } -TEST_F(TonemapTest, generateTonemapGainShaderSkSL_containsEntryPoint) { +TEST_F(TonemapTest, generateTonemapGainShaderSkSL_containsEntryPointForPQ) { const auto shader = tonemap::getToneMapper() ->generateTonemapGainShaderSkSL(aidl::android::hardware::graphics::common:: @@ -73,4 +73,16 @@ TEST_F(TonemapTest, generateTonemapGainShaderSkSL_containsEntryPoint) { EXPECT_THAT(shader, HasSubstr("float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz)")); } +TEST_F(TonemapTest, generateTonemapGainShaderSkSL_containsEntryPointForHLG) { + const auto shader = + tonemap::getToneMapper() + ->generateTonemapGainShaderSkSL(aidl::android::hardware::graphics::common:: + Dataspace::BT2020_ITU_HLG, + aidl::android::hardware::graphics::common:: + Dataspace::DISPLAY_P3); + + // Other tests such as librenderengine_test will plug in the shader to check compilation. + EXPECT_THAT(shader, HasSubstr("float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz)")); +} + } // namespace android diff --git a/libs/tonemap/tonemap.cpp b/libs/tonemap/tonemap.cpp index c2372fe828..bc0a884ee4 100644 --- a/libs/tonemap/tonemap.cpp +++ b/libs/tonemap/tonemap.cpp @@ -407,7 +407,6 @@ public: )"); switch (sourceDataspaceInt & kTransferMask) { case kTransferST2084: - case kTransferHLG: switch (destinationDataspaceInt & kTransferMask) { case kTransferST2084: program.append(R"( @@ -428,39 +427,22 @@ public: break; default: - switch (sourceDataspaceInt & kTransferMask) { - case kTransferST2084: - program.append(R"( - float libtonemap_OETFTone(float channel) { - channel = channel / 10000.0; - float m1 = (2610.0 / 4096.0) / 4.0; - float m2 = (2523.0 / 4096.0) * 128.0; - float c1 = (3424.0 / 4096.0); - float c2 = (2413.0 / 4096.0) * 32.0; - float c3 = (2392.0 / 4096.0) * 32.0; - - float tmp = pow(channel, float(m1)); - tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp); - return pow(tmp, float(m2)); - } - )"); - break; - case kTransferHLG: - program.append(R"( - float libtonemap_OETFTone(float channel) { - channel = channel / 1000.0; - const float a = 0.17883277; - const float b = 0.28466892; - const float c = 0.55991073; - return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) : - a * log(12.0 * channel - b) + c; - } - )"); - break; - } // Here we're mapping from HDR to SDR content, so interpolate using a // Hermitian polynomial onto the smaller luminance range. program.append(R"( + float libtonemap_OETFTone(float channel) { + channel = channel / 10000.0; + float m1 = (2610.0 / 4096.0) / 4.0; + float m2 = (2523.0 / 4096.0) * 128.0; + float c1 = (3424.0 / 4096.0); + float c2 = (2413.0 / 4096.0) * 32.0; + float c3 = (2392.0 / 4096.0) * 32.0; + + float tmp = pow(channel, float(m1)); + tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp); + return pow(tmp, float(m2)); + } + float libtonemap_ToneMapTargetNits(float maxRGB) { float maxInLumi = in_libtonemap_inputMaxLuminance; float maxOutLumi = in_libtonemap_displayMaxLuminance; @@ -508,6 +490,30 @@ public: break; } break; + case kTransferHLG: + switch (destinationDataspaceInt & kTransferMask) { + // HLG -> HDR does not tone-map at all + case kTransferST2084: + case kTransferHLG: + program.append(R"( + float libtonemap_ToneMapTargetNits(float maxRGB) { + return maxRGB; + } + )"); + break; + default: + // libshaders follows BT2100 OOTF, but with a nominal peak display luminance + // of 1000 nits. Renormalize to max display luminance if we're tone-mapping + // down to SDR, as libshaders normalizes all SDR output from [0, + // maxDisplayLumins] -> [0, 1] + program.append(R"( + float libtonemap_ToneMapTargetNits(float maxRGB) { + return maxRGB * in_libtonemap_displayMaxLuminance / 1000.0; + } + )"); + break; + } + break; default: // Inverse tone-mapping and SDR-SDR mapping is not supported. program.append(R"( @@ -558,7 +564,6 @@ public: double targetNits = 0.0; switch (sourceDataspaceInt & kTransferMask) { case kTransferST2084: - case kTransferHLG: switch (destinationDataspaceInt & kTransferMask) { case kTransferST2084: targetNits = maxRGB; @@ -587,19 +592,9 @@ public: double x2 = x1 + (x3 - x1) * 4.0 / 17.0; double y2 = maxOutLumi * 0.9; - double greyNorm1 = 0.0; - double greyNorm2 = 0.0; - double greyNorm3 = 0.0; - - if ((sourceDataspaceInt & kTransferMask) == kTransferST2084) { - greyNorm1 = OETF_ST2084(x1); - greyNorm2 = OETF_ST2084(x2); - greyNorm3 = OETF_ST2084(x3); - } else if ((sourceDataspaceInt & kTransferMask) == kTransferHLG) { - greyNorm1 = OETF_HLG(x1); - greyNorm2 = OETF_HLG(x2); - greyNorm3 = OETF_HLG(x3); - } + const double greyNorm1 = OETF_ST2084(x1); + const double greyNorm2 = OETF_ST2084(x2); + const double greyNorm3 = OETF_ST2084(x3); double slope2 = (y2 - y1) / (greyNorm2 - greyNorm1); double slope3 = (y3 - y2) / (greyNorm3 - greyNorm2); @@ -613,12 +608,7 @@ public: break; } - double greyNits = 0.0; - if ((sourceDataspaceInt & kTransferMask) == kTransferST2084) { - greyNits = OETF_ST2084(targetNits); - } else if ((sourceDataspaceInt & kTransferMask) == kTransferHLG) { - greyNits = OETF_HLG(targetNits); - } + const double greyNits = OETF_ST2084(targetNits); if (greyNits <= greyNorm2) { targetNits = (greyNits - greyNorm2) * slope2 + y2; @@ -630,15 +620,20 @@ public: break; } break; - default: + case kTransferHLG: switch (destinationDataspaceInt & kTransferMask) { case kTransferST2084: case kTransferHLG: - default: targetNits = maxRGB; break; + default: + targetNits = maxRGB * metadata.displayMaxLuminance / 1000.0; + break; } break; + default: + targetNits = maxRGB; + break; } return targetNits / maxRGB; diff --git a/libs/ui/DynamicDisplayInfo.cpp b/libs/ui/DynamicDisplayInfo.cpp index d5c4ef0c17..78ba9965b6 100644 --- a/libs/ui/DynamicDisplayInfo.cpp +++ b/libs/ui/DynamicDisplayInfo.cpp @@ -41,7 +41,8 @@ size_t DynamicDisplayInfo::getFlattenedSize() const { FlattenableHelpers::getFlattenedSize(activeColorMode) + FlattenableHelpers::getFlattenedSize(hdrCapabilities) + FlattenableHelpers::getFlattenedSize(autoLowLatencyModeSupported) + - FlattenableHelpers::getFlattenedSize(gameContentTypeSupported); + FlattenableHelpers::getFlattenedSize(gameContentTypeSupported) + + FlattenableHelpers::getFlattenedSize(preferredBootDisplayMode); } status_t DynamicDisplayInfo::flatten(void* buffer, size_t size) const { @@ -55,6 +56,7 @@ status_t DynamicDisplayInfo::flatten(void* buffer, size_t size) const { RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, hdrCapabilities)); RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, autoLowLatencyModeSupported)); RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, gameContentTypeSupported)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, preferredBootDisplayMode)); return OK; } @@ -66,6 +68,7 @@ status_t DynamicDisplayInfo::unflatten(const void* buffer, size_t size) { RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &hdrCapabilities)); RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &autoLowLatencyModeSupported)); RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &gameContentTypeSupported)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &preferredBootDisplayMode)); return OK; } diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp index 33ab7c470e..cc96f83578 100644 --- a/libs/ui/Fence.cpp +++ b/libs/ui/Fence.cpp @@ -132,9 +132,13 @@ nsecs_t Fence::getSignalTime() const { ALOGE("sync_file_info returned NULL for fd %d", mFenceFd.get()); return SIGNAL_TIME_INVALID; } + if (finfo->status != 1) { + const auto status = finfo->status; + ALOGE_IF(status < 0, "%s: sync_file_info contains an error: <%d> for fd: <%d>", __func__, + status, mFenceFd.get()); sync_file_info_free(finfo); - return SIGNAL_TIME_PENDING; + return status < 0 ? SIGNAL_TIME_INVALID : SIGNAL_TIME_PENDING; } uint64_t timestamp = 0; diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp index 040a62b542..f23f10a1a9 100644 --- a/libs/ui/Gralloc2.cpp +++ b/libs/ui/Gralloc2.cpp @@ -110,6 +110,15 @@ status_t Gralloc2Mapper::validateBufferDescriptorInfo( descriptorInfo->usage & ~validUsageBits); return BAD_VALUE; } + + // Gralloc2 implementations never understand non-BLOB with GPU_DATA_BUFFER + // and do not reliably reject it. + if (descriptorInfo->usage & BufferUsage::GPU_DATA_BUFFER && + descriptorInfo->format != hardware::graphics::common::V1_1::PixelFormat::BLOB) { + ALOGE("gralloc2 does not support non-BLOB pixel formats with GPU_DATA_BUFFER usage"); + return BAD_VALUE; + } + return NO_ERROR; } diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp index 882674f479..15c60bcadf 100644 --- a/libs/ui/Gralloc3.cpp +++ b/libs/ui/Gralloc3.cpp @@ -101,6 +101,15 @@ status_t Gralloc3Mapper::validateBufferDescriptorInfo( descriptorInfo->usage & ~validUsageBits); return BAD_VALUE; } + + // Gralloc3 implementations never understand non-BLOB with GPU_DATA_BUFFER + // and do not reliably reject it. + if (descriptorInfo->usage & BufferUsage::GPU_DATA_BUFFER && + descriptorInfo->format != hardware::graphics::common::V1_2::PixelFormat::BLOB) { + ALOGE("gralloc3 does not support non-BLOB pixel formats with GPU_DATA_BUFFER usage"); + return BAD_VALUE; + } + return NO_ERROR; } diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp index 1f8a2f058f..1a42642838 100644 --- a/libs/ui/Gralloc4.cpp +++ b/libs/ui/Gralloc4.cpp @@ -41,6 +41,7 @@ using aidl::android::hardware::graphics::common::StandardMetadataType; using android::hardware::hidl_vec; using android::hardware::graphics::allocator::V4_0::IAllocator; using android::hardware::graphics::common::V1_2::BufferUsage; +using android::hardware::graphics::common::V1_2::PixelFormat; using android::hardware::graphics::mapper::V4_0::BufferDescriptor; using android::hardware::graphics::mapper::V4_0::Error; using android::hardware::graphics::mapper::V4_0::IMapper; @@ -61,6 +62,9 @@ namespace { static constexpr Error kTransactionError = Error::NO_RESOURCES; static const auto kAidlAllocatorServiceName = AidlIAllocator::descriptor + std::string("/default"); +// TODO(b/72323293, b/72703005): Remove these invalid bits from callers +static constexpr uint64_t kRemovedUsageBits = static_cast<uint64_t>((1 << 10) | (1 << 13)); + uint64_t getValidUsageBits() { static const uint64_t validUsageBits = []() -> uint64_t { uint64_t bits = 0; @@ -70,7 +74,7 @@ uint64_t getValidUsageBits() { } return bits; }(); - return validUsageBits; + return validUsageBits | kRemovedUsageBits; } uint64_t getValidUsageBits41() { @@ -92,25 +96,11 @@ static inline IMapper::Rect sGralloc4Rect(const Rect& rect) { outRect.height = rect.height(); return outRect; } -static inline void sBufferDescriptorInfo(std::string name, uint32_t width, uint32_t height, - PixelFormat format, uint32_t layerCount, uint64_t usage, - IMapper::BufferDescriptorInfo* outDescriptorInfo) { - outDescriptorInfo->name = name; - outDescriptorInfo->width = width; - outDescriptorInfo->height = height; - outDescriptorInfo->layerCount = layerCount; - outDescriptorInfo->format = static_cast<hardware::graphics::common::V1_2::PixelFormat>(format); - outDescriptorInfo->usage = usage; - outDescriptorInfo->reservedSize = 0; -} // See if gralloc "4.1" is available. static bool hasIAllocatorAidl() { // Avoid re-querying repeatedly for this information; static bool sHasIAllocatorAidl = []() -> bool { - // TODO: Enable after landing sepolicy changes - if constexpr ((true)) return false; - if (__builtin_available(android 31, *)) { return AServiceManager_isDeclared(kAidlAllocatorServiceName.c_str()); } @@ -119,6 +109,46 @@ static bool hasIAllocatorAidl() { return sHasIAllocatorAidl; } +// Determines whether the passed info is compatible with the mapper. +static status_t validateBufferDescriptorInfo(IMapper::BufferDescriptorInfo* descriptorInfo) { + uint64_t validUsageBits = getValidUsageBits(); + if (hasIAllocatorAidl()) { + validUsageBits |= getValidUsageBits41(); + } + + if (descriptorInfo->usage & ~validUsageBits) { + ALOGE("buffer descriptor contains invalid usage bits 0x%" PRIx64, + descriptorInfo->usage & ~validUsageBits); + return BAD_VALUE; + } + + // Combinations that are only allowed with gralloc 4.1. + // Previous grallocs must be protected from this. + if (!hasIAllocatorAidl() && + descriptorInfo->format != hardware::graphics::common::V1_2::PixelFormat::BLOB && + descriptorInfo->usage & BufferUsage::GPU_DATA_BUFFER) { + ALOGE("non-BLOB pixel format with GPU_DATA_BUFFER usage is not supported prior to gralloc 4.1"); + return BAD_VALUE; + } + + return NO_ERROR; +} + +static inline status_t sBufferDescriptorInfo(std::string name, uint32_t width, uint32_t height, + PixelFormat format, uint32_t layerCount, + uint64_t usage, + IMapper::BufferDescriptorInfo* outDescriptorInfo) { + outDescriptorInfo->name = name; + outDescriptorInfo->width = width; + outDescriptorInfo->height = height; + outDescriptorInfo->layerCount = layerCount; + outDescriptorInfo->format = static_cast<hardware::graphics::common::V1_2::PixelFormat>(format); + outDescriptorInfo->usage = usage; + outDescriptorInfo->reservedSize = 0; + + return validateBufferDescriptorInfo(outDescriptorInfo); +} + } // anonymous namespace void Gralloc4Mapper::preload() { @@ -140,21 +170,6 @@ bool Gralloc4Mapper::isLoaded() const { return mMapper != nullptr; } -status_t Gralloc4Mapper::validateBufferDescriptorInfo( - IMapper::BufferDescriptorInfo* descriptorInfo) const { - uint64_t validUsageBits = getValidUsageBits(); - if (hasIAllocatorAidl()) { - validUsageBits |= getValidUsageBits41(); - } - - if (descriptorInfo->usage & ~validUsageBits) { - ALOGE("buffer descriptor contains invalid usage bits 0x%" PRIx64, - descriptorInfo->usage & ~validUsageBits); - return BAD_VALUE; - } - return NO_ERROR; -} - status_t Gralloc4Mapper::createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const { IMapper::BufferDescriptorInfo* descriptorInfo = @@ -207,8 +222,10 @@ status_t Gralloc4Mapper::validateBufferSize(buffer_handle_t bufferHandle, uint32 uint32_t layerCount, uint64_t usage, uint32_t stride) const { IMapper::BufferDescriptorInfo descriptorInfo; - sBufferDescriptorInfo("validateBufferSize", width, height, format, layerCount, usage, - &descriptorInfo); + if (auto error = sBufferDescriptorInfo("validateBufferSize", width, height, format, layerCount, + usage, &descriptorInfo) != OK) { + return error; + } auto buffer = const_cast<native_handle_t*>(bufferHandle); auto ret = mMapper->validateBufferSize(buffer, descriptorInfo, stride); @@ -427,7 +444,7 @@ int Gralloc4Mapper::unlock(buffer_handle_t bufferHandle) const { if (fd >= 0) { releaseFence = fd; } else { - ALOGD("failed to dup unlock release fence"); + ALOGW("failed to dup unlock release fence"); sync_wait(fenceHandle->data[0], -1); } } @@ -448,7 +465,12 @@ status_t Gralloc4Mapper::isSupported(uint32_t width, uint32_t height, PixelForma uint32_t layerCount, uint64_t usage, bool* outSupported) const { IMapper::BufferDescriptorInfo descriptorInfo; - sBufferDescriptorInfo("isSupported", width, height, format, layerCount, usage, &descriptorInfo); + if (auto error = sBufferDescriptorInfo("isSupported", width, height, format, layerCount, usage, + &descriptorInfo) != OK) { + // Usage isn't known to the HAL or otherwise failed validation. + *outSupported = false; + return OK; + } Error error; auto ret = mMapper->isSupported(descriptorInfo, @@ -691,7 +713,10 @@ status_t Gralloc4Mapper::getDefault(uint32_t width, uint32_t height, PixelFormat } IMapper::BufferDescriptorInfo descriptorInfo; - sBufferDescriptorInfo("getDefault", width, height, format, layerCount, usage, &descriptorInfo); + if (auto error = sBufferDescriptorInfo("getDefault", width, height, format, layerCount, usage, + &descriptorInfo) != OK) { + return error; + } hidl_vec<uint8_t> vec; Error error; @@ -1133,7 +1158,10 @@ status_t Gralloc4Allocator::allocate(std::string requestorName, uint32_t width, uint64_t usage, uint32_t bufferCount, uint32_t* outStride, buffer_handle_t* outBufferHandles, bool importBuffers) const { IMapper::BufferDescriptorInfo descriptorInfo; - sBufferDescriptorInfo(requestorName, width, height, format, layerCount, usage, &descriptorInfo); + if (auto error = sBufferDescriptorInfo(requestorName, width, height, format, layerCount, usage, + &descriptorInfo) != OK) { + return error; + } BufferDescriptor descriptor; status_t error = mMapper.createDescriptor(static_cast<void*>(&descriptorInfo), diff --git a/libs/ui/StaticDisplayInfo.cpp b/libs/ui/StaticDisplayInfo.cpp index b66b281394..03d15e4694 100644 --- a/libs/ui/StaticDisplayInfo.cpp +++ b/libs/ui/StaticDisplayInfo.cpp @@ -29,7 +29,8 @@ size_t StaticDisplayInfo::getFlattenedSize() const { return FlattenableHelpers::getFlattenedSize(connectionType) + FlattenableHelpers::getFlattenedSize(density) + FlattenableHelpers::getFlattenedSize(secure) + - FlattenableHelpers::getFlattenedSize(deviceProductInfo); + FlattenableHelpers::getFlattenedSize(deviceProductInfo) + + FlattenableHelpers::getFlattenedSize(installOrientation); } status_t StaticDisplayInfo::flatten(void* buffer, size_t size) const { @@ -40,6 +41,7 @@ status_t StaticDisplayInfo::flatten(void* buffer, size_t size) const { RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, density)); RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, secure)); RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, deviceProductInfo)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, installOrientation)); return OK; } @@ -48,6 +50,7 @@ status_t StaticDisplayInfo::unflatten(void const* buffer, size_t size) { RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &density)); RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &secure)); RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &deviceProductInfo)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &installOrientation)); return OK; } diff --git a/libs/ui/include/ui/DynamicDisplayInfo.h b/libs/ui/include/ui/DynamicDisplayInfo.h index a4c2f71a4c..ce75a65214 100644 --- a/libs/ui/include/ui/DynamicDisplayInfo.h +++ b/libs/ui/include/ui/DynamicDisplayInfo.h @@ -35,7 +35,7 @@ struct DynamicDisplayInfo : LightFlattenable<DynamicDisplayInfo> { // This struct is going to be serialized over binder, so // we can't use size_t because it may have different width // in the client process. - int32_t activeDisplayModeId; + ui::DisplayModeId activeDisplayModeId; std::vector<ui::ColorMode> supportedColorModes; ui::ColorMode activeColorMode; @@ -49,6 +49,9 @@ struct DynamicDisplayInfo : LightFlattenable<DynamicDisplayInfo> { // For more information, see the HDMI 1.4 specification. bool gameContentTypeSupported; + // The boot display mode preferred by the implementation. + ui::DisplayModeId preferredBootDisplayMode; + std::optional<ui::DisplayMode> getActiveDisplayMode() const; bool isFixedSize() const { return false; } diff --git a/libs/ui/include/ui/Gralloc4.h b/libs/ui/include/ui/Gralloc4.h index 6bafcd6c81..fe387090cc 100644 --- a/libs/ui/include/ui/Gralloc4.h +++ b/libs/ui/include/ui/Gralloc4.h @@ -155,10 +155,6 @@ public: private: friend class GraphicBufferAllocator; - // Determines whether the passed info is compatible with the mapper. - status_t validateBufferDescriptorInfo( - hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo* descriptorInfo) const; - template <class T> using DecodeFunction = status_t (*)(const hardware::hidl_vec<uint8_t>& input, T* output); diff --git a/libs/ui/include/ui/StaticDisplayInfo.h b/libs/ui/include/ui/StaticDisplayInfo.h index e86ca29a2a..cc7c869b3b 100644 --- a/libs/ui/include/ui/StaticDisplayInfo.h +++ b/libs/ui/include/ui/StaticDisplayInfo.h @@ -19,6 +19,7 @@ #include <optional> #include <ui/DeviceProductInfo.h> +#include <ui/Rotation.h> #include <utils/Flattenable.h> namespace android::ui { @@ -31,6 +32,7 @@ struct StaticDisplayInfo : LightFlattenable<StaticDisplayInfo> { float density = 0.f; bool secure = false; std::optional<DeviceProductInfo> deviceProductInfo; + Rotation installOrientation = ROTATION_0; bool isFixedSize() const { return false; } size_t getFlattenedSize() const; diff --git a/services/batteryservice/include/batteryservice/BatteryService.h b/services/batteryservice/include/batteryservice/BatteryService.h index 1e8eb1e4b3..178bc29510 100644 --- a/services/batteryservice/include/batteryservice/BatteryService.h +++ b/services/batteryservice/include/batteryservice/BatteryService.h @@ -40,6 +40,7 @@ struct BatteryProperties { bool chargerAcOnline; bool chargerUsbOnline; bool chargerWirelessOnline; + bool chargerDockOnline; int maxChargingCurrent; int maxChargingVoltage; int batteryStatus; diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index ec5832515c..22a69e5a30 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -49,6 +49,7 @@ filegroup { srcs: [ "InputClassifier.cpp", "InputClassifierConverter.cpp", + "UnwantedInteractionBlocker.cpp", "InputManager.cpp", ], } @@ -60,6 +61,7 @@ cc_defaults { "android.hardware.input.classifier@1.0", "libbase", "libbinder", + "libchrome", "libcrypto", "libcutils", "libhidlbase", @@ -76,6 +78,7 @@ cc_defaults { ], static_libs: [ "libattestation", + "libpalmrejection", ], } diff --git a/services/inputflinger/InputClassifierConverter.cpp b/services/inputflinger/InputClassifierConverter.cpp index fc8c7c39f9..b58a188a82 100644 --- a/services/inputflinger/InputClassifierConverter.cpp +++ b/services/inputflinger/InputClassifierConverter.cpp @@ -325,11 +325,6 @@ static std::vector<common::V1_0::VideoFrame> convertVideoFrames( return out; } -static uint8_t getActionIndex(int32_t action) { - return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> - AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; -} - static void getHidlPropertiesAndCoords(const NotifyMotionArgs& args, std::vector<common::V1_0::PointerProperties>* outPointerProperties, std::vector<common::V1_0::PointerCoords>* outPointerCoords) { @@ -360,7 +355,7 @@ common::V1_0::MotionEvent notifyMotionArgsToHalMotionEvent(const NotifyMotionArg event.eventTime = args.eventTime; event.deviceTimestamp = 0; event.action = getAction(args.action & AMOTION_EVENT_ACTION_MASK); - event.actionIndex = getActionIndex(args.action); + event.actionIndex = MotionEvent::getActionIndex(args.action); event.actionButton = getActionButton(args.actionButton); event.flags = getFlags(args.flags); event.policyFlags = getPolicyFlags(args.policyFlags); diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp index 158d0ebd58..73b63e3141 100644 --- a/services/inputflinger/InputListener.cpp +++ b/services/inputflinger/InputListener.cpp @@ -188,6 +188,25 @@ bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const { return true; } +std::string NotifyMotionArgs::dump() const { + std::string coords; + for (uint32_t i = 0; i < pointerCount; i++) { + if (!coords.empty()) { + coords += ", "; + } + coords += StringPrintf("{%" PRIu32 ": ", i); + coords += + StringPrintf("id=%" PRIu32 " x=%.1f y=%.1f, pressure=%.1f", pointerProperties[i].id, + pointerCoords[i].getX(), pointerCoords[i].getY(), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); + coords += "}"; + } + return StringPrintf("NotifyMotionArgs(id=%" PRId32 ", eventTime=%" PRId64 ", deviceId=%" PRId32 + ", source=%s, action=%s, pointerCount=%" PRIu32 " pointers=%s)", + id, eventTime, deviceId, inputEventSourceToString(source).c_str(), + MotionEvent::actionToString(action).c_str(), pointerCount, coords.c_str()); +} + void NotifyMotionArgs::notify(InputListenerInterface& listener) const { listener.notifyMotion(this); } diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 221e193136..7a9862d065 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -21,6 +21,7 @@ #include "InputManager.h" #include "InputDispatcherFactory.h" #include "InputReaderFactory.h" +#include "UnwantedInteractionBlocker.h" #include <binder/IPCThreadState.h> @@ -54,12 +55,17 @@ static int32_t exceptionCodeFromStatusT(status_t status) { } } +/** + * The event flow is via the "InputListener" interface, as follows: + * InputReader -> UnwantedInteractionBlocker -> InputClassifier -> InputDispatcher + */ InputManager::InputManager( const sp<InputReaderPolicyInterface>& readerPolicy, const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) { mDispatcher = createInputDispatcher(dispatcherPolicy); mClassifier = std::make_unique<InputClassifier>(*mDispatcher); - mReader = createInputReader(readerPolicy, *mClassifier); + mUnwantedInteractionBlocker = std::make_unique<UnwantedInteractionBlocker>(*mClassifier); + mReader = createInputReader(readerPolicy, *mUnwantedInteractionBlocker); } InputManager::~InputManager() { @@ -106,6 +112,10 @@ InputReaderInterface& InputManager::getReader() { return *mReader; } +UnwantedInteractionBlockerInterface& InputManager::getUnwantedInteractionBlocker() { + return *mUnwantedInteractionBlocker; +} + InputClassifierInterface& InputManager::getClassifier() { return *mClassifier; } diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index e00028364a..35d2b0fa19 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -23,6 +23,7 @@ #include "InputClassifier.h" #include "InputReaderBase.h" +#include "include/UnwantedInteractionBlockerInterface.h" #include <InputDispatcherInterface.h> #include <InputDispatcherPolicyInterface.h> @@ -46,11 +47,16 @@ class InputDispatcherThread; * The input manager has three components. * * 1. The InputReader class starts a thread that reads and preprocesses raw input events, applies - * policy, and posts messages to a queue managed by the InputClassifier. - * 2. The InputClassifier class starts a thread to communicate with the device-specific - * classifiers. It then waits on the queue of events from InputReader, applies a classification - * to them, and queues them for the InputDispatcher. - * 3. The InputDispatcher class starts a thread that waits for new events on the + * policy, and posts messages to a queue managed by the UnwantedInteractionBlocker. + * 2. The UnwantedInteractionBlocker is responsible for removing unwanted interactions. For example, + * this could be a palm on the screen. This stage would alter the event stream to remove either + * partially (some of the pointers) or fully (all touches) the unwanted interaction. The events + * are processed on the InputReader thread, without any additional queue. The events are then + * posted to the queue managed by the InputClassifier. + * 3. The InputClassifier class starts a thread to communicate with the device-specific + * classifiers. It then waits on the queue of events from UnwantedInteractionBlocker, applies + * a classification to them, and queues them for the InputDispatcher. + * 4. The InputDispatcher class starts a thread that waits for new events on the * previous queue and asynchronously dispatches them to applications. * * By design, none of these classes share any internal state. Moreover, all communication is @@ -76,6 +82,9 @@ public: /* Gets the input reader. */ virtual InputReaderInterface& getReader() = 0; + /* Gets the unwanted interaction blocker. */ + virtual UnwantedInteractionBlockerInterface& getUnwantedInteractionBlocker() = 0; + /* Gets the input classifier */ virtual InputClassifierInterface& getClassifier() = 0; @@ -96,6 +105,7 @@ public: status_t stop() override; InputReaderInterface& getReader() override; + UnwantedInteractionBlockerInterface& getUnwantedInteractionBlocker() override; InputClassifierInterface& getClassifier() override; InputDispatcherInterface& getDispatcher() override; @@ -107,6 +117,8 @@ public: private: std::unique_ptr<InputReaderInterface> mReader; + std::unique_ptr<UnwantedInteractionBlockerInterface> mUnwantedInteractionBlocker; + std::unique_ptr<InputClassifierInterface> mClassifier; std::unique_ptr<InputDispatcherInterface> mDispatcher; diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING index 3d85befaf8..9b72ff4b19 100644 --- a/services/inputflinger/TEST_MAPPING +++ b/services/inputflinger/TEST_MAPPING @@ -15,6 +15,9 @@ "name": "inputflinger_tests" }, { + "name": "libpalmrejection_test" + }, + { "name": "InputTests" }, { diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp new file mode 100644 index 0000000000..64dbb8ceb4 --- /dev/null +++ b/services/inputflinger/UnwantedInteractionBlocker.cpp @@ -0,0 +1,687 @@ +/* + * 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. + */ + +#define LOG_TAG "UnwantedInteractionBlocker" +#include "UnwantedInteractionBlocker.h" + +#include <android-base/stringprintf.h> +#include <inttypes.h> +#include <linux/input-event-codes.h> +#include <linux/input.h> +#include <server_configurable_flags/get_flags.h> + +#include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.h" +#include "ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.h" + +using android::base::StringPrintf; + +namespace android { + +// Category (=namespace) name for the input settings that are applied at boot time +static const char* INPUT_NATIVE_BOOT = "input_native_boot"; +/** + * Feature flag name. This flag determines whether palm rejection is enabled. To enable, specify + * 'true' (not case sensitive) or '1'. To disable, specify any other value. + */ +static const char* PALM_REJECTION_ENABLED = "palm_rejection_enabled"; + +static std::string toLower(std::string s) { + std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); }); + return s; +} + +static bool isFromTouchscreen(int32_t source) { + return isFromSource(source, AINPUT_SOURCE_TOUCHSCREEN); +} + +static ::base::TimeTicks toChromeTimestamp(nsecs_t eventTime) { + return ::base::TimeTicks::UnixEpoch() + + ::base::Milliseconds(static_cast<float>(ns2ms(eventTime))); +} + +/** + * Return true if palm rejection is enabled via the server configurable flags. Return false + * otherwise. + */ +static bool isPalmRejectionEnabled() { + std::string value = toLower( + server_configurable_flags::GetServerConfigurableFlag(INPUT_NATIVE_BOOT, + PALM_REJECTION_ENABLED, "false")); + if (value == "true" || value == "1") { + return true; + } + return false; +} + +static int getLinuxToolType(int32_t toolType) { + switch (toolType) { + case AMOTION_EVENT_TOOL_TYPE_FINGER: + return MT_TOOL_FINGER; + case AMOTION_EVENT_TOOL_TYPE_STYLUS: + return MT_TOOL_PEN; + case AMOTION_EVENT_TOOL_TYPE_PALM: + return MT_TOOL_PALM; + } + ALOGW("Got tool type %" PRId32 ", converting to MT_TOOL_FINGER", toolType); + return MT_TOOL_FINGER; +} + +static std::string addPrefix(std::string str, const std::string& prefix) { + std::stringstream ss; + bool newLineStarted = true; + for (const auto& ch : str) { + if (newLineStarted) { + ss << prefix; + newLineStarted = false; + } + if (ch == '\n') { + newLineStarted = true; + } + ss << ch; + } + return ss.str(); +} + +template <typename T> +static std::string dumpSet(const std::set<T>& v) { + static_assert(std::is_integral<T>::value, "Only integral types can be printed."); + std::string out; + for (const T& entry : v) { + out += out.empty() ? "{" : ", "; + out += android::base::StringPrintf("%i", entry); + } + return out.empty() ? "{}" : (out + "}"); +} + +template <typename K, typename V> +static std::string dumpMap(const std::map<K, V>& map) { + static_assert(std::is_integral<K>::value, "Keys should have integral type to be printed."); + static_assert(std::is_integral<V>::value, "Values should have integral type to be printed."); + std::string out; + for (const auto& [k, v] : map) { + if (!out.empty()) { + out += "\n"; + } + out += android::base::StringPrintf("%i : %i", static_cast<int>(k), static_cast<int>(v)); + } + return out; +} + +static std::string dumpDeviceInfo(const AndroidPalmFilterDeviceInfo& info) { + std::string out; + out += StringPrintf("max_x = %.2f\n", info.max_x); + out += StringPrintf("max_y = %.2f\n", info.max_y); + out += StringPrintf("x_res = %.2f\n", info.x_res); + out += StringPrintf("y_res = %.2f\n", info.y_res); + out += StringPrintf("major_radius_res = %.2f\n", info.major_radius_res); + out += StringPrintf("minor_radius_res = %.2f\n", info.minor_radius_res); + out += StringPrintf("minor_radius_supported = %s\n", + info.minor_radius_supported ? "true" : "false"); + out += StringPrintf("touch_major_res = %" PRId32 "\n", info.touch_major_res); + out += StringPrintf("touch_minor_res = %" PRId32 "\n", info.touch_minor_res); + return out; +} + +static int32_t getActionUpForPointerId(const NotifyMotionArgs& args, int32_t pointerId) { + for (size_t i = 0; i < args.pointerCount; i++) { + if (pointerId == args.pointerProperties[i].id) { + return AMOTION_EVENT_ACTION_POINTER_UP | + (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + } + } + LOG_ALWAYS_FATAL("Can't find pointerId %" PRId32 " in %s", pointerId, args.dump().c_str()); +} + +/** + * Find the action for individual pointer at the given pointer index. + * This is always equal to MotionEvent::getActionMasked, except for + * POINTER_UP or POINTER_DOWN events. For example, in a POINTER_UP event, the action for + * the active pointer is ACTION_POINTER_UP, while the action for the other pointers is ACTION_MOVE. + */ +static int32_t resolveActionForPointer(uint8_t pointerIndex, int32_t action) { + const int32_t actionMasked = MotionEvent::getActionMasked(action); + if (actionMasked != AMOTION_EVENT_ACTION_POINTER_DOWN && + actionMasked != AMOTION_EVENT_ACTION_POINTER_UP) { + return actionMasked; + } + // This is a POINTER_DOWN or POINTER_UP event + const uint8_t actionIndex = MotionEvent::getActionIndex(action); + if (pointerIndex == actionIndex) { + return actionMasked; + } + // When POINTER_DOWN or POINTER_UP happens, it's actually a MOVE for all of the other + // pointers + return AMOTION_EVENT_ACTION_MOVE; +} + +static const char* toString(bool value) { + return value ? "true" : "false"; +} + +std::string toString(const ::ui::InProgressTouchEvdev& touch) { + return StringPrintf("x=%.1f, y=%.1f, tracking_id=%i, slot=%zu," + " pressure=%.1f, major=%i, minor=%i, " + "tool_type=%i, altered=%s, was_touching=%s, touching=%s", + touch.x, touch.y, touch.tracking_id, touch.slot, touch.pressure, + touch.major, touch.minor, touch.tool_type, toString(touch.altered), + toString(touch.was_touching), toString(touch.touching)); +} + +/** + * Remove the data for the provided pointers from the args. The pointers are identified by their + * pointerId, not by the index inside the array. + * Return the new NotifyMotionArgs struct that has the remaining pointers. + * The only fields that may be different in the returned args from the provided args are: + * - action + * - pointerCount + * - pointerProperties + * - pointerCoords + * Action might change because it contains a pointer index. If another pointer is removed, the + * active pointer index would be shifted. + * Do not call this function for events with POINTER_UP or POINTER_DOWN events when removed pointer + * id is the acting pointer id. + * + * @param args the args from which the pointers should be removed + * @param pointerIds the pointer ids of the pointers that should be removed + */ +NotifyMotionArgs removePointerIds(const NotifyMotionArgs& args, + const std::set<int32_t>& pointerIds) { + const uint8_t actionIndex = MotionEvent::getActionIndex(args.action); + const int32_t actionMasked = MotionEvent::getActionMasked(args.action); + const bool isPointerUpOrDownAction = actionMasked == AMOTION_EVENT_ACTION_POINTER_DOWN || + actionMasked == AMOTION_EVENT_ACTION_POINTER_UP; + + NotifyMotionArgs newArgs{args}; + newArgs.pointerCount = 0; + int32_t newActionIndex = 0; + for (uint32_t i = 0; i < args.pointerCount; i++) { + const int32_t pointerId = args.pointerProperties[i].id; + if (pointerIds.find(pointerId) != pointerIds.end()) { + // skip this pointer + if (isPointerUpOrDownAction && i == actionIndex) { + // The active pointer is being removed, so the action is no longer valid. + // Set the action to 'UNKNOWN' here. The caller is responsible for updating this + // action later to a proper value. + newArgs.action = ACTION_UNKNOWN; + } + continue; + } + newArgs.pointerProperties[newArgs.pointerCount].copyFrom(args.pointerProperties[i]); + newArgs.pointerCoords[newArgs.pointerCount].copyFrom(args.pointerCoords[i]); + if (i == actionIndex) { + newActionIndex = newArgs.pointerCount; + } + newArgs.pointerCount++; + } + // Update POINTER_DOWN or POINTER_UP actions + if (isPointerUpOrDownAction && newArgs.action != ACTION_UNKNOWN) { + newArgs.action = + actionMasked | (newActionIndex << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + // Convert POINTER_DOWN and POINTER_UP to DOWN and UP if there's only 1 pointer remaining + if (newArgs.pointerCount == 1) { + if (actionMasked == AMOTION_EVENT_ACTION_POINTER_DOWN) { + newArgs.action = AMOTION_EVENT_ACTION_DOWN; + } else if (actionMasked == AMOTION_EVENT_ACTION_POINTER_UP) { + newArgs.action = AMOTION_EVENT_ACTION_UP; + } + } + } + return newArgs; +} + +std::optional<AndroidPalmFilterDeviceInfo> createPalmFilterDeviceInfo( + const InputDeviceInfo& deviceInfo) { + if (!isFromTouchscreen(deviceInfo.getSources())) { + return std::nullopt; + } + AndroidPalmFilterDeviceInfo out; + const InputDeviceInfo::MotionRange* axisX = + deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_X, AINPUT_SOURCE_TOUCHSCREEN); + if (axisX != nullptr) { + out.max_x = axisX->max; + out.x_res = axisX->resolution; + } else { + ALOGW("Palm rejection is disabled for %s because AXIS_X is not supported", + deviceInfo.getDisplayName().c_str()); + return std::nullopt; + } + const InputDeviceInfo::MotionRange* axisY = + deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_Y, AINPUT_SOURCE_TOUCHSCREEN); + if (axisY != nullptr) { + out.max_y = axisY->max; + out.y_res = axisY->resolution; + } else { + ALOGW("Palm rejection is disabled for %s because AXIS_Y is not supported", + deviceInfo.getDisplayName().c_str()); + return std::nullopt; + } + const InputDeviceInfo::MotionRange* axisMajor = + deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_TOUCH_MAJOR, AINPUT_SOURCE_TOUCHSCREEN); + if (axisMajor != nullptr) { + out.major_radius_res = axisMajor->resolution; + out.touch_major_res = axisMajor->resolution; + } else { + return std::nullopt; + } + const InputDeviceInfo::MotionRange* axisMinor = + deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_TOUCH_MINOR, AINPUT_SOURCE_TOUCHSCREEN); + if (axisMinor != nullptr) { + out.minor_radius_res = axisMinor->resolution; + out.touch_minor_res = axisMinor->resolution; + out.minor_radius_supported = true; + } else { + out.minor_radius_supported = false; + } + + return out; +} + +/** + * Synthesize CANCEL events for any new pointers that should be canceled, while removing pointers + * that have already been canceled. + * The flow of the function is as follows: + * 1. Remove all already canceled pointers + * 2. Cancel all newly suppressed pointers + * 3. Decide what to do with the current event : keep it, or drop it + * The pointers can never be "unsuppressed": once a pointer is canceled, it will never become valid. + */ +std::vector<NotifyMotionArgs> cancelSuppressedPointers( + const NotifyMotionArgs& args, const std::set<int32_t>& oldSuppressedPointerIds, + const std::set<int32_t>& newSuppressedPointerIds) { + LOG_ALWAYS_FATAL_IF(args.pointerCount == 0, "0 pointers in %s", args.dump().c_str()); + + // First, let's remove the old suppressed pointers. They've already been canceled previously. + NotifyMotionArgs oldArgs = removePointerIds(args, oldSuppressedPointerIds); + + // Cancel any newly suppressed pointers. + std::vector<NotifyMotionArgs> out; + const int32_t activePointerId = + args.pointerProperties[MotionEvent::getActionIndex(args.action)].id; + const int32_t actionMasked = MotionEvent::getActionMasked(args.action); + // We will iteratively remove pointers from 'removedArgs'. + NotifyMotionArgs removedArgs{oldArgs}; + for (uint32_t i = 0; i < oldArgs.pointerCount; i++) { + const int32_t pointerId = oldArgs.pointerProperties[i].id; + if (newSuppressedPointerIds.find(pointerId) == newSuppressedPointerIds.end()) { + // This is a pointer that should not be canceled. Move on. + continue; + } + if (pointerId == activePointerId && actionMasked == AMOTION_EVENT_ACTION_POINTER_DOWN) { + // Remove this pointer, but don't cancel it. We'll just not send the POINTER_DOWN event + removedArgs = removePointerIds(removedArgs, {pointerId}); + continue; + } + + if (removedArgs.pointerCount == 1) { + // We are about to remove the last pointer, which means there will be no more gesture + // remaining. This is identical to canceling all pointers, so just send a single CANCEL + // event, without any of the preceding POINTER_UP with FLAG_CANCELED events. + oldArgs.flags |= AMOTION_EVENT_FLAG_CANCELED; + oldArgs.action = AMOTION_EVENT_ACTION_CANCEL; + return {oldArgs}; + } + // Cancel the current pointer + out.push_back(removedArgs); + out.back().flags |= AMOTION_EVENT_FLAG_CANCELED; + out.back().action = getActionUpForPointerId(out.back(), pointerId); + + // Remove the newly canceled pointer from the args + removedArgs = removePointerIds(removedArgs, {pointerId}); + } + + // Now 'removedArgs' contains only pointers that are valid. + if (removedArgs.pointerCount <= 0 || removedArgs.action == ACTION_UNKNOWN) { + return out; + } + out.push_back(removedArgs); + return out; +} + +UnwantedInteractionBlocker::UnwantedInteractionBlocker(InputListenerInterface& listener) + : UnwantedInteractionBlocker(listener, isPalmRejectionEnabled()){}; + +UnwantedInteractionBlocker::UnwantedInteractionBlocker(InputListenerInterface& listener, + bool enablePalmRejection) + : mListener(listener), mEnablePalmRejection(enablePalmRejection) {} + +void UnwantedInteractionBlocker::notifyConfigurationChanged( + const NotifyConfigurationChangedArgs* args) { + mListener.notifyConfigurationChanged(args); +} + +void UnwantedInteractionBlocker::notifyKey(const NotifyKeyArgs* args) { + mListener.notifyKey(args); +} + +void UnwantedInteractionBlocker::notifyMotion(const NotifyMotionArgs* args) { + auto it = mPalmRejectors.find(args->deviceId); + const bool sendToPalmRejector = it != mPalmRejectors.end() && isFromTouchscreen(args->source); + if (!sendToPalmRejector) { + mListener.notifyMotion(args); + return; + } + + const std::vector<NotifyMotionArgs> newMotions = it->second.processMotion(*args); + for (const NotifyMotionArgs& newArgs : newMotions) { + mListener.notifyMotion(&newArgs); + } +} + +void UnwantedInteractionBlocker::notifySwitch(const NotifySwitchArgs* args) { + mListener.notifySwitch(args); +} + +void UnwantedInteractionBlocker::notifySensor(const NotifySensorArgs* args) { + mListener.notifySensor(args); +} + +void UnwantedInteractionBlocker::notifyVibratorState(const NotifyVibratorStateArgs* args) { + mListener.notifyVibratorState(args); +} +void UnwantedInteractionBlocker::notifyDeviceReset(const NotifyDeviceResetArgs* args) { + auto it = mPalmRejectors.find(args->deviceId); + if (it != mPalmRejectors.end()) { + AndroidPalmFilterDeviceInfo info = it->second.getPalmFilterDeviceInfo(); + // Re-create the object instead of resetting it + mPalmRejectors.erase(it); + mPalmRejectors.emplace(args->deviceId, info); + } + mListener.notifyDeviceReset(args); +} + +void UnwantedInteractionBlocker::notifyPointerCaptureChanged( + const NotifyPointerCaptureChangedArgs* args) { + mListener.notifyPointerCaptureChanged(args); +} + +void UnwantedInteractionBlocker::notifyInputDevicesChanged( + const std::vector<InputDeviceInfo>& inputDevices) { + if (!mEnablePalmRejection) { + // Palm rejection is disabled. Don't create any palm rejector objects. + return; + } + + // Let's see which of the existing devices didn't change, so that we can keep them + // and prevent event stream disruption + std::set<int32_t /*deviceId*/> devicesToKeep; + for (const InputDeviceInfo& device : inputDevices) { + std::optional<AndroidPalmFilterDeviceInfo> info = createPalmFilterDeviceInfo(device); + if (!info) { + continue; + } + + auto [it, emplaced] = mPalmRejectors.try_emplace(device.getId(), *info); + if (!emplaced && *info != it->second.getPalmFilterDeviceInfo()) { + // Re-create the PalmRejector because the device info has changed. + mPalmRejectors.erase(it); + mPalmRejectors.emplace(device.getId(), *info); + } + devicesToKeep.insert(device.getId()); + } + // Delete all devices that we don't need to keep + std::erase_if(mPalmRejectors, [&devicesToKeep](const auto& item) { + auto const& [deviceId, _] = item; + return devicesToKeep.find(deviceId) == devicesToKeep.end(); + }); +} + +void UnwantedInteractionBlocker::dump(std::string& dump) { + dump += "UnwantedInteractionBlocker:\n"; + dump += StringPrintf(" mEnablePalmRejection: %s\n", toString(mEnablePalmRejection)); + dump += StringPrintf(" isPalmRejectionEnabled (flag value): %s\n", + toString(isPalmRejectionEnabled())); + dump += mPalmRejectors.empty() ? " mPalmRejectors: None\n" : " mPalmRejectors:\n"; + for (const auto& [deviceId, palmRejector] : mPalmRejectors) { + dump += StringPrintf(" deviceId = %" PRId32 ":\n", deviceId); + dump += addPrefix(palmRejector.dump(), " "); + } +} + +void UnwantedInteractionBlocker::monitor() {} + +UnwantedInteractionBlocker::~UnwantedInteractionBlocker() {} + +void SlotState::update(const NotifyMotionArgs& args) { + for (size_t i = 0; i < args.pointerCount; i++) { + const int32_t pointerId = args.pointerProperties[i].id; + const int32_t resolvedAction = resolveActionForPointer(i, args.action); + processPointerId(pointerId, resolvedAction); + } +} + +size_t SlotState::findUnusedSlot() const { + size_t unusedSlot = 0; + // Since the collection is ordered, we can rely on the in-order traversal + for (const auto& [slot, trackingId] : mPointerIdsBySlot) { + if (unusedSlot != slot) { + break; + } + unusedSlot++; + } + return unusedSlot; +} + +void SlotState::processPointerId(int pointerId, int32_t actionMasked) { + switch (MotionEvent::getActionMasked(actionMasked)) { + case AMOTION_EVENT_ACTION_DOWN: + case AMOTION_EVENT_ACTION_POINTER_DOWN: + case AMOTION_EVENT_ACTION_HOVER_ENTER: { + // New pointer going down + size_t newSlot = findUnusedSlot(); + mPointerIdsBySlot[newSlot] = pointerId; + mSlotsByPointerId[pointerId] = newSlot; + return; + } + case AMOTION_EVENT_ACTION_MOVE: + case AMOTION_EVENT_ACTION_HOVER_MOVE: { + return; + } + case AMOTION_EVENT_ACTION_CANCEL: + case AMOTION_EVENT_ACTION_POINTER_UP: + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_HOVER_EXIT: { + auto it = mSlotsByPointerId.find(pointerId); + LOG_ALWAYS_FATAL_IF(it == mSlotsByPointerId.end()); + size_t slot = it->second; + // Erase this pointer from both collections + mPointerIdsBySlot.erase(slot); + mSlotsByPointerId.erase(pointerId); + return; + } + } + LOG_ALWAYS_FATAL("Unhandled action : %s", MotionEvent::actionToString(actionMasked).c_str()); + return; +} + +std::optional<size_t> SlotState::getSlotForPointerId(int32_t pointerId) const { + auto it = mSlotsByPointerId.find(pointerId); + if (it == mSlotsByPointerId.end()) { + return std::nullopt; + } + return it->second; +} + +std::string SlotState::dump() const { + std::string out = "mSlotsByPointerId:\n"; + out += addPrefix(dumpMap(mSlotsByPointerId), " ") + "\n"; + out += "mPointerIdsBySlot:\n"; + out += addPrefix(dumpMap(mPointerIdsBySlot), " ") + "\n"; + return out; +} + +PalmRejector::PalmRejector(const AndroidPalmFilterDeviceInfo& info, + std::unique_ptr<::ui::PalmDetectionFilter> filter) + : mSharedPalmState(std::make_unique<::ui::SharedPalmDetectionFilterState>()), + mDeviceInfo(info), + mPalmDetectionFilter(std::move(filter)) { + if (mPalmDetectionFilter != nullptr) { + // This path is used for testing. Non-testing invocations should let this constructor + // create a real PalmDetectionFilter + return; + } + std::unique_ptr<::ui::NeuralStylusPalmDetectionFilterModel> model = + std::make_unique<::ui::OneDeviceTrainNeuralStylusPalmDetectionFilterModel>( + std::vector<float>()); + mPalmDetectionFilter = + std::make_unique<::ui::NeuralStylusPalmDetectionFilter>(mDeviceInfo, std::move(model), + mSharedPalmState.get()); +} + +std::vector<::ui::InProgressTouchEvdev> getTouches(const NotifyMotionArgs& args, + const AndroidPalmFilterDeviceInfo& deviceInfo, + const SlotState& oldSlotState, + const SlotState& newSlotState) { + std::vector<::ui::InProgressTouchEvdev> touches; + + for (size_t i = 0; i < args.pointerCount; i++) { + const int32_t pointerId = args.pointerProperties[i].id; + touches.emplace_back(::ui::InProgressTouchEvdev()); + touches.back().major = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR); + touches.back().minor = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR); + touches.back().tool_type = getLinuxToolType(args.pointerProperties[i].toolType); + + // Whether there is new information for the touch. + touches.back().altered = true; + + // Whether the touch was cancelled. Touch events should be ignored till a + // new touch is initiated. + touches.back().was_cancelled = false; + + // Whether the touch is going to be canceled. + touches.back().cancelled = false; + + // Whether the touch is delayed at first appearance. Will not be reported yet. + touches.back().delayed = false; + + // Whether the touch was delayed before. + touches.back().was_delayed = false; + + // Whether the touch is held until end or no longer held. + touches.back().held = false; + + // Whether this touch was held before being sent. + touches.back().was_held = false; + + const int32_t resolvedAction = resolveActionForPointer(i, args.action); + const bool isDown = resolvedAction == AMOTION_EVENT_ACTION_POINTER_DOWN || + resolvedAction == AMOTION_EVENT_ACTION_DOWN; + touches.back().was_touching = !isDown; + + const bool isUpOrCancel = resolvedAction == AMOTION_EVENT_ACTION_CANCEL || + resolvedAction == AMOTION_EVENT_ACTION_UP || + resolvedAction == AMOTION_EVENT_ACTION_POINTER_UP; + + touches.back().x = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X); + touches.back().y = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y); + + std::optional<size_t> slot = newSlotState.getSlotForPointerId(pointerId); + if (!slot) { + slot = oldSlotState.getSlotForPointerId(pointerId); + } + LOG_ALWAYS_FATAL_IF(!slot, "Could not find slot for pointer %d", pointerId); + touches.back().slot = *slot; + touches.back().tracking_id = (!isUpOrCancel) ? pointerId : -1; + touches.back().touching = !isUpOrCancel; + + // The fields 'radius_x' and 'radius_x' are not used for palm rejection + touches.back().pressure = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE); + touches.back().tool_code = BTN_TOOL_FINGER; + // The field 'orientation' is not used for palm rejection + // The fields 'tilt_x' and 'tilt_y' are not used for palm rejection + touches.back().reported_tool_type = ::ui::EventPointerType::kTouch; + touches.back().stylus_button = false; + } + return touches; +} + +std::vector<NotifyMotionArgs> PalmRejector::processMotion(const NotifyMotionArgs& args) { + if (mPalmDetectionFilter == nullptr) { + return {args}; + } + const bool skipThisEvent = args.action == AMOTION_EVENT_ACTION_HOVER_ENTER || + args.action == AMOTION_EVENT_ACTION_HOVER_MOVE || + args.action == AMOTION_EVENT_ACTION_HOVER_EXIT || + args.action == AMOTION_EVENT_ACTION_BUTTON_PRESS || + args.action == AMOTION_EVENT_ACTION_BUTTON_RELEASE || + args.action == AMOTION_EVENT_ACTION_SCROLL; + if (skipThisEvent) { + // Lets not process hover events, button events, or scroll for now. + return {args}; + } + if (args.action == AMOTION_EVENT_ACTION_DOWN) { + mSuppressedPointerIds.clear(); + } + std::bitset<::ui::kNumTouchEvdevSlots> slotsToHold; + std::bitset<::ui::kNumTouchEvdevSlots> slotsToSuppress; + + // Store the slot state before we call getTouches and update it. This way, we can find + // the slots that have been removed due to the incoming event. + SlotState oldSlotState = mSlotState; + mSlotState.update(args); + std::vector<::ui::InProgressTouchEvdev> touches = + getTouches(args, mDeviceInfo, oldSlotState, mSlotState); + ::base::TimeTicks chromeTimestamp = toChromeTimestamp(args.eventTime); + + mPalmDetectionFilter->Filter(touches, chromeTimestamp, &slotsToHold, &slotsToSuppress); + + // Now that we know which slots should be suppressed, let's convert those to pointer id's. + std::set<int32_t> oldSuppressedIds; + std::swap(oldSuppressedIds, mSuppressedPointerIds); + for (size_t i = 0; i < args.pointerCount; i++) { + const int32_t pointerId = args.pointerProperties[i].id; + std::optional<size_t> slot = oldSlotState.getSlotForPointerId(pointerId); + if (!slot) { + slot = mSlotState.getSlotForPointerId(pointerId); + LOG_ALWAYS_FATAL_IF(!slot, "Could not find slot for pointer id %" PRId32, pointerId); + } + if (slotsToSuppress.test(*slot)) { + mSuppressedPointerIds.insert(pointerId); + } + } + + std::vector<NotifyMotionArgs> argsWithoutUnwantedPointers = + cancelSuppressedPointers(args, oldSuppressedIds, mSuppressedPointerIds); + for (const NotifyMotionArgs& checkArgs : argsWithoutUnwantedPointers) { + LOG_ALWAYS_FATAL_IF(checkArgs.action == ACTION_UNKNOWN, "%s", checkArgs.dump().c_str()); + } + + if (mSuppressedPointerIds != oldSuppressedIds) { + if (argsWithoutUnwantedPointers.size() != 1 || + argsWithoutUnwantedPointers[0].pointerCount != args.pointerCount) { + ALOGI("Palm detected, removing pointer ids %s from %s", + dumpSet(mSuppressedPointerIds).c_str(), args.dump().c_str()); + } + } + + return argsWithoutUnwantedPointers; +} + +const AndroidPalmFilterDeviceInfo& PalmRejector::getPalmFilterDeviceInfo() { + return mDeviceInfo; +} + +std::string PalmRejector::dump() const { + std::string out; + out += "mDeviceInfo:\n"; + out += addPrefix(dumpDeviceInfo(mDeviceInfo), " "); + out += "mSlotState:\n"; + out += addPrefix(mSlotState.dump(), " "); + out += "mSuppressedPointerIds: "; + out += dumpSet(mSuppressedPointerIds) + "\n"; + return out; +} + +} // namespace android diff --git a/services/inputflinger/UnwantedInteractionBlocker.h b/services/inputflinger/UnwantedInteractionBlocker.h new file mode 100644 index 0000000000..14068fd878 --- /dev/null +++ b/services/inputflinger/UnwantedInteractionBlocker.h @@ -0,0 +1,153 @@ +/* + * 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. + */ + +#pragma once + +#include <map> +#include <set> + +#include "include/UnwantedInteractionBlockerInterface.h" +#include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_util.h" +#include "ui/events/ozone/evdev/touch_filter/palm_detection_filter.h" + +namespace android { + +// --- Functions for manipulation of event streams + +struct AndroidPalmFilterDeviceInfo : ::ui::PalmFilterDeviceInfo { + // Additional fields from 'TouchEventConverterEvdev', added here for convenience + int32_t touch_major_res = 1; // info.GetAbsInfoByCode(ABS_MT_TOUCH_MAJOR).resolution; + int32_t touch_minor_res = 1; // info.GetAbsInfoByCode(ABS_MT_TOUCH_MINOR).resolution; + + auto operator<=>(const AndroidPalmFilterDeviceInfo&) const = default; +}; + +std::optional<AndroidPalmFilterDeviceInfo> createPalmFilterDeviceInfo( + const InputDeviceInfo& deviceInfo); + +static constexpr int32_t ACTION_UNKNOWN = -1; + +NotifyMotionArgs removePointerIds(const NotifyMotionArgs& args, + const std::set<int32_t>& pointerIds); + +std::vector<NotifyMotionArgs> cancelSuppressedPointers( + const NotifyMotionArgs& args, const std::set<int32_t>& oldSuppressedPointerIds, + const std::set<int32_t>& newSuppressedPointerIds); + +std::string toString(const ::ui::InProgressTouchEvdev& touch); + +// --- Main classes and interfaces --- + +class PalmRejector; + +// --- Implementations --- + +/** + * Implementation of the UnwantedInteractionBlockerInterface. + * Represents a separate stage of input processing. All of the input events go through this stage. + * Acts as a passthrough for all input events except for motion events. + * + * The events of motion type are sent to PalmRejectors. PalmRejectors detect unwanted touches, + * and emit input streams with the bad pointers removed. + */ +class UnwantedInteractionBlocker : public UnwantedInteractionBlockerInterface { +public: + explicit UnwantedInteractionBlocker(InputListenerInterface& listener); + explicit UnwantedInteractionBlocker(InputListenerInterface& listener, bool enablePalmRejection); + + void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; + void notifyKey(const NotifyKeyArgs* args) override; + void notifyMotion(const NotifyMotionArgs* args) override; + void notifySwitch(const NotifySwitchArgs* args) override; + void notifySensor(const NotifySensorArgs* args) override; + void notifyVibratorState(const NotifyVibratorStateArgs* args) override; + void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; + void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override; + + void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override; + void dump(std::string& dump) override; + void monitor() override; + + ~UnwantedInteractionBlocker(); + +private: + // The next stage to pass input events to + InputListenerInterface& mListener; + const bool mEnablePalmRejection; + + // Detect and reject unwanted palms on screen + // Use a separate palm rejector for every touch device. + std::map<int32_t /*deviceId*/, PalmRejector> mPalmRejectors; +}; + +class SlotState { +public: + /** + * Update the state using the new information provided in the NotifyMotionArgs + */ + void update(const NotifyMotionArgs& args); + std::optional<size_t> getSlotForPointerId(int32_t pointerId) const; + std::string dump() const; + +private: + // Process a pointer with the provided action, and return the slot associated with it + void processPointerId(int32_t pointerId, int32_t action); + // The map from tracking id to slot state. Since the PalmRejectionFilter works close to the + // evdev level, the only way to tell it about UP or CANCEL events is by sending tracking id = -1 + // to the appropriate touch slot. So we need to reconstruct the original slot. + // The two collections below must always be in-sync. + // Use std::map instead of std::unordered_map because we rely on these collections being + // ordered. It also has better space efficiency than unordered_map because we only have a few + // pointers most of the time. + std::map<int32_t /*pointerId*/, size_t /*slot*/> mSlotsByPointerId; + std::map<size_t /*slot*/, int32_t /*pointerId */> mPointerIdsBySlot; + + size_t findUnusedSlot() const; +}; + +/** + * Convert an Android event to a linux-like 'InProgressTouchEvdev'. The provided SlotState's + * are used to figure out which slot does each pointer belong to. + */ +std::vector<::ui::InProgressTouchEvdev> getTouches(const NotifyMotionArgs& args, + const AndroidPalmFilterDeviceInfo& deviceInfo, + const SlotState& oldSlotState, + const SlotState& newSlotState); + +class PalmRejector { +public: + explicit PalmRejector(const AndroidPalmFilterDeviceInfo& info, + std::unique_ptr<::ui::PalmDetectionFilter> filter = nullptr); + std::vector<NotifyMotionArgs> processMotion(const NotifyMotionArgs& args); + + // Get the device info of this device, for comparison purposes + const AndroidPalmFilterDeviceInfo& getPalmFilterDeviceInfo(); + std::string dump() const; + +private: + PalmRejector(const PalmRejector&) = delete; + PalmRejector& operator=(const PalmRejector&) = delete; + + std::unique_ptr<::ui::SharedPalmDetectionFilterState> mSharedPalmState; + AndroidPalmFilterDeviceInfo mDeviceInfo; + std::unique_ptr<::ui::PalmDetectionFilter> mPalmDetectionFilter; + std::set<int32_t> mSuppressedPointerIds; + + // Used to help convert an Android touch stream to Linux input stream. + SlotState mSlotState; +}; + +} // namespace android diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 441a1de710..aa3643caef 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -120,7 +120,8 @@ constexpr nsecs_t APP_SWITCH_TIMEOUT = 500 * 1000000LL; // 0.5sec // Amount of time to allow for an event to be dispatched (measured since its eventTime) // before considering it stale and dropping it. -constexpr nsecs_t STALE_EVENT_TIMEOUT = 10000 * 1000000LL; // 10sec +const nsecs_t STALE_EVENT_TIMEOUT = 10000 * 1000000LL // 10sec + * HwTimeoutMultiplier(); // Log a warning when an event takes longer than this to process, even if an ANR does not occur. constexpr nsecs_t SLOW_EVENT_PROCESSING_WARNING_TIMEOUT = 2000 * 1000000LL; // 2sec @@ -215,7 +216,7 @@ bool validateMotionEvent(int32_t action, int32_t actionButton, size_t pointerCou return false; } if (pointerCount < 1 || pointerCount > MAX_POINTERS) { - ALOGE("Motion event has invalid pointer count %zu; value must be between 1 and %d.", + ALOGE("Motion event has invalid pointer count %zu; value must be between 1 and %zu.", pointerCount, MAX_POINTERS); return false; } @@ -547,6 +548,7 @@ InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& polic mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX), mNextUnblockedEvent(nullptr), + mMonitorDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT), mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false), @@ -706,8 +708,13 @@ nsecs_t InputDispatcher::processAnrsLocked() { return LONG_LONG_MIN; } -std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked(const sp<IBinder>& token) { - sp<WindowInfoHandle> window = getWindowHandleLocked(token); +std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked( + const sp<Connection>& connection) { + if (connection->monitor) { + return mMonitorDispatchingTimeout; + } + const sp<WindowInfoHandle> window = + getWindowHandleLocked(connection->inputChannel->getConnectionToken()); if (window != nullptr) { return window->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); } @@ -3204,8 +3211,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, while (connection->status == Connection::Status::NORMAL && !connection->outboundQueue.empty()) { DispatchEntry* dispatchEntry = connection->outboundQueue.front(); dispatchEntry->deliveryTime = currentTime; - const std::chrono::nanoseconds timeout = - getDispatchingTimeoutLocked(connection->inputChannel->getConnectionToken()); + const std::chrono::nanoseconds timeout = getDispatchingTimeoutLocked(connection); dispatchEntry->timeoutTime = currentTime + timeout.count(); // Publish the event. @@ -6399,4 +6405,9 @@ void InputDispatcher::cancelCurrentTouch() { mLooper->wake(); } +void InputDispatcher::setMonitorDispatchingTimeoutForTest(std::chrono::nanoseconds timeout) { + std::scoped_lock _l(mLock); + mMonitorDispatchingTimeout = timeout; +} + } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 756483904e..bff6cac149 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -148,6 +148,9 @@ public: void cancelCurrentTouch() override; + // Public to allow tests to verify that a Monitor can get ANR. + void setMonitorDispatchingTimeoutForTest(std::chrono::nanoseconds timeout); + private: enum class DropReason { NOT_DROPPED, @@ -324,8 +327,12 @@ private: bool runCommandsLockedInterruptable() REQUIRES(mLock); void postCommandLocked(Command&& command) REQUIRES(mLock); + // The dispatching timeout to use for Monitors. + std::chrono::nanoseconds mMonitorDispatchingTimeout GUARDED_BY(mLock); + nsecs_t processAnrsLocked() REQUIRES(mLock); - std::chrono::nanoseconds getDispatchingTimeoutLocked(const sp<IBinder>& token) REQUIRES(mLock); + std::chrono::nanoseconds getDispatchingTimeoutLocked(const sp<Connection>& connection) + REQUIRES(mLock); // Input filter processing. bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock); diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h index db6310478c..dff58949a7 100644 --- a/services/inputflinger/include/InputListener.h +++ b/services/inputflinger/include/InputListener.h @@ -142,6 +142,8 @@ struct NotifyMotionArgs : public NotifyArgs { bool operator==(const NotifyMotionArgs& rhs) const; void notify(InputListenerInterface& listener) const override; + + std::string dump() const; }; /* Describes a sensor event. */ diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 1aab856205..41ecef365d 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -17,6 +17,7 @@ #ifndef _UI_INPUT_READER_BASE_H #define _UI_INPUT_READER_BASE_H +#include <android/os/IInputConstants.h> #include <input/DisplayViewport.h> #include <input/Input.h> #include <input/InputDevice.h> @@ -87,6 +88,8 @@ public: virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) = 0; + virtual int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const = 0; + /* Toggle Caps Lock */ virtual void toggleCapsLockState(int32_t deviceId) = 0; @@ -286,7 +289,10 @@ struct InputReaderConfiguration { InputReaderConfiguration() : virtualKeyQuietTime(0), - pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, 3.0f), + pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, + static_cast<float>( + android::os::IInputConstants:: + DEFAULT_POINTER_ACCELERATION)), wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f), pointerGesturesEnabled(true), pointerGestureQuietInterval(100 * 1000000LL), // 100 ms diff --git a/services/inputflinger/include/UnwantedInteractionBlockerInterface.h b/services/inputflinger/include/UnwantedInteractionBlockerInterface.h new file mode 100644 index 0000000000..2327266563 --- /dev/null +++ b/services/inputflinger/include/UnwantedInteractionBlockerInterface.h @@ -0,0 +1,53 @@ +/* + * 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. + */ + +#pragma once + +#include "InputListener.h" + +namespace android { + +/** + * Base interface for an InputListener stage. + * Blocks unintentional input events. Not thread safe. Must be called from the same + * thread. All work is performed on the calling threads. + */ +class UnwantedInteractionBlockerInterface : public InputListenerInterface { +public: + /* Notifies the input reader policy that some input devices have changed + * and provides information about all current input devices. + * Important! This call should happen on the same thread as the calls to the + * InputListenerInterface methods. + * That is, same thread should call 'notifyMotion' and 'notifyInputDevicesChanged' and + * 'notifyDeviceReset'. If this architecture changes, we will need to make the implementation + * of this interface thread-safe. + */ + virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) = 0; + + /** + * Dump the state of the interaction blocker. + * This method may be called on any thread (usually by the input manager). + */ + virtual void dump(std::string& dump) = 0; + + /* Called by the heatbeat to ensures that the dispatcher has not deadlocked. */ + virtual void monitor() = 0; + + UnwantedInteractionBlockerInterface() {} + ~UnwantedInteractionBlockerInterface() override {} +}; + +} // namespace android diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index 09a62f35cd..db67877cc9 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -880,6 +880,42 @@ int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const { return AKEY_STATE_UNKNOWN; } +int32_t EventHub::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const { + std::scoped_lock _l(mLock); + + Device* device = getDeviceLocked(deviceId); + if (device == nullptr || !device->hasValidFd() || device->keyMap.keyCharacterMap == nullptr || + device->keyMap.keyLayoutMap == nullptr) { + return AKEYCODE_UNKNOWN; + } + std::vector<int32_t> scanCodes; + device->keyMap.keyLayoutMap->findScanCodesForKey(locationKeyCode, &scanCodes); + if (scanCodes.empty()) { + ALOGW("Failed to get key code for key location: no scan code maps to key code %d for input" + "device %d", + locationKeyCode, deviceId); + return AKEYCODE_UNKNOWN; + } + if (scanCodes.size() > 1) { + ALOGW("Multiple scan codes map to the same key code %d, returning only the first match", + locationKeyCode); + } + int32_t outKeyCode; + status_t mapKeyRes = + device->getKeyCharacterMap()->mapKey(scanCodes[0], 0 /*usageCode*/, &outKeyCode); + switch (mapKeyRes) { + case OK: + return outKeyCode; + case NAME_NOT_FOUND: + // key character map doesn't re-map this scanCode, hence the keyCode remains the same + return locationKeyCode; + default: + ALOGW("Failed to get key code for key location: Key character map returned error %s", + statusToString(mapKeyRes).c_str()); + return AKEYCODE_UNKNOWN; + } +} + int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const { if (sw >= 0 && sw <= SW_MAX) { std::scoped_lock _l(mLock); diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index 7cff6728f2..a050963fef 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -309,15 +309,15 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config const auto& displayPort = ports.find(inputPort); if (displayPort != ports.end()) { mAssociatedDisplayPort = std::make_optional(displayPort->second); + } else { + const std::unordered_map<std::string, std::string>& displayUniqueIds = + config->uniqueIdAssociations; + const auto& displayUniqueId = displayUniqueIds.find(inputPort); + if (displayUniqueId != displayUniqueIds.end()) { + mAssociatedDisplayUniqueId = displayUniqueId->second; + } } } - const std::string& inputDeviceName = mIdentifier.name; - const std::unordered_map<std::string, std::string>& names = - config->uniqueIdAssociations; - const auto& displayUniqueId = names.find(inputDeviceName); - if (displayUniqueId != names.end()) { - mAssociatedDisplayUniqueId = displayUniqueId->second; - } // If the device was explicitly disabled by the user, it would be present in the // "disabledDevices" list. If it is associated with a specific display, and it was not @@ -338,7 +338,7 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config if (!mAssociatedViewport) { ALOGW("Input device %s should be associated with display %s but the " "corresponding viewport cannot be found", - inputDeviceName.c_str(), mAssociatedDisplayUniqueId->c_str()); + getName().c_str(), mAssociatedDisplayUniqueId->c_str()); enabled = false; } } @@ -475,6 +475,23 @@ bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, return result; } +int32_t InputDevice::getKeyCodeForKeyLocation(int32_t locationKeyCode) const { + std::optional<int32_t> result = first_in_mappers<int32_t>( + [locationKeyCode](const InputMapper& mapper) -> std::optional<int32_t> const { + if (sourcesMatchMask(mapper.getSources(), AINPUT_SOURCE_KEYBOARD)) { + return std::make_optional(mapper.getKeyCodeForKeyLocation(locationKeyCode)); + } + return std::nullopt; + }); + if (!result) { + ALOGE("Failed to get key code for key location: No matching InputMapper with source mask " + "KEYBOARD found. The provided input device with id %d has sources %s.", + getId(), inputEventSourceToString(getSources()).c_str()); + return AKEYCODE_UNKNOWN; + } + return *result; +} + void InputDevice::vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token) { for_each_mapper([sequence, repeat, token](InputMapper& mapper) { mapper.vibrate(sequence, repeat, token); diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 564db56ca9..31d331bb98 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -303,7 +303,7 @@ void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEven device->process(rawEvents, count); } -InputDevice* InputReader::findInputDeviceLocked(int32_t deviceId) { +InputDevice* InputReader::findInputDeviceLocked(int32_t deviceId) const { auto deviceIt = std::find_if(mDevices.begin(), mDevices.end(), [deviceId](const auto& devicePair) { return devicePair.second->getId() == deviceId; @@ -589,6 +589,18 @@ bool InputReader::markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceM return result; } +int32_t InputReader::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const { + std::scoped_lock _l(mLock); + + InputDevice* device = findInputDeviceLocked(deviceId); + if (device == nullptr) { + ALOGW("Failed to get key code for key location: Input device with id %d not found", + deviceId); + return AKEYCODE_UNKNOWN; + } + return device->getKeyCodeForKeyLocation(locationKeyCode); +} + void InputReader::requestRefreshConfiguration(uint32_t changes) { std::scoped_lock _l(mLock); diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h index 1f96294e96..18e912db75 100644 --- a/services/inputflinger/reader/include/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -306,6 +306,7 @@ public: virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const = 0; virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const = 0; + virtual int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const = 0; /* * Examine key input devices for specific framework keycode support @@ -482,6 +483,8 @@ public: int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override final; int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override final; int32_t getSwitchState(int32_t deviceId, int32_t sw) const override final; + int32_t getKeyCodeForKeyLocation(int32_t deviceId, + int32_t locationKeyCode) const override final; status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const override final; diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index 518aaa0490..694daa99ce 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -61,6 +61,9 @@ public: inline std::optional<uint8_t> getAssociatedDisplayPort() const { return mAssociatedDisplayPort; } + inline std::optional<std::string> getAssociatedDisplayUniqueId() const { + return mAssociatedDisplayUniqueId; + } inline std::optional<DisplayViewport> getAssociatedViewport() const { return mAssociatedViewport; } @@ -84,6 +87,7 @@ public: int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); + int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const; bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags); void vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token); @@ -216,7 +220,8 @@ private: // return the first value returned by a function over every mapper. // if all mappers return nullopt, return nullopt. template <typename T> - inline std::optional<T> first_in_mappers(std::function<std::optional<T>(InputMapper&)> f) { + inline std::optional<T> first_in_mappers( + std::function<std::optional<T>(InputMapper&)> f) const { for (auto& deviceEntry : mDevices) { auto& devicePair = deviceEntry.second; auto& mappers = devicePair.second; @@ -312,6 +317,9 @@ public: inline int32_t getKeyCodeState(int32_t keyCode) const { return mEventHub->getKeyCodeState(mId, keyCode); } + inline int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const { + return mEventHub->getKeyCodeForKeyLocation(mId, locationKeyCode); + } inline int32_t getSwitchState(int32_t sw) const { return mEventHub->getSwitchState(mId, sw); } inline status_t getAbsoluteAxisValue(int32_t code, int32_t* outValue) const { return mEventHub->getAbsoluteAxisValue(mId, code, outValue); @@ -381,6 +389,9 @@ public: inline std::optional<uint8_t> getAssociatedDisplayPort() const { return mDevice.getAssociatedDisplayPort(); } + inline std::optional<std::string> getAssociatedDisplayUniqueId() const { + return mDevice.getAssociatedDisplayUniqueId(); + } inline std::optional<DisplayViewport> getAssociatedViewport() const { return mDevice.getAssociatedViewport(); } diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index fb1d16695d..daeaa1dbe3 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -69,6 +69,8 @@ public: int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) override; int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override; + int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override; + void toggleCapsLockState(int32_t deviceId) override; bool hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, @@ -239,7 +241,7 @@ private: const int32_t* keyCodes, uint8_t* outFlags) REQUIRES(mLock); // find an InputDevice from an InputDevice id - InputDevice* findInputDeviceLocked(int32_t deviceId) REQUIRES(mLock); + InputDevice* findInputDeviceLocked(int32_t deviceId) const REQUIRES(mLock); }; } // namespace android diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index fcb56ef05c..dc5fcec1df 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -66,7 +66,7 @@ CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext) CursorInputMapper::~CursorInputMapper() {} -uint32_t CursorInputMapper::getSources() { +uint32_t CursorInputMapper::getSources() const { return mSource; } diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h index 9a8ca01294..c84c6c4229 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.h +++ b/services/inputflinger/reader/mapper/CursorInputMapper.h @@ -56,7 +56,7 @@ public: explicit CursorInputMapper(InputDeviceContext& deviceContext); virtual ~CursorInputMapper(); - virtual uint32_t getSources() override; + virtual uint32_t getSources() const override; virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; virtual void dump(std::string& dump) override; virtual void configure(nsecs_t when, const InputReaderConfiguration* config, diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp index 37d1d7499c..6b5d37f8d5 100644 --- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp +++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp @@ -26,7 +26,7 @@ namespace android { ExternalStylusInputMapper::ExternalStylusInputMapper(InputDeviceContext& deviceContext) : InputMapper(deviceContext) {} -uint32_t ExternalStylusInputMapper::getSources() { +uint32_t ExternalStylusInputMapper::getSources() const { return AINPUT_SOURCE_STYLUS; } diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h index 1d42b30fe2..516aa51a14 100644 --- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h +++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h @@ -30,7 +30,7 @@ public: explicit ExternalStylusInputMapper(InputDeviceContext& deviceContext); virtual ~ExternalStylusInputMapper() = default; - virtual uint32_t getSources() override; + virtual uint32_t getSources() const override; virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; virtual void dump(std::string& dump) override; virtual void configure(nsecs_t when, const InputReaderConfiguration* config, diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp index b9aef54ead..7b185e029c 100644 --- a/services/inputflinger/reader/mapper/InputMapper.cpp +++ b/services/inputflinger/reader/mapper/InputMapper.cpp @@ -51,6 +51,10 @@ int32_t InputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { return AKEY_STATE_UNKNOWN; } +int32_t InputMapper::getKeyCodeForKeyLocation(int32_t locationKeyCode) const { + return AKEYCODE_UNKNOWN; +} + bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) { return false; diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h index d83d74df7b..fce6409b3f 100644 --- a/services/inputflinger/reader/mapper/InputMapper.h +++ b/services/inputflinger/reader/mapper/InputMapper.h @@ -45,12 +45,13 @@ public: inline int32_t getDeviceId() { return mDeviceContext.getId(); } inline InputDeviceContext& getDeviceContext() { return mDeviceContext; } + inline InputDeviceContext& getDeviceContext() const { return mDeviceContext; }; inline const std::string getDeviceName() const { return mDeviceContext.getName(); } inline InputReaderContext* getContext() { return mDeviceContext.getContext(); } inline InputReaderPolicyInterface* getPolicy() { return getContext()->getPolicy(); } inline InputListenerInterface& getListener() { return getContext()->getListener(); } - virtual uint32_t getSources() = 0; + virtual uint32_t getSources() const = 0; virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); virtual void dump(std::string& dump); virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); @@ -61,6 +62,8 @@ public: virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); + virtual int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const; + virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags); virtual void vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token); diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp index ad11b05576..a8e9baeb80 100644 --- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp +++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp @@ -25,7 +25,7 @@ JoystickInputMapper::JoystickInputMapper(InputDeviceContext& deviceContext) JoystickInputMapper::~JoystickInputMapper() {} -uint32_t JoystickInputMapper::getSources() { +uint32_t JoystickInputMapper::getSources() const { return AINPUT_SOURCE_JOYSTICK; } diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h index bba95addc4..307bf5b38b 100644 --- a/services/inputflinger/reader/mapper/JoystickInputMapper.h +++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h @@ -26,7 +26,7 @@ public: explicit JoystickInputMapper(InputDeviceContext& deviceContext); virtual ~JoystickInputMapper(); - virtual uint32_t getSources() override; + virtual uint32_t getSources() const override; virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; virtual void dump(std::string& dump) override; virtual void configure(nsecs_t when, const InputReaderConfiguration* config, diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index a8602a4c02..1d63c0ebab 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -95,7 +95,7 @@ KeyboardInputMapper::KeyboardInputMapper(InputDeviceContext& deviceContext, uint KeyboardInputMapper::~KeyboardInputMapper() {} -uint32_t KeyboardInputMapper::getSources() { +uint32_t KeyboardInputMapper::getSources() const { return mSource; } @@ -375,6 +375,10 @@ int32_t KeyboardInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanC return getDeviceContext().getScanCodeState(scanCode); } +int32_t KeyboardInputMapper::getKeyCodeForKeyLocation(int32_t locationKeyCode) const { + return getDeviceContext().getKeyCodeForKeyLocation(locationKeyCode); +} + bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) { return getDeviceContext().markSupportedKeyCodes(numCodes, keyCodes, outFlags); diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h index fc92320773..378769639e 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h @@ -26,7 +26,7 @@ public: KeyboardInputMapper(InputDeviceContext& deviceContext, uint32_t source, int32_t keyboardType); virtual ~KeyboardInputMapper(); - virtual uint32_t getSources() override; + virtual uint32_t getSources() const override; virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; virtual void dump(std::string& dump) override; virtual void configure(nsecs_t when, const InputReaderConfiguration* config, @@ -38,6 +38,7 @@ public: virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override; virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) override; + virtual int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const override; virtual int32_t getMetaState() override; virtual bool updateMetaState(int32_t keyCode) override; diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp index 4bd1cd8d12..ff3a592c0f 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp @@ -282,7 +282,7 @@ void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { if (outCount >= MAX_POINTERS) { if (DEBUG_POINTERS) { - ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; " + ALOGD("MultiTouch device %s emitted more than maximum of %zu pointers; " "ignoring the rest.", getDeviceName().c_str(), MAX_POINTERS); } diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp index b83a8fcf4e..eca25f6e6a 100644 --- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp @@ -31,7 +31,7 @@ RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDeviceContext& deviceCon RotaryEncoderInputMapper::~RotaryEncoderInputMapper() {} -uint32_t RotaryEncoderInputMapper::getSources() { +uint32_t RotaryEncoderInputMapper::getSources() const { return mSource; } diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h index e0c94040f0..1859355a93 100644 --- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h @@ -27,7 +27,7 @@ public: explicit RotaryEncoderInputMapper(InputDeviceContext& deviceContext); virtual ~RotaryEncoderInputMapper(); - virtual uint32_t getSources() override; + virtual uint32_t getSources() const override; virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; virtual void dump(std::string& dump) override; virtual void configure(nsecs_t when, const InputReaderConfiguration* config, diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp index 677a372997..b01c2bc13f 100644 --- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp @@ -57,7 +57,7 @@ SensorInputMapper::SensorInputMapper(InputDeviceContext& deviceContext) SensorInputMapper::~SensorInputMapper() {} -uint32_t SensorInputMapper::getSources() { +uint32_t SensorInputMapper::getSources() const { return AINPUT_SOURCE_SENSOR; } diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.h b/services/inputflinger/reader/mapper/SensorInputMapper.h index 1797fe3c71..27a61771f2 100644 --- a/services/inputflinger/reader/mapper/SensorInputMapper.h +++ b/services/inputflinger/reader/mapper/SensorInputMapper.h @@ -28,7 +28,7 @@ public: explicit SensorInputMapper(InputDeviceContext& deviceContext); ~SensorInputMapper() override; - uint32_t getSources() override; + uint32_t getSources() const override; void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; void dump(std::string& dump) override; void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) override; diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp index 3237824994..ebb5de66ed 100644 --- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp @@ -25,7 +25,7 @@ SwitchInputMapper::SwitchInputMapper(InputDeviceContext& deviceContext) SwitchInputMapper::~SwitchInputMapper() {} -uint32_t SwitchInputMapper::getSources() { +uint32_t SwitchInputMapper::getSources() const { return AINPUT_SOURCE_SWITCH; } diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.h b/services/inputflinger/reader/mapper/SwitchInputMapper.h index 4d74163069..64b9aa27b4 100644 --- a/services/inputflinger/reader/mapper/SwitchInputMapper.h +++ b/services/inputflinger/reader/mapper/SwitchInputMapper.h @@ -26,7 +26,7 @@ public: explicit SwitchInputMapper(InputDeviceContext& deviceContext); virtual ~SwitchInputMapper(); - virtual uint32_t getSources() override; + virtual uint32_t getSources() const override; virtual void process(const RawEvent* rawEvent) override; virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode) override; diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 0a5de279dc..f729ba958a 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -178,7 +178,7 @@ TouchInputMapper::TouchInputMapper(InputDeviceContext& deviceContext) TouchInputMapper::~TouchInputMapper() {} -uint32_t TouchInputMapper::getSources() { +uint32_t TouchInputMapper::getSources() const { return mSource; } @@ -565,6 +565,12 @@ std::optional<DisplayViewport> TouchInputMapper::findViewport() { return getDeviceContext().getAssociatedViewport(); } + const std::optional<std::string> associatedDisplayUniqueId = + getDeviceContext().getAssociatedDisplayUniqueId(); + if (associatedDisplayUniqueId) { + return getDeviceContext().getAssociatedViewport(); + } + if (mDeviceMode == DeviceMode::POINTER) { std::optional<DisplayViewport> viewport = mConfig.getDisplayViewportById(mConfig.defaultPointerDisplayId); diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index 9fd30e40f3..c948f565d9 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -138,7 +138,7 @@ public: explicit TouchInputMapper(InputDeviceContext& deviceContext); ~TouchInputMapper() override; - uint32_t getSources() override; + uint32_t getSources() const override; void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; void dump(std::string& dump) override; void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) override; diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp index 1976fedb2a..33db527db1 100644 --- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp @@ -25,7 +25,7 @@ VibratorInputMapper::VibratorInputMapper(InputDeviceContext& deviceContext) VibratorInputMapper::~VibratorInputMapper() {} -uint32_t VibratorInputMapper::getSources() { +uint32_t VibratorInputMapper::getSources() const { return 0; } diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h index 7ce621ac73..d3c22b60a8 100644 --- a/services/inputflinger/reader/mapper/VibratorInputMapper.h +++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h @@ -26,7 +26,7 @@ public: explicit VibratorInputMapper(InputDeviceContext& deviceContext); virtual ~VibratorInputMapper(); - virtual uint32_t getSources() override; + virtual uint32_t getSources() const override; virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; virtual void process(const RawEvent* rawEvent) override; diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index e68692474d..9d200bdeff 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -49,6 +49,7 @@ cc_test { "LatencyTracker_test.cpp", "TestInputListener.cpp", "UinputDevice.cpp", + "UnwantedInteractionBlocker_test.cpp", ], aidl: { include_dirs: [ @@ -57,7 +58,7 @@ cc_test { ], }, static_libs: [ - "libc++fs" + "libc++fs", ], require_root: true, test_suites: ["device-tests"], diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 0814bc2be8..f97a9ec8bc 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -17,6 +17,7 @@ #include "../dispatcher/InputDispatcher.h" #include <android-base/properties.h> +#include <android-base/silent_death_test.h> #include <android-base/stringprintf.h> #include <android-base/thread_annotations.h> #include <binder/Binder.h> @@ -558,7 +559,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { MotionEvent event; PointerProperties pointerProperties[MAX_POINTERS + 1]; PointerCoords pointerCoords[MAX_POINTERS + 1]; - for (int i = 0; i <= MAX_POINTERS; i++) { + for (size_t i = 0; i <= MAX_POINTERS; i++) { pointerProperties[i].clear(); pointerProperties[i].id = i; pointerCoords[i].clear(); @@ -2766,9 +2767,10 @@ TEST_F(InputDispatcherTest, SendTimeline_DoesNotCrashDispatcher) { class FakeMonitorReceiver { public: FakeMonitorReceiver(const std::unique_ptr<InputDispatcher>& dispatcher, const std::string name, - int32_t displayId, bool isGestureMonitor = false) { + int32_t displayId) { base::Result<std::unique_ptr<InputChannel>> channel = - dispatcher->createInputMonitor(displayId, isGestureMonitor, name, MONITOR_PID); + dispatcher->createInputMonitor(displayId, false /*isGestureMonitor*/, name, + MONITOR_PID); mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name); } @@ -2829,6 +2831,8 @@ private: std::unique_ptr<FakeInputReceiver> mInputReceiver; }; +using InputDispatcherMonitorTest = InputDispatcherTest; + /** * Two entities that receive touch: A window, and a global monitor. * The touch goes to the window, and then the window disappears. @@ -2837,14 +2841,12 @@ private: * 1. foregroundWindow * 2. monitor <-- global monitor (doesn't observe z order, receives all events) */ -TEST_F(InputDispatcherTest, WhenForegroundWindowDisappears_GlobalMonitorTouchIsCanceled) { +TEST_F(InputDispatcherMonitorTest, MonitorTouchIsCanceledWhenForegroundWindowDisappears) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT); - FakeMonitorReceiver monitor = - FakeMonitorReceiver(mDispatcher, "GlobalMonitor", ADISPLAY_ID_DEFAULT, - false /*isGestureMonitor*/); + FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, @@ -2880,15 +2882,13 @@ TEST_F(InputDispatcherTest, WhenForegroundWindowDisappears_GlobalMonitorTouchIsC monitor.consumeMotionCancel(ADISPLAY_ID_DEFAULT); } -// Tests for gesture monitors -TEST_F(InputDispatcherTest, GestureMonitor_ReceivesMotionEvents) { +TEST_F(InputDispatcherMonitorTest, ReceivesMotionEvents) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); - FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, - true /*isGestureMonitor*/); + FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) @@ -2897,71 +2897,34 @@ TEST_F(InputDispatcherTest, GestureMonitor_ReceivesMotionEvents) { monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); } -TEST_F(InputDispatcherTest, GestureMonitor_DoesNotReceiveKeyEvents) { - std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - - mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - window->setFocusable(true); - - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); - setFocusedWindow(window); - - window->consumeFocusEvent(true); +TEST_F(InputDispatcherMonitorTest, MonitorCannotPilferPointers) { + FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); - FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, - true /*isGestureMonitor*/); - - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT)) - << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; - window->consumeKeyDown(ADISPLAY_ID_DEFAULT); - monitor.assertNoEvents(); -} - -TEST_F(InputDispatcherTest, GestureMonitor_CanPilferAfterWindowIsRemovedMidStream) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); - FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, - true /*isGestureMonitor*/); - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - window->consumeMotionDown(ADISPLAY_ID_DEFAULT); monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); - window->releaseChannel(); - - mDispatcher->pilferPointers(monitor.getToken()); + // Pilfer pointers from the monitor. + // This should not do anything and the window should continue to receive events. + EXPECT_NE(OK, mDispatcher->pilferPointers(monitor.getToken())); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, - injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT)) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); -} - -TEST_F(InputDispatcherTest, UnresponsiveGestureMonitor_GetsAnr) { - FakeMonitorReceiver monitor = - FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT, - true /*isGestureMonitor*/); - - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, - injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)); - std::optional<uint32_t> consumeSeq = monitor.receiveEvent(); - ASSERT_TRUE(consumeSeq); - mFakePolicy->assertNotifyMonitorUnresponsiveWasCalled(DISPATCHING_TIMEOUT); - monitor.finishEvent(*consumeSeq); - ASSERT_TRUE(mDispatcher->waitForIdle()); - mFakePolicy->assertNotifyMonitorResponsiveWasCalled(); + monitor.consumeMotionMove(ADISPLAY_ID_DEFAULT); + window->consumeMotionMove(ADISPLAY_ID_DEFAULT); } -// Tests for gesture monitors -TEST_F(InputDispatcherTest, GestureMonitor_NoWindowTransform) { +TEST_F(InputDispatcherMonitorTest, NoWindowTransform) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); @@ -2969,8 +2932,7 @@ TEST_F(InputDispatcherTest, GestureMonitor_NoWindowTransform) { window->setWindowOffset(20, 40); window->setWindowTransform(0, 1, -1, 0); - FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, - true /*isGestureMonitor*/); + FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) @@ -2981,15 +2943,14 @@ TEST_F(InputDispatcherTest, GestureMonitor_NoWindowTransform) { ASSERT_EQ(ui::Transform(), event->getTransform()); } -TEST_F(InputDispatcherTest, GestureMonitor_NoWindow) { +TEST_F(InputDispatcherMonitorTest, InjectionFailsWithNoWindow) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, - true /*isGestureMonitor*/); + FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + ASSERT_EQ(InputEventInjectionResult::FAILED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) - << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); + << "Injection should fail if there is a monitor, but no touchable window"; + monitor.assertNoEvents(); } TEST_F(InputDispatcherTest, TestMoveEvent) { @@ -3018,91 +2979,6 @@ TEST_F(InputDispatcherTest, TestMoveEvent) { 0 /*expectedFlags*/); } -TEST_F(InputDispatcherTest, GestureMonitor_SplitIfNoWindowTouched) { - FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, - true /*isGestureMonitor*/); - - std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - // Create a non touch modal window that supports split touch - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - window->setFrame(Rect(0, 0, 100, 100)); - window->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); - - // First finger down, no window touched. - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, - injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, - {100, 200})) - << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); - window->assertNoEvents(); - - // Second finger down on window, the window should receive touch down. - const MotionEvent secondFingerDownEvent = - MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN | - (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - AINPUT_SOURCE_TOUCHSCREEN) - .displayId(ADISPLAY_ID_DEFAULT) - .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) - .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER) - .x(100) - .y(200)) - .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) - .build(); - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, - injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, - InputEventInjectionSync::WAIT_FOR_RESULT)) - << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - - window->consumeMotionDown(ADISPLAY_ID_DEFAULT); - monitor.consumeMotionPointerDown(1 /* pointerIndex */); -} - -TEST_F(InputDispatcherTest, GestureMonitor_NoSplitAfterPilfer) { - FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, - true /*isGestureMonitor*/); - - std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - // Create a non touch modal window that supports split touch - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - window->setFrame(Rect(0, 0, 100, 100)); - window->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); - - // First finger down, no window touched. - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, - injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, - {100, 200})) - << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); - window->assertNoEvents(); - - // Gesture monitor pilfer the pointers. - mDispatcher->pilferPointers(monitor.getToken()); - - // Second finger down on window, the window should not receive touch down. - const MotionEvent secondFingerDownEvent = - MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN | - (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - AINPUT_SOURCE_TOUCHSCREEN) - .displayId(ADISPLAY_ID_DEFAULT) - .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) - .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER) - .x(100) - .y(200)) - .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) - .build(); - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, - injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, - InputEventInjectionSync::WAIT_FOR_RESULT)) - << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - - window->assertNoEvents(); - monitor.consumeMotionPointerDown(1 /* pointerIndex */); -} - /** * Dispatcher has touch mode enabled by default. Typically, the policy overrides that value to * the device default right away. In the test scenario, we check both the default value, @@ -4442,6 +4318,17 @@ protected: injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, WINDOW_LOCATION)); } + + sp<FakeWindowHandle> addSpyWindow() { + sp<FakeWindowHandle> spy = + new FakeWindowHandle(mApplication, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT); + spy->setTrustedOverlay(true); + spy->setFocusable(false); + spy->setInputFeatures(WindowInfo::Feature::SPY); + spy->setDispatchingTimeout(30ms); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, mWindow}}}); + return spy; + } }; // Send a tap and respond, which should not cause an ANR. @@ -4617,12 +4504,31 @@ TEST_F(InputDispatcherSingleWindowAnr, Anr_HandlesEventsWithIdenticalTimestamps) mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken()); } -// If an app is not responding to a key event, gesture monitors should continue to receive +// A spy window can receive an ANR +TEST_F(InputDispatcherSingleWindowAnr, SpyWindowAnr) { + sp<FakeWindowHandle> spy = addSpyWindow(); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + WINDOW_LOCATION)); + mWindow->consumeMotionDown(); + + std::optional<uint32_t> sequenceNum = spy->receiveEvent(); // ACTION_DOWN + ASSERT_TRUE(sequenceNum); + const std::chrono::duration timeout = spy->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, spy->getToken()); + + spy->finishEvent(*sequenceNum); + spy->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, ADISPLAY_ID_DEFAULT, + 0 /*flags*/); + ASSERT_TRUE(mDispatcher->waitForIdle()); + mFakePolicy->assertNotifyWindowResponsiveWasCalled(spy->getToken()); +} + +// If an app is not responding to a key event, spy windows should continue to receive // new motion events -TEST_F(InputDispatcherSingleWindowAnr, GestureMonitors_ReceiveEventsDuringAppAnrOnKey) { - FakeMonitorReceiver monitor = - FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT, - true /*isGestureMonitor*/); +TEST_F(InputDispatcherSingleWindowAnr, SpyWindowReceivesEventsDuringAppAnrOnKey) { + sp<FakeWindowHandle> spy = addSpyWindow(); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT)); @@ -4633,44 +4539,64 @@ TEST_F(InputDispatcherSingleWindowAnr, GestureMonitors_ReceiveEventsDuringAppAnr const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken()); - // New tap will go to the gesture monitor, but not to the window + // New tap will go to the spy window, but not to the window tapOnWindow(); - monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); - monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); + spy->consumeMotionDown(ADISPLAY_ID_DEFAULT); + spy->consumeMotionUp(ADISPLAY_ID_DEFAULT); mWindow->consumeKeyUp(ADISPLAY_ID_DEFAULT); // still the previous motion mDispatcher->waitForIdle(); mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken()); mWindow->assertNoEvents(); - monitor.assertNoEvents(); + spy->assertNoEvents(); } -// If an app is not responding to a motion event, gesture monitors should continue to receive +// If an app is not responding to a motion event, spy windows should continue to receive // new motion events -TEST_F(InputDispatcherSingleWindowAnr, GestureMonitors_ReceiveEventsDuringAppAnrOnMotion) { - FakeMonitorReceiver monitor = - FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT, - true /*isGestureMonitor*/); +TEST_F(InputDispatcherSingleWindowAnr, SpyWindowReceivesEventsDuringAppAnrOnMotion) { + sp<FakeWindowHandle> spy = addSpyWindow(); tapOnWindow(); - monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); - monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); + spy->consumeMotionDown(ADISPLAY_ID_DEFAULT); + spy->consumeMotionUp(ADISPLAY_ID_DEFAULT); mWindow->consumeMotionDown(); // Stuck on the ACTION_UP const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken()); - // New tap will go to the gesture monitor, but not to the window + // New tap will go to the spy window, but not to the window tapOnWindow(); - monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); - monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); + spy->consumeMotionDown(ADISPLAY_ID_DEFAULT); + spy->consumeMotionUp(ADISPLAY_ID_DEFAULT); mWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); // still the previous motion mDispatcher->waitForIdle(); mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken()); mWindow->assertNoEvents(); - monitor.assertNoEvents(); + spy->assertNoEvents(); +} + +TEST_F(InputDispatcherSingleWindowAnr, UnresponsiveMonitorAnr) { + mDispatcher->setMonitorDispatchingTimeoutForTest(30ms); + + FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + WINDOW_LOCATION)); + + mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT); + const std::optional<uint32_t> consumeSeq = monitor.receiveEvent(); + ASSERT_TRUE(consumeSeq); + + mFakePolicy->assertNotifyMonitorUnresponsiveWasCalled(30ms); + + monitor.finishEvent(*consumeSeq); + monitor.consumeMotionCancel(ADISPLAY_ID_DEFAULT); + + ASSERT_TRUE(mDispatcher->waitForIdle()); + mFakePolicy->assertNotifyMonitorResponsiveWasCalled(); } // If a window is unresponsive, then you get anr. if the window later catches up and starts to @@ -6338,6 +6264,7 @@ public: sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); window->addFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH); + window->setFocusable(true); return window; } @@ -6345,10 +6272,13 @@ private: int mSpyCount{0}; }; +using InputDispatcherSpyWindowDeathTest = InputDispatcherSpyWindowTest; /** * Adding a spy window that is not a trusted overlay causes Dispatcher to abort. */ -TEST_F(InputDispatcherSpyWindowTest, UntrustedSpy_AbortsDispatcher) { +TEST_F(InputDispatcherSpyWindowDeathTest, UntrustedSpy_AbortsDispatcher) { + ScopedSilentDeath _silentDeath; + auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL); spy->setTrustedOverlay(false); ASSERT_DEATH(mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy}}}), @@ -6532,7 +6462,7 @@ TEST_F(InputDispatcherSpyWindowTest, PilferPointers) { spy2->consumeMotionDown(); // Pilfer pointers from the second spy window. - mDispatcher->pilferPointers(spy2->getToken()); + EXPECT_EQ(OK, mDispatcher->pilferPointers(spy2->getToken())); spy2->assertNoEvents(); spy1->consumeMotionCancel(); window->consumeMotionCancel(); @@ -6548,6 +6478,80 @@ TEST_F(InputDispatcherSpyWindowTest, PilferPointers) { } /** + * A spy window can pilfer pointers for a gesture even after the foreground window has been removed + * in the middle of the gesture. + */ +TEST_F(InputDispatcherSpyWindowTest, CanPilferAfterWindowIsRemovedMidStream) { + auto window = createForeground(); + auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}}); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); + spy->consumeMotionDown(ADISPLAY_ID_DEFAULT); + + window->releaseChannel(); + + EXPECT_EQ(OK, mDispatcher->pilferPointers(spy->getToken())); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + spy->consumeMotionUp(ADISPLAY_ID_DEFAULT); +} + +/** + * After a spy window pilfers pointers, new pointers that go down should not go to any foreground + * windows. + */ +TEST_F(InputDispatcherSpyWindowTest, NoSplitAfterPilfer) { + // Create a touch modal spy that spies on the entire display. + auto spy = createSpy(static_cast<WindowInfo::Flag>(0)); + + // Create a non touch modal window that supports split touch. + auto window = createForeground(); + window->setFrame(Rect(0, 0, 100, 100)); + window->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}}); + + // First finger down, no window touched. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 200})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + spy->consumeMotionDown(ADISPLAY_ID_DEFAULT); + window->assertNoEvents(); + + // Spy window pilfer the pointers. + EXPECT_EQ(OK, mDispatcher->pilferPointers(spy->getToken())); + + // Second finger down on window, the window should not receive touch down. + const MotionEvent secondFingerDownEvent = + MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN | + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + AINPUT_SOURCE_TOUCHSCREEN) + .displayId(ADISPLAY_ID_DEFAULT) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(100) + .y(200)) + .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) + .build(); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::WAIT_FOR_RESULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + window->assertNoEvents(); + // Since we no longer allow splitting, the spy will not receive new pointers. + // TODO(b/217376964): Add a way for the pilfering window to receive the rest of the gesture. + spy->consumeMotionMove(); +} + +/** * Even when a spy window spans over multiple foreground windows, the spy should receive all * pointers that are down within its bounds. */ @@ -6619,6 +6623,76 @@ TEST_F(InputDispatcherSpyWindowTest, ReceivesSecondPointerAsDown) { spyRight->consumeMotionDown(); } +/** + * The spy window should not be able to affect whether or not touches are split. Only the foreground + * windows should be allowed to control split touch. + */ +TEST_F(InputDispatcherSpyWindowTest, SplitIfNoForegroundWindowTouched) { + // Create a touch modal spy that spies on the entire display. + // This spy window does not set the SPLIT_TOUCH flag. However, we still expect to split touches + // because a foreground window has not disabled splitting. + auto spy = createSpy(static_cast<WindowInfo::Flag>(0)); + + // Create a non touch modal window that supports split touch. + auto window = createForeground(); + window->setFrame(Rect(0, 0, 100, 100)); + window->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}}); + + // First finger down, no window touched. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 200})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + spy->consumeMotionDown(ADISPLAY_ID_DEFAULT); + window->assertNoEvents(); + + // Second finger down on window, the window should receive touch down. + const MotionEvent secondFingerDownEvent = + MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN | + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + AINPUT_SOURCE_TOUCHSCREEN) + .displayId(ADISPLAY_ID_DEFAULT) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(100) + .y(200)) + .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) + .build(); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::WAIT_FOR_RESULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); + spy->consumeMotionPointerDown(1 /* pointerIndex */); +} + +/** + * A spy window will usually be implemented as an un-focusable window. Verify that these windows + * do not receive key events. + */ +TEST_F(InputDispatcherSpyWindowTest, UnfocusableSpyDoesNotReceiveKeyEvents) { + auto spy = createSpy(static_cast<WindowInfo::Flag>(0)); + spy->setFocusable(false); + + auto window = createForeground(); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}}); + setFocusedWindow(window); + window->consumeFocusEvent(true); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; + window->consumeKeyDown(ADISPLAY_ID_NONE); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; + window->consumeKeyUp(ADISPLAY_ID_NONE); + + spy->assertNoEvents(); +} + class InputDispatcherStylusInterceptorTest : public InputDispatcherTest { public: std::pair<sp<FakeWindowHandle>, sp<FakeWindowHandle>> setupStylusOverlayScenario() { @@ -6665,7 +6739,11 @@ public: } }; -TEST_F(InputDispatcherStylusInterceptorTest, UntrustedOverlay_AbortsDispatcher) { +using InputDispatcherStylusInterceptorDeathTest = InputDispatcherStylusInterceptorTest; + +TEST_F(InputDispatcherStylusInterceptorDeathTest, UntrustedOverlay_AbortsDispatcher) { + ScopedSilentDeath _silentDeath; + auto [overlay, window] = setupStylusOverlayScenario(); overlay->setTrustedOverlay(false); // Configuring an untrusted overlay as a stylus interceptor should cause Dispatcher to abort. diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 2c5b32140d..54cf15d1d4 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -439,6 +439,8 @@ class FakeEventHub : public EventHubInterface { KeyedVector<int32_t, KeyInfo> keysByScanCode; KeyedVector<int32_t, KeyInfo> keysByUsageCode; KeyedVector<int32_t, bool> leds; + // fake mapping which would normally come from keyCharacterMap + std::unordered_map<int32_t, int32_t> keyCodeMapping; std::unordered_map<int32_t, SensorInfo> sensorsByAbsCode; BitArray<MSC_MAX> mscBitmask; std::vector<VirtualKeyDefinition> virtualKeys; @@ -600,6 +602,11 @@ public: } } + void addKeyCodeMapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) { + Device* device = getDevice(deviceId); + device->keyCodeMapping.insert_or_assign(fromKeyCode, toKeyCode); + } + void addLed(int32_t deviceId, int32_t led, bool initialState) { Device* device = getDevice(deviceId); device->leds.add(led, initialState); @@ -863,6 +870,15 @@ private: return -1; } + int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override { + Device* device = getDevice(deviceId); + if (!device) { + return AKEYCODE_UNKNOWN; + } + auto it = device->keyCodeMapping.find(locationKeyCode); + return it != device->keyCodeMapping.end() ? it->second : locationKeyCode; + } + // Return true if the device has non-empty key layout. bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const override { @@ -1034,6 +1050,8 @@ class FakeInputMapper : public InputMapper { KeyedVector<int32_t, int32_t> mKeyCodeStates; KeyedVector<int32_t, int32_t> mScanCodeStates; KeyedVector<int32_t, int32_t> mSwitchStates; + // fake mapping which would normally come from keyCharacterMap + std::unordered_map<int32_t, int32_t> mKeyCodeMapping; std::vector<int32_t> mSupportedKeyCodes; std::mutex mLock; @@ -1122,8 +1140,12 @@ public: mSupportedKeyCodes.push_back(keyCode); } + void addKeyCodeMapping(int32_t fromKeyCode, int32_t toKeyCode) { + mKeyCodeMapping.insert_or_assign(fromKeyCode, toKeyCode); + } + private: - uint32_t getSources() override { return mSources; } + uint32_t getSources() const override { return mSources; } void populateDeviceInfo(InputDeviceInfo* deviceInfo) override { InputMapper::populateDeviceInfo(deviceInfo); @@ -1164,6 +1186,11 @@ private: return index >= 0 ? mKeyCodeStates.valueAt(index) : AKEY_STATE_UNKNOWN; } + int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const override { + auto it = mKeyCodeMapping.find(locationKeyCode); + return it != mKeyCodeMapping.end() ? it->second : locationKeyCode; + } + int32_t getScanCodeState(uint32_t, int32_t scanCode) override { ssize_t index = mScanCodeStates.indexOfKey(scanCode); return index >= 0 ? mScanCodeStates.valueAt(index) : AKEY_STATE_UNKNOWN; @@ -1712,6 +1739,37 @@ TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) { << "Should return value provided by mapper when device id is < 0 and one of the devices supports some of the sources."; } +TEST_F(InputReaderTest, GetKeyCodeForKeyLocation_ForwardsRequestsToMappers) { + constexpr int32_t deviceId = END_RESERVED_ID + 1000; + constexpr int32_t eventHubId = 1; + FakeInputMapper& mapper = addDeviceWithFakeInputMapper(deviceId, eventHubId, "keyboard", + InputDeviceClass::KEYBOARD, + AINPUT_SOURCE_KEYBOARD, nullptr); + mapper.addKeyCodeMapping(AKEYCODE_Y, AKEYCODE_Z); + + ASSERT_EQ(AKEYCODE_UNKNOWN, mReader->getKeyCodeForKeyLocation(0, AKEYCODE_Y)) + << "Should return unknown when the device with the specified id is not found."; + + ASSERT_EQ(AKEYCODE_Z, mReader->getKeyCodeForKeyLocation(deviceId, AKEYCODE_Y)) + << "Should return correct mapping when device id is valid and mapping exists."; + + ASSERT_EQ(AKEYCODE_A, mReader->getKeyCodeForKeyLocation(deviceId, AKEYCODE_A)) + << "Should return the location key code when device id is valid and there's no " + "mapping."; +} + +TEST_F(InputReaderTest, GetKeyCodeForKeyLocation_NoKeyboardMapper) { + constexpr int32_t deviceId = END_RESERVED_ID + 1000; + constexpr int32_t eventHubId = 1; + FakeInputMapper& mapper = addDeviceWithFakeInputMapper(deviceId, eventHubId, "joystick", + InputDeviceClass::JOYSTICK, + AINPUT_SOURCE_GAMEPAD, nullptr); + mapper.addKeyCodeMapping(AKEYCODE_Y, AKEYCODE_Z); + + ASSERT_EQ(AKEYCODE_UNKNOWN, mReader->getKeyCodeForKeyLocation(deviceId, AKEYCODE_Y)) + << "Should return unknown when the device id is valid but there is no keyboard mapper"; +} + TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) { constexpr int32_t deviceId = END_RESERVED_ID + 1000; constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD; @@ -2804,7 +2862,7 @@ TEST_F(InputDeviceTest, Configure_AssignsDisplayUniqueId) { // Device should be disabled because it is associated with a specific display, but the // corresponding display is not found. const std::string DISPLAY_UNIQUE_ID = "displayUniqueId"; - mFakePolicy->addInputUniqueIdAssociation(DEVICE_NAME, DISPLAY_UNIQUE_ID); + mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID); mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), InputReaderConfiguration::CHANGE_DISPLAY_INFO); ASSERT_FALSE(mDevice->isEnabled()); @@ -2829,6 +2887,21 @@ TEST_F(InputDeviceTest, Configure_AssignsDisplayUniqueId) { ASSERT_FALSE(mDevice->isEnabled()); } +TEST_F(InputDeviceTest, Configure_UniqueId_CorrectlyMatches) { + mFakePolicy->clearViewports(); + mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD); + mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0); + + const std::string DISPLAY_UNIQUE_ID = "displayUniqueId"; + mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID); + mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, + DISPLAY_ORIENTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID, + NO_PORT, ViewportType::INTERNAL); + mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_DISPLAY_INFO); + ASSERT_EQ(DISPLAY_UNIQUE_ID, mDevice->getAssociatedDisplayUniqueId()); +} + // --- InputMapperTest --- class InputMapperTest : public testing::Test { @@ -3609,6 +3682,19 @@ TEST_F(KeyboardInputMapperTest, GetKeyCodeState) { ASSERT_EQ(0, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A)); } +TEST_F(KeyboardInputMapperTest, GetKeyCodeForKeyLocation) { + KeyboardInputMapper& mapper = + addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); + + mFakeEventHub->addKeyCodeMapping(EVENTHUB_ID, AKEYCODE_Y, AKEYCODE_Z); + ASSERT_EQ(AKEYCODE_Z, mapper.getKeyCodeForKeyLocation(AKEYCODE_Y)) + << "If a mapping is available, the result is equal to the mapping"; + + ASSERT_EQ(AKEYCODE_A, mapper.getKeyCodeForKeyLocation(AKEYCODE_A)) + << "If no mapping is available, the result is the key location"; +} + TEST_F(KeyboardInputMapperTest, GetScanCodeState) { KeyboardInputMapper& mapper = addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, diff --git a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp new file mode 100644 index 0000000000..a3220cc63a --- /dev/null +++ b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp @@ -0,0 +1,938 @@ +/* + * 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. + */ + +#include "../UnwantedInteractionBlocker.h" +#include <android-base/silent_death_test.h> +#include <gtest/gtest.h> +#include <gui/constants.h> +#include <linux/input.h> + +#include "TestInputListener.h" + +namespace android { + +constexpr int32_t DEVICE_ID = 3; +constexpr int32_t X_RESOLUTION = 11; +constexpr int32_t Y_RESOLUTION = 11; +constexpr int32_t MAJOR_RESOLUTION = 1; + +constexpr int POINTER_0_DOWN = + AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +constexpr int POINTER_1_DOWN = + AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +constexpr int POINTER_2_DOWN = + AMOTION_EVENT_ACTION_POINTER_DOWN | (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +constexpr int POINTER_0_UP = + AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +constexpr int POINTER_1_UP = + AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +constexpr int POINTER_2_UP = + AMOTION_EVENT_ACTION_POINTER_UP | (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +constexpr int DOWN = AMOTION_EVENT_ACTION_DOWN; +constexpr int MOVE = AMOTION_EVENT_ACTION_MOVE; +constexpr int UP = AMOTION_EVENT_ACTION_UP; +constexpr int CANCEL = AMOTION_EVENT_ACTION_CANCEL; + +struct PointerData { + float x; + float y; + float major; +}; + +static NotifyMotionArgs generateMotionArgs(nsecs_t downTime, nsecs_t eventTime, int32_t action, + const std::vector<PointerData>& points) { + size_t pointerCount = points.size(); + if (action == AMOTION_EVENT_ACTION_DOWN || action == AMOTION_EVENT_ACTION_UP) { + EXPECT_EQ(1U, pointerCount) << "Actions DOWN and UP can only contain a single pointer"; + } + + PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties[i].clear(); + pointerProperties[i].id = i; + pointerProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + + pointerCoords[i].clear(); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, points[i].x); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, points[i].y); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, points[i].major); + } + + // Define a valid motion event. + NotifyMotionArgs args(/* id */ 0, eventTime, 0 /*readTime*/, DEVICE_ID, + AINPUT_SOURCE_TOUCHSCREEN, 0 /*displayId*/, POLICY_FLAG_PASS_TO_USER, + action, /* actionButton */ 0, + /* flags */ 0, AMETA_NONE, /* buttonState */ 0, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, + pointerProperties, pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime, /* videoFrames */ {}); + + return args; +} + +static InputDeviceInfo generateTestDeviceInfo() { + InputDeviceIdentifier identifier; + + auto info = InputDeviceInfo(); + info.initialize(DEVICE_ID, /*generation*/ 1, /*controllerNumber*/ 1, identifier, "alias", + /*isExternal*/ false, /*hasMic*/ false); + info.addSource(AINPUT_SOURCE_TOUCHSCREEN); + info.addMotionRange(AMOTION_EVENT_AXIS_X, AINPUT_SOURCE_TOUCHSCREEN, 0, 1599, /*flat*/ 0, + /*fuzz*/ 0, X_RESOLUTION); + info.addMotionRange(AMOTION_EVENT_AXIS_Y, AINPUT_SOURCE_TOUCHSCREEN, 0, 2559, /*flat*/ 0, + /*fuzz*/ 0, Y_RESOLUTION); + info.addMotionRange(AMOTION_EVENT_AXIS_TOUCH_MAJOR, AINPUT_SOURCE_TOUCHSCREEN, 0, 255, + /*flat*/ 0, /*fuzz*/ 0, MAJOR_RESOLUTION); + + return info; +} + +static AndroidPalmFilterDeviceInfo generatePalmFilterDeviceInfo() { + InputDeviceInfo androidInfo = generateTestDeviceInfo(); + std::optional<AndroidPalmFilterDeviceInfo> info = createPalmFilterDeviceInfo(androidInfo); + if (!info) { + ADD_FAILURE() << "Could not convert android device info to ::ui version"; + return {}; + } + return *info; +} + +TEST(DeviceInfoConversionTest, TabletDeviceTest) { + AndroidPalmFilterDeviceInfo info = generatePalmFilterDeviceInfo(); + ASSERT_EQ(X_RESOLUTION, info.x_res); + ASSERT_EQ(Y_RESOLUTION, info.y_res); + ASSERT_EQ(MAJOR_RESOLUTION, info.touch_major_res); + ASSERT_EQ(1599, info.max_x); + ASSERT_EQ(2559, info.max_y); +} + +static void assertArgs(const NotifyMotionArgs& args, int32_t action, + const std::vector<std::pair<int32_t /*pointerId*/, PointerData>>& pointers) { + ASSERT_EQ(action, args.action); + ASSERT_EQ(pointers.size(), args.pointerCount); + for (size_t i = 0; i < args.pointerCount; i++) { + const auto& [pointerId, pointerData] = pointers[i]; + ASSERT_EQ(pointerId, args.pointerProperties[i].id); + ASSERT_EQ(pointerData.x, args.pointerCoords[i].getX()); + ASSERT_EQ(pointerData.y, args.pointerCoords[i].getY()); + ASSERT_EQ(pointerData.major, + args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR)); + } +} + +TEST(RemovePointerIdsTest, RemoveOnePointer) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, + AMOTION_EVENT_ACTION_MOVE, {{1, 2, 3}, {4, 5, 6}}); + + NotifyMotionArgs pointer1Only = removePointerIds(args, {0}); + assertArgs(pointer1Only, AMOTION_EVENT_ACTION_MOVE, {{1, {4, 5, 6}}}); + + NotifyMotionArgs pointer0Only = removePointerIds(args, {1}); + assertArgs(pointer0Only, AMOTION_EVENT_ACTION_MOVE, {{0, {1, 2, 3}}}); +} + +/** + * Remove 2 out of 3 pointers during a MOVE event. + */ +TEST(RemovePointerIdsTest, RemoveTwoPointers) { + NotifyMotionArgs args = + generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, AMOTION_EVENT_ACTION_MOVE, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + + NotifyMotionArgs pointer1Only = removePointerIds(args, {0, 2}); + assertArgs(pointer1Only, AMOTION_EVENT_ACTION_MOVE, {{1, {4, 5, 6}}}); +} + +/** + * Remove an active pointer during a POINTER_DOWN event, and also remove a non-active + * pointer during a POINTER_DOWN event. + */ +TEST(RemovePointerIdsTest, ActionPointerDown) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_DOWN, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + + NotifyMotionArgs pointers0And2 = removePointerIds(args, {1}); + assertArgs(pointers0And2, ACTION_UNKNOWN, {{0, {1, 2, 3}}, {2, {7, 8, 9}}}); + + NotifyMotionArgs pointers1And2 = removePointerIds(args, {0}); + assertArgs(pointers1And2, POINTER_0_DOWN, {{1, {4, 5, 6}}, {2, {7, 8, 9}}}); +} + +/** + * Remove all pointers during a MOVE event. + */ +TEST(RemovePointerIdsTest, RemoveAllPointersDuringMove) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, + AMOTION_EVENT_ACTION_MOVE, {{1, 2, 3}, {4, 5, 6}}); + + NotifyMotionArgs noPointers = removePointerIds(args, {0, 1}); + ASSERT_EQ(0u, noPointers.pointerCount); +} + +/** + * If we have ACTION_POINTER_DOWN, and we remove all pointers except for the active pointer, + * then we should just have ACTION_DOWN. Likewise, a POINTER_UP event should become an UP event. + */ +TEST(RemovePointerIdsTest, PointerDownBecomesDown) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_DOWN, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + + NotifyMotionArgs pointer1 = removePointerIds(args, {0, 2}); + assertArgs(pointer1, DOWN, {{1, {4, 5, 6}}}); + + args.action = POINTER_1_UP; + pointer1 = removePointerIds(args, {0, 2}); + assertArgs(pointer1, UP, {{1, {4, 5, 6}}}); +} + +/** + * If a pointer that is now going down is canceled, then we can just drop the POINTER_DOWN event. + */ +TEST(CancelSuppressedPointersTest, CanceledPointerDownIsDropped) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_DOWN, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + std::vector<NotifyMotionArgs> result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {}, + /*newSuppressedPointerIds*/ {1}); + ASSERT_TRUE(result.empty()); +} + +/** + * If a pointer is already suppressed, the POINTER_UP event for this pointer should be dropped + */ +TEST(CancelSuppressedPointersTest, SuppressedPointerUpIsDropped) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_UP, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + std::vector<NotifyMotionArgs> result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {1}, + /*newSuppressedPointerIds*/ {1}); + ASSERT_TRUE(result.empty()); +} + +/** + * If a pointer is already suppressed, it should be removed from a MOVE event. + */ +TEST(CancelSuppressedPointersTest, SuppressedPointerIsRemovedDuringMove) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + std::vector<NotifyMotionArgs> result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {1}, + /*newSuppressedPointerIds*/ {1}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], MOVE, {{0, {1, 2, 3}}, {2, {7, 8, 9}}}); +} + +/** + * If a pointer just got canceled during a MOVE event, we should see two events: + * 1) ACTION_POINTER_UP with FLAG_CANCELED so that this pointer is lifted + * 2) A MOVE event without this pointer + */ +TEST(CancelSuppressedPointersTest, NewlySuppressedPointerIsCanceled) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + std::vector<NotifyMotionArgs> result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {}, + /*newSuppressedPointerIds*/ {1}); + ASSERT_EQ(2u, result.size()); + assertArgs(result[0], POINTER_1_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}, {2, {7, 8, 9}}}); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); + assertArgs(result[1], MOVE, {{0, {1, 2, 3}}, {2, {7, 8, 9}}}); +} + +/** + * If we have a single pointer that gets canceled during a MOVE, the entire gesture + * should be canceled with ACTION_CANCEL. + */ +TEST(CancelSuppressedPointersTest, SingleSuppressedPointerIsCanceled) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE, {{1, 2, 3}}); + std::vector<NotifyMotionArgs> result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {}, + /*newSuppressedPointerIds*/ {0}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}}); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); +} + +/** + * If one of 3 pointers gets canceled during a POINTER_UP event, we should proceed with POINTER_UP, + * but this event should also have FLAG_CANCELED to indicate that this pointer was unintentional. + */ +TEST(CancelSuppressedPointersTest, SuppressedPointer1GoingUpIsCanceled) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_UP, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + std::vector<NotifyMotionArgs> result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {}, + /*newSuppressedPointerIds*/ {1}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], POINTER_1_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}, {2, {7, 8, 9}}}); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); +} + +/** + * Same test as above, but we change the pointer's index to 0 instead of 1. This helps detect + * errors with handling pointer index inside the action. + */ +TEST(CancelSuppressedPointersTest, SuppressedPointer0GoingUpIsCanceled) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_0_UP, + {{1, 2, 3}, {4, 5, 6}}); + std::vector<NotifyMotionArgs> result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {}, + /*newSuppressedPointerIds*/ {0}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], POINTER_0_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}}); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); +} + +/** + * If two pointers are canceled simultaneously during MOVE, we should see a single ACTION_CANCEL + * event. This event would cancel the entire gesture. + */ +TEST(CancelSuppressedPointersTest, TwoNewlySuppressedPointersAreBothCanceled) { + NotifyMotionArgs args = + generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE, {{1, 2, 3}, {4, 5, 6}}); + std::vector<NotifyMotionArgs> result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {}, + /*newSuppressedPointerIds*/ {0, 1}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}, {1, {4, 5, 6}}}); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); +} + +/** + * Similar test to above. During a POINTER_UP event, both pointers are detected as 'palm' and + * therefore should be removed. In this case, we should send a single ACTION_CANCEL that + * would undo the entire gesture. + */ +TEST(CancelSuppressedPointersTest, TwoPointersAreCanceledDuringPointerUp) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_UP, + {{1, 2, 3}, {4, 5, 6}}); + std::vector<NotifyMotionArgs> result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {1}, + /*newSuppressedPointerIds*/ {0, 1}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}}); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); +} + +/** + * When all pointers have been removed from the touch stream, and we have a new POINTER_DOWN, + * this should become a regular DOWN event because it's the only pointer that will be valid now. + */ +TEST(CancelSuppressedPointersTest, NewPointerDownBecomesDown) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_2_DOWN, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + std::vector<NotifyMotionArgs> result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {0, 1}, + /*newSuppressedPointerIds*/ {0, 1}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], DOWN, {{2, {7, 8, 9}}}); + ASSERT_EQ(0, result[0].flags); +} + +/** + * Call 'getTouches' for a DOWN event and check that the resulting 'InProgressTouchEvdev' + * struct is populated as expected. + */ +TEST(GetTouchesTest, ConvertDownEvent) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, DOWN, {{1, 2, 3}}); + AndroidPalmFilterDeviceInfo deviceInfo = generatePalmFilterDeviceInfo(); + SlotState slotState; + SlotState oldSlotState = slotState; + slotState.update(args); + std::vector<::ui::InProgressTouchEvdev> touches = + getTouches(args, deviceInfo, oldSlotState, slotState); + ASSERT_EQ(1u, touches.size()); + ::ui::InProgressTouchEvdev expected; + + expected.major = 3; + expected.minor = 0; + expected.tool_type = MT_TOOL_FINGER; + expected.altered = true; + expected.was_cancelled = false; + expected.cancelled = false; + expected.delayed = false; + expected.was_delayed = false; + expected.held = false; + expected.was_held = false; + expected.was_touching = false; + expected.touching = true; + expected.x = 1; + expected.y = 2; + expected.tracking_id = 0; + std::optional<size_t> slot = slotState.getSlotForPointerId(0); + ASSERT_TRUE(slot); + expected.slot = *slot; + expected.pressure = 0; + expected.tool_code = BTN_TOOL_FINGER; + expected.reported_tool_type = ::ui::EventPointerType::kTouch; + expected.stylus_button = false; + + ASSERT_EQ(expected, touches[0]) << toString(touches[0]); +} + +// --- UnwantedInteractionBlockerTest --- + +class UnwantedInteractionBlockerTest : public testing::Test { +protected: + TestInputListener mTestListener; + std::unique_ptr<UnwantedInteractionBlockerInterface> mBlocker; + + void SetUp() override { + mBlocker = std::make_unique<UnwantedInteractionBlocker>(mTestListener, + /*enablePalmRejection*/ true); + } +}; + +/** + * Create a basic configuration change and send it to input classifier. + * Expect that the event is received by the next input stage, unmodified. + */ +TEST_F(UnwantedInteractionBlockerTest, ConfigurationChangedIsPassedToNextListener) { + // Create a basic configuration change and send to classifier + NotifyConfigurationChangedArgs args(1 /*sequenceNum*/, 2 /*eventTime*/); + + mBlocker->notifyConfigurationChanged(&args); + NotifyConfigurationChangedArgs outArgs; + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyConfigurationChangedWasCalled(&outArgs)); + ASSERT_EQ(args, outArgs); +} + +/** + * Keys are not handled in 'UnwantedInteractionBlocker' and should be passed + * to next stage unmodified. + */ +TEST_F(UnwantedInteractionBlockerTest, KeyIsPassedToNextListener) { + // Create a basic key event and send to classifier + NotifyKeyArgs args(1 /*sequenceNum*/, 2 /*eventTime*/, 21 /*readTime*/, 3 /*deviceId*/, + AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_DEFAULT, 0 /*policyFlags*/, + AKEY_EVENT_ACTION_DOWN, 4 /*flags*/, AKEYCODE_HOME, 5 /*scanCode*/, + AMETA_NONE, 6 /*downTime*/); + + mBlocker->notifyKey(&args); + NotifyKeyArgs outArgs; + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyKeyWasCalled(&outArgs)); + ASSERT_EQ(args, outArgs); +} + +/** + * Create a basic motion event. Since it's just a DOWN event, it should not + * be detected as palm and should be sent to the next listener stage + * unmodified. + */ +TEST_F(UnwantedInteractionBlockerTest, DownEventIsPassedToNextListener) { + NotifyMotionArgs motionArgs = + generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}}); + mBlocker->notifyMotion(&motionArgs); + NotifyMotionArgs args; + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyMotionWasCalled(&args)); + ASSERT_EQ(motionArgs, args); +} + +/** + * Create a basic switch event and send it to the UnwantedInteractionBlocker. + * Expect that the event is received by the next input stage, unmodified. + */ +TEST_F(UnwantedInteractionBlockerTest, SwitchIsPassedToNextListener) { + NotifySwitchArgs args(1 /*sequenceNum*/, 2 /*eventTime*/, 3 /*policyFlags*/, 4 /*switchValues*/, + 5 /*switchMask*/); + + mBlocker->notifySwitch(&args); + NotifySwitchArgs outArgs; + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifySwitchWasCalled(&outArgs)); + ASSERT_EQ(args, outArgs); +} + +/** + * Create a basic device reset event and send it to UnwantedInteractionBlocker. + * Expect that the event is received by the next input stage, unmodified. + */ +TEST_F(UnwantedInteractionBlockerTest, DeviceResetIsPassedToNextListener) { + NotifyDeviceResetArgs args(1 /*sequenceNum*/, 2 /*eventTime*/, DEVICE_ID); + + mBlocker->notifyDeviceReset(&args); + NotifyDeviceResetArgs outArgs; + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyDeviceResetWasCalled(&outArgs)); + ASSERT_EQ(args, outArgs); +} + +/** + * The state should be reset when device reset happens. That means, we can reset in the middle of a + * gesture, and start a new stream. There should be no crash. If the state wasn't reset correctly, + * a crash due to inconsistent event stream could have occurred. + */ +TEST_F(UnwantedInteractionBlockerTest, NoCrashWhenResetHappens) { + NotifyMotionArgs args; + mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()}); + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, DOWN, {{1, 2, 3}}))); + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 2 /*eventTime*/, MOVE, {{4, 5, 6}}))); + NotifyDeviceResetArgs resetArgs(1 /*sequenceNum*/, 3 /*eventTime*/, DEVICE_ID); + mBlocker->notifyDeviceReset(&resetArgs); + // Start a new gesture with a DOWN event, even though the previous event stream was incomplete. + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 4 /*eventTime*/, DOWN, {{7, 8, 9}}))); +} + +/** + * If input devices have changed, but the important device info that's used by the + * UnwantedInteractionBlocker has not changed, there should not be a reset. + */ +TEST_F(UnwantedInteractionBlockerTest, NoResetIfDeviceInfoChanges) { + NotifyMotionArgs args; + mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()}); + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, DOWN, {{1, 2, 3}}))); + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 2 /*eventTime*/, MOVE, {{4, 5, 6}}))); + + // Now pretend the device changed, even though nothing is different for DEVICE_ID in practice. + mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()}); + + // The MOVE event continues the gesture that started before 'devices changed', so it should not + // cause a crash. + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 4 /*eventTime*/, MOVE, {{7, 8, 9}}))); +} + +using UnwantedInteractionBlockerTestDeathTest = UnwantedInteractionBlockerTest; + +/** + * The state should be reset when device reset happens. If we receive an inconsistent event after + * the reset happens, crash should occur. + */ +TEST_F(UnwantedInteractionBlockerTestDeathTest, InconsistentEventAfterResetCausesACrash) { + ScopedSilentDeath _silentDeath; + NotifyMotionArgs args; + mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()}); + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, DOWN, {{1, 2, 3}}))); + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 2 /*eventTime*/, MOVE, {{4, 5, 6}}))); + NotifyDeviceResetArgs resetArgs(1 /*sequenceNum*/, 3 /*eventTime*/, DEVICE_ID); + mBlocker->notifyDeviceReset(&resetArgs); + // Sending MOVE without a DOWN -> should crash! + ASSERT_DEATH( + { + mBlocker->notifyMotion(&(args = generateMotionArgs(0 /*downTime*/, 4 /*eventTime*/, + MOVE, {{7, 8, 9}}))); + }, + "Could not find slot"); +} + +/** + * There should be a crash when an inconsistent event is received. + */ +TEST_F(UnwantedInteractionBlockerTestDeathTest, WhenMoveWithoutDownCausesACrash) { + ScopedSilentDeath _silentDeath; + NotifyMotionArgs args = generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, MOVE, {{1, 2, 3}}); + mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()}); + ASSERT_DEATH({ mBlocker->notifyMotion(&args); }, "Could not find slot"); +} + +class PalmRejectorTest : public testing::Test { +protected: + std::unique_ptr<PalmRejector> mPalmRejector; + + void SetUp() override { + AndroidPalmFilterDeviceInfo info = generatePalmFilterDeviceInfo(); + mPalmRejector = std::make_unique<PalmRejector>(info); + } +}; + +using PalmRejectorTestDeathTest = PalmRejectorTest; + +TEST_F(PalmRejectorTestDeathTest, InconsistentEventCausesACrash) { + ScopedSilentDeath _silentDeath; + constexpr nsecs_t downTime = 0; + NotifyMotionArgs args = + generateMotionArgs(downTime, 2 /*eventTime*/, MOVE, {{1406.0, 650.0, 52.0}}); + ASSERT_DEATH({ mPalmRejector->processMotion(args); }, "Could not find slot"); +} + +/** + * Use PalmRejector with actual touchscreen data and real model. + * Two pointers that should both be classified as palms. + */ +TEST_F(PalmRejectorTest, TwoPointersAreCanceled) { + std::vector<NotifyMotionArgs> argsList; + constexpr nsecs_t downTime = 255955749837000; + + mPalmRejector->processMotion( + generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955759313000, MOVE, {{1406.0, 650.0, 52.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955766361000, MOVE, {{1429.0, 672.0, 46.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955775989000, MOVE, {{1417.0, 685.0, 41.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955775989000, POINTER_1_DOWN, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783039000, MOVE, + {{1414.0, 702.0, 41.0}, {1059.0, 731.0, 12.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955792536000, MOVE, + {{1415.0, 719.0, 44.0}, {1060.0, 760.0, 11.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955799474000, MOVE, + {{1421.0, 733.0, 42.0}, {1065.0, 769.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955809177000, MOVE, + {{1426.0, 742.0, 43.0}, {1068.0, 771.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955816131000, MOVE, + {{1430.0, 748.0, 45.0}, {1069.0, 772.0, 13.0}})); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955825907000, MOVE, + {{1432.0, 750.0, 44.0}, {1069.0, 772.0, 13.0}})); + ASSERT_EQ(1u, argsList.size()); + ASSERT_EQ(0 /* No FLAG_CANCELED */, argsList[0].flags); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955832736000, MOVE, + {{1433.0, 751.0, 44.0}, {1070.0, 771.0, 13.0}})); + ASSERT_EQ(2u, argsList.size()); + ASSERT_EQ(POINTER_0_UP, argsList[0].action); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags); + ASSERT_EQ(MOVE, argsList[1].action); + ASSERT_EQ(1u, argsList[1].pointerCount); + ASSERT_EQ(0, argsList[1].flags); + + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955842432000, MOVE, + {{1433.0, 751.0, 42.0}, {1071.0, 770.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955849380000, MOVE, + {{1433.0, 751.0, 45.0}, {1072.0, 769.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955859046000, MOVE, + {{1433.0, 751.0, 43.0}, {1072.0, 768.0, 13.0}})); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955869823000, MOVE, + {{1433.0, 751.0, 45.0}, {1072.0, 767.0, 13.0}})); + ASSERT_EQ(1u, argsList.size()); + ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, argsList[0].action); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955875641000, MOVE, + {{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955882693000, MOVE, + {{1433.0, 750.0, 44.0}, {1072.0, 765.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955892324000, MOVE, + {{1433.0, 750.0, 42.0}, {1072.0, 763.0, 14.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955899425000, MOVE, + {{1434.0, 750.0, 44.0}, {1073.0, 761.0, 14.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955909400000, MOVE, + {{1435.0, 750.0, 43.0}, {1073.0, 759.0, 15.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955915885000, MOVE, + {{1436.0, 750.0, 45.0}, {1074.0, 757.0, 15.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955925607000, MOVE, + {{1436.0, 750.0, 44.0}, {1074.0, 755.0, 15.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955932580000, MOVE, + {{1436.0, 750.0, 45.0}, {1074.0, 753.0, 15.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955942231000, MOVE, + {{1436.0, 749.0, 44.0}, {1074.0, 751.0, 15.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955949204000, MOVE, + {{1435.0, 748.0, 45.0}, {1074.0, 749.0, 15.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955959103000, MOVE, + {{1434.0, 746.0, 44.0}, {1074.0, 747.0, 14.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955965884000, MOVE, + {{1433.0, 744.0, 44.0}, {1075.0, 745.0, 14.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955975649000, MOVE, + {{1431.0, 741.0, 43.0}, {1075.0, 742.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955982537000, MOVE, + {{1428.0, 738.0, 43.0}, {1076.0, 739.0, 12.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955992284000, MOVE, + {{1400.0, 726.0, 54.0}, {1076.0, 739.0, 13.0}})); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955999348000, POINTER_1_UP, + {{1362.0, 716.0, 55.0}, {1076.0, 739.0, 13.0}})); + ASSERT_TRUE(argsList.empty()); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955999348000, MOVE, {{1362.0, 716.0, 55.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956008885000, MOVE, {{1347.0, 707.0, 54.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956015791000, MOVE, {{1340.0, 698.0, 54.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956025804000, MOVE, {{1338.0, 694.0, 55.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956032314000, MOVE, {{1336.0, 690.0, 53.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956042329000, MOVE, {{1334.0, 685.0, 47.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956048979000, MOVE, {{1333.0, 679.0, 46.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956058813000, MOVE, {{1332.0, 672.0, 45.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956065592000, MOVE, {{1333.0, 666.0, 40.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956075276000, MOVE, {{1336.0, 661.0, 24.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956082198000, MOVE, {{1338.0, 656.0, 16.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956092059000, MOVE, {{1341.0, 649.0, 1.0}})); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956098764000, UP, {{1341.0, 649.0, 1.0}})); + ASSERT_TRUE(argsList.empty()); +} + +/** + * A test implementation of PalmDetectionFilter that allows you to specify which pointer you want + * the model to consider 'suppressed'. The pointer is specified using its position (x, y). + * Current limitation: + * Pointers may not cross each other in space during motion. Otherwise, any pointer with the + * position matching the suppressed position will be considered "palm". + */ +class TestFilter : public ::ui::PalmDetectionFilter { +public: + TestFilter(::ui::SharedPalmDetectionFilterState* state, + std::vector<std::pair<float, float>>& suppressedPointers) + : ::ui::PalmDetectionFilter(state), mSuppressedPointers(suppressedPointers) {} + + void Filter(const std::vector<::ui::InProgressTouchEvdev>& touches, ::base::TimeTicks time, + std::bitset<::ui::kNumTouchEvdevSlots>* slots_to_hold, + std::bitset<::ui::kNumTouchEvdevSlots>* slots_to_suppress) override { + updateSuppressedSlots(touches); + *slots_to_suppress = mSuppressedSlots; + } + + std::string FilterNameForTesting() const override { return "test filter"; } + +private: + void updateSuppressedSlots(const std::vector<::ui::InProgressTouchEvdev>& touches) { + for (::ui::InProgressTouchEvdev touch : touches) { + for (const auto& [x, y] : mSuppressedPointers) { + const float dx = (touch.x - x); + const float dy = (touch.y - y); + const float distanceSquared = dx * dx + dy * dy; + if (distanceSquared < 1) { + mSuppressedSlots.set(touch.slot, true); + } + } + } + } + + std::bitset<::ui::kNumTouchEvdevSlots> mSuppressedSlots; + std::vector<std::pair<float, float>>& mSuppressedPointers; +}; + +class PalmRejectorFakeFilterTest : public testing::Test { +protected: + std::unique_ptr<PalmRejector> mPalmRejector; + + void SetUp() override { + std::unique_ptr<::ui::PalmDetectionFilter> filter = + std::make_unique<TestFilter>(&mSharedPalmState, /*byref*/ mSuppressedPointers); + mPalmRejector = + std::make_unique<PalmRejector>(generatePalmFilterDeviceInfo(), std::move(filter)); + } + + void suppressPointerAtPosition(float x, float y) { mSuppressedPointers.push_back({x, y}); } + +private: + std::vector<std::pair<float, float>> mSuppressedPointers; + ::ui::SharedPalmDetectionFilterState mSharedPalmState; // unused, but we must retain ownership +}; + +/** + * When a MOVE event happens, the model identifies the pointer as palm. At that time, the palm + * rejector should send a POINTER_UP event for this pointer with FLAG_CANCELED, and subsequent + * events should have this pointer removed. + */ +TEST_F(PalmRejectorFakeFilterTest, OneOfTwoPointersIsCanceled) { + std::vector<NotifyMotionArgs> argsList; + constexpr nsecs_t downTime = 0; + + mPalmRejector->processMotion( + generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 1, POINTER_1_DOWN, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}})); + // Cancel the second pointer + suppressPointerAtPosition(1059, 731); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783039000, MOVE, + {{1414.0, 702.0, 41.0}, {1059.0, 731.0, 12.0}})); + ASSERT_EQ(2u, argsList.size()); + // First event - cancel pointer 1 + ASSERT_EQ(POINTER_1_UP, argsList[0].action); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags); + // Second event - send MOVE for the remaining pointer + ASSERT_EQ(MOVE, argsList[1].action); + ASSERT_EQ(0, argsList[1].flags); + + // Future move events only contain 1 pointer, because the second pointer will continue + // to be suppressed + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783039000, MOVE, + {{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}})); + ASSERT_EQ(1u, argsList.size()); + ASSERT_EQ(MOVE, argsList[0].action); + ASSERT_EQ(1u, argsList[0].pointerCount); + ASSERT_EQ(1433, argsList[0].pointerCoords[0].getX()); + ASSERT_EQ(751, argsList[0].pointerCoords[0].getY()); +} + +/** + * Send two pointers, and suppress both of them. Check that ACTION_CANCEL is generated. + * Afterwards: + * 1) Future MOVE events are ignored. + * 2) When a new pointer goes down, ACTION_DOWN is generated + */ +TEST_F(PalmRejectorFakeFilterTest, NewDownEventAfterCancel) { + std::vector<NotifyMotionArgs> argsList; + constexpr nsecs_t downTime = 0; + + mPalmRejector->processMotion( + generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 1, POINTER_1_DOWN, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}})); + // Cancel both pointers + suppressPointerAtPosition(1059, 731); + suppressPointerAtPosition(1400, 680); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 1, MOVE, {{1400, 680, 41}, {1059, 731, 10}})); + ASSERT_EQ(1u, argsList.size()); + // Cancel all + ASSERT_EQ(CANCEL, argsList[0].action); + ASSERT_EQ(2u, argsList[0].pointerCount); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags); + + // Future move events are ignored + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783039000, MOVE, + {{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}})); + ASSERT_EQ(0u, argsList.size()); + + // When a new pointer goes down, a new DOWN event is generated + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783039000, POINTER_2_DOWN, + {{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}, {1000, 700, 10}})); + ASSERT_EQ(1u, argsList.size()); + ASSERT_EQ(DOWN, argsList[0].action); + ASSERT_EQ(1u, argsList[0].pointerCount); + ASSERT_EQ(2, argsList[0].pointerProperties[0].id); +} + +/** + * 2 pointers are classified as palm simultaneously. When they are later + * released by Android, make sure that we drop both of these POINTER_UP events. + * Since they are classified as palm at the same time, we just need to receive a single CANCEL + * event. From MotionEvent docs: """A pointer id remains valid until the pointer eventually goes up + * (indicated by ACTION_UP or ACTION_POINTER_UP) or when the gesture is canceled (indicated by + * ACTION_CANCEL).""" + * This means that generating additional POINTER_UP events is not necessary. + * The risk here is that "oldSuppressedPointerIds" will not be correct, because it will update after + * each motion, but pointers are canceled one at a time by Android. + */ +TEST_F(PalmRejectorFakeFilterTest, TwoPointersCanceledWhenOnePointerGoesUp) { + std::vector<NotifyMotionArgs> argsList; + constexpr nsecs_t downTime = 0; + + mPalmRejector->processMotion( + generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_1_DOWN, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}})); + // Suppress both pointers!! + suppressPointerAtPosition(1414, 702); + suppressPointerAtPosition(1059, 731); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783039000, POINTER_1_UP, + {{1414.0, 702.0, 41.0}, {1059.0, 731.0, 12.0}})); + ASSERT_EQ(1u, argsList.size()); + ASSERT_EQ(CANCEL, argsList[0].action) << MotionEvent::actionToString(argsList[0].action); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags); + + // Future move events should not go to the listener. + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783049000, MOVE, {{1435.0, 755.0, 43.0}})); + ASSERT_EQ(0u, argsList.size()); + + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783059000, UP, {{1436.0, 756.0, 43.0}})); + ASSERT_EQ(0u, argsList.size()); +} + +/** + * Send 3 pointers, and then cancel one of them during a MOVE event. We should see ACTION_POINTER_UP + * generated for that. Next, another pointer is canceled during ACTION_POINTER_DOWN. For that + * pointer, we simply shouldn't send the event. + */ +TEST_F(PalmRejectorFakeFilterTest, CancelTwoPointers) { + std::vector<NotifyMotionArgs> argsList; + constexpr nsecs_t downTime = 0; + + mPalmRejector->processMotion( + generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_1_DOWN, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}})); + + // Suppress second pointer (pointer 1) + suppressPointerAtPosition(1060, 700); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, /*eventTime*/ 1, MOVE, + {{1417.0, 685.0, 41.0}, {1060, 700, 10.0}})); + ASSERT_EQ(2u, argsList.size()); + ASSERT_EQ(POINTER_1_UP, argsList[0].action); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags); + + ASSERT_EQ(MOVE, argsList[1].action) << MotionEvent::actionToString(argsList[1].action); + ASSERT_EQ(0, argsList[1].flags); + + // A new pointer goes down and gets suppressed right away. It should just be dropped + suppressPointerAtPosition(1001, 601); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_2_DOWN, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}, {1001, 601, 5}})); + + ASSERT_EQ(0u, argsList.size()); + // Likewise, pointer that's already canceled should be ignored + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_2_UP, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}, {1001, 601, 5}})); + ASSERT_EQ(0u, argsList.size()); + + // Cancel all pointers when pointer 1 goes up. Pointer 1 was already canceled earlier. + suppressPointerAtPosition(1417, 685); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_1_UP, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}})); + ASSERT_EQ(1u, argsList.size()); + ASSERT_EQ(CANCEL, argsList[0].action); +} + +} // namespace android diff --git a/services/sensorservice/AidlSensorHalWrapper.cpp b/services/sensorservice/AidlSensorHalWrapper.cpp index cdd95ca18f..09aed46a67 100644 --- a/services/sensorservice/AidlSensorHalWrapper.cpp +++ b/services/sensorservice/AidlSensorHalWrapper.cpp @@ -250,6 +250,41 @@ void convertToSensorEvent(const Event &src, sensors_event_t *dst) { break; } + case SensorType::ACCELEROMETER_LIMITED_AXES: + case SensorType::GYROSCOPE_LIMITED_AXES: + dst->limited_axes_imu.x = src.payload.get<Event::EventPayload::limitedAxesImu>().x; + dst->limited_axes_imu.y = src.payload.get<Event::EventPayload::limitedAxesImu>().y; + dst->limited_axes_imu.z = src.payload.get<Event::EventPayload::limitedAxesImu>().z; + dst->limited_axes_imu.x_supported = + src.payload.get<Event::EventPayload::limitedAxesImu>().xSupported; + dst->limited_axes_imu.y_supported = + src.payload.get<Event::EventPayload::limitedAxesImu>().ySupported; + dst->limited_axes_imu.z_supported = + src.payload.get<Event::EventPayload::limitedAxesImu>().zSupported; + break; + + case SensorType::ACCELEROMETER_LIMITED_AXES_UNCALIBRATED: + case SensorType::GYROSCOPE_LIMITED_AXES_UNCALIBRATED: + dst->limited_axes_imu_uncalibrated.x_uncalib = + src.payload.get<Event::EventPayload::limitedAxesImuUncal>().x; + dst->limited_axes_imu_uncalibrated.y_uncalib = + src.payload.get<Event::EventPayload::limitedAxesImuUncal>().y; + dst->limited_axes_imu_uncalibrated.z_uncalib = + src.payload.get<Event::EventPayload::limitedAxesImuUncal>().z; + dst->limited_axes_imu_uncalibrated.x_bias = + src.payload.get<Event::EventPayload::limitedAxesImuUncal>().xBias; + dst->limited_axes_imu_uncalibrated.y_bias = + src.payload.get<Event::EventPayload::limitedAxesImuUncal>().yBias; + dst->limited_axes_imu_uncalibrated.z_bias = + src.payload.get<Event::EventPayload::limitedAxesImuUncal>().zBias; + dst->limited_axes_imu_uncalibrated.x_supported = + src.payload.get<Event::EventPayload::limitedAxesImuUncal>().xSupported; + dst->limited_axes_imu_uncalibrated.y_supported = + src.payload.get<Event::EventPayload::limitedAxesImuUncal>().ySupported; + dst->limited_axes_imu_uncalibrated.z_supported = + src.payload.get<Event::EventPayload::limitedAxesImuUncal>().zSupported; + break; + default: { CHECK_GE((int32_t)src.sensorType, (int32_t)SensorType::DEVICE_PRIVATE_BASE); @@ -264,7 +299,7 @@ void convertFromSensorEvent(const sensors_event_t &src, Event *dst) { *dst = { .timestamp = src.timestamp, .sensorHandle = src.sensor, - .sensorType = (SensorType) src.type, + .sensorType = (SensorType)src.type, }; switch (dst->sensorType) { @@ -409,6 +444,35 @@ void convertFromSensorEvent(const sensors_event_t &src, Event *dst) { break; } + case SensorType::ACCELEROMETER_LIMITED_AXES: + case SensorType::GYROSCOPE_LIMITED_AXES: { + Event::EventPayload::LimitedAxesImu limitedAxesImu; + limitedAxesImu.x = src.limited_axes_imu.x; + limitedAxesImu.y = src.limited_axes_imu.y; + limitedAxesImu.z = src.limited_axes_imu.z; + limitedAxesImu.xSupported = src.limited_axes_imu.x_supported; + limitedAxesImu.ySupported = src.limited_axes_imu.y_supported; + limitedAxesImu.zSupported = src.limited_axes_imu.z_supported; + dst->payload.set<Event::EventPayload::Tag::limitedAxesImu>(limitedAxesImu); + break; + } + + case SensorType::ACCELEROMETER_LIMITED_AXES_UNCALIBRATED: + case SensorType::GYROSCOPE_LIMITED_AXES_UNCALIBRATED: { + Event::EventPayload::LimitedAxesImuUncal limitedAxesImuUncal; + limitedAxesImuUncal.x = src.limited_axes_imu_uncalibrated.x_uncalib; + limitedAxesImuUncal.y = src.limited_axes_imu_uncalibrated.y_uncalib; + limitedAxesImuUncal.z = src.limited_axes_imu_uncalibrated.z_uncalib; + limitedAxesImuUncal.xBias = src.limited_axes_imu_uncalibrated.x_bias; + limitedAxesImuUncal.yBias = src.limited_axes_imu_uncalibrated.y_bias; + limitedAxesImuUncal.zBias = src.limited_axes_imu_uncalibrated.z_bias; + limitedAxesImuUncal.xSupported = src.limited_axes_imu_uncalibrated.x_supported; + limitedAxesImuUncal.ySupported = src.limited_axes_imu_uncalibrated.y_supported; + limitedAxesImuUncal.zSupported = src.limited_axes_imu_uncalibrated.z_supported; + dst->payload.set<Event::EventPayload::Tag::limitedAxesImuUncal>(limitedAxesImuUncal); + break; + } + default: { CHECK_GE((int32_t)dst->sensorType, (int32_t)SensorType::DEVICE_PRIVATE_BASE); diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp index d5b629d564..3c4f8d9bae 100644 --- a/services/sensorservice/Android.bp +++ b/services/sensorservice/Android.bp @@ -17,6 +17,7 @@ cc_library_shared { "Fusion.cpp", "GravitySensor.cpp", "HidlSensorHalWrapper.cpp", + "LimitedAxesImuSensor.cpp", "LinearAccelerationSensor.cpp", "OrientationSensor.cpp", "RecentEventLogger.cpp", diff --git a/services/sensorservice/LimitedAxesImuSensor.cpp b/services/sensorservice/LimitedAxesImuSensor.cpp new file mode 100644 index 0000000000..2f91479b38 --- /dev/null +++ b/services/sensorservice/LimitedAxesImuSensor.cpp @@ -0,0 +1,134 @@ +/* + * 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. + */ + +#include <math.h> +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> + +#include <hardware/sensors.h> + +#include "LimitedAxesImuSensor.h" +#include "SensorDevice.h" +#include "SensorFusion.h" +#include "SensorServiceUtils.h" + +namespace android { + +namespace { +const sensor_t DUMMY_SENSOR = {.name = "", + .vendor = "", + .stringType = "", + .requiredPermission = ""}; +} // unnamed namespace + +LimitedAxesImuSensor::LimitedAxesImuSensor(sensor_t const* list, size_t count, + int32_t imu3dSensorType) + : BaseSensor(DUMMY_SENSOR) { + for (size_t i = 0; i < count; i++) { + if (list[i].type == imu3dSensorType) { + mImu3dSensor = Sensor(list + i); + break; + } + } + + const int32_t imuLimitedAxesSensorType = convertImu3dToLimitedAxesSensorType(imu3dSensorType); + + const sensor_t sensor = { + .name = convertLimitedAxesSensorTypeToName(imuLimitedAxesSensorType), + .vendor = "AOSP", + .version = 1, + .handle = convertLimitedAxesSensorTypeToHandle(imuLimitedAxesSensorType), + .type = imuLimitedAxesSensorType, + .maxRange = mImu3dSensor.getMaxValue(), + .resolution = mImu3dSensor.getResolution(), + .power = mImu3dSensor.getPowerUsage(), + .minDelay = mImu3dSensor.getMinDelay(), + }; + mSensor = Sensor(&sensor); +} + +bool LimitedAxesImuSensor::process(sensors_event_t* outEvent, const sensors_event_t& event) { + if (event.type == mImu3dSensor.getType()) { + *outEvent = event; + size_t imu3dDataSize = SensorServiceUtil::eventSizeBySensorType(mImu3dSensor.getType()); + outEvent->data[0 + imu3dDataSize] = 1; + outEvent->data[1 + imu3dDataSize] = 1; + outEvent->data[2 + imu3dDataSize] = 1; + outEvent->sensor = mSensor.getHandle(); + outEvent->type = mSensor.getType(); + return true; + } + return false; +} + +status_t LimitedAxesImuSensor::activate(void* ident, bool enabled) { + return mSensorDevice.activate(ident, mImu3dSensor.getHandle(), enabled); +} + +status_t LimitedAxesImuSensor::setDelay(void* ident, int /*handle*/, int64_t ns) { + return mSensorDevice.setDelay(ident, mImu3dSensor.getHandle(), ns); +} + +int32_t LimitedAxesImuSensor::convertImu3dToLimitedAxesSensorType(int32_t imu3dSensorType) { + switch (imu3dSensorType) { + case SENSOR_TYPE_ACCELEROMETER: + return SENSOR_TYPE_ACCELEROMETER_LIMITED_AXES; + case SENSOR_TYPE_GYROSCOPE: + return SENSOR_TYPE_GYROSCOPE_LIMITED_AXES; + case SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED: + return SENSOR_TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED; + case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED: + return SENSOR_TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED; + default: + return 0; + } +} + +int32_t LimitedAxesImuSensor::convertLimitedAxesSensorTypeToHandle( + int32_t imuLimitedAxesSensorType) { + switch (imuLimitedAxesSensorType) { + case SENSOR_TYPE_ACCELEROMETER_LIMITED_AXES: + return '_ala'; + case SENSOR_TYPE_GYROSCOPE_LIMITED_AXES: + return '_gla'; + case SENSOR_TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED: + return '_alc'; + case SENSOR_TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED: + return '_glc'; + default: + return 0; + } +} + +const char* LimitedAxesImuSensor::convertLimitedAxesSensorTypeToName( + int32_t imuLimitedAxesSensorType) { + switch (imuLimitedAxesSensorType) { + case SENSOR_TYPE_ACCELEROMETER_LIMITED_AXES: + return "Accelerometer Limited Axes Sensor"; + case SENSOR_TYPE_GYROSCOPE_LIMITED_AXES: + return "Gyroscope Limited Axes Sensor"; + case SENSOR_TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED: + return "Accelerometer Limited Axes Uncalibrated Sensor"; + case SENSOR_TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED: + return "Gyroscope Limited Axes Uncalibrated Sensor"; + default: + return ""; + } +} + +}; // namespace android diff --git a/services/sensorservice/LimitedAxesImuSensor.h b/services/sensorservice/LimitedAxesImuSensor.h new file mode 100644 index 0000000000..fd46a9860a --- /dev/null +++ b/services/sensorservice/LimitedAxesImuSensor.h @@ -0,0 +1,46 @@ +/* + * 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. + */ + +#pragma once + +#include <stdint.h> +#include <sys/types.h> + +#include <sensor/Sensor.h> + +#include "SensorInterface.h" + +namespace android { + +class SensorDevice; + +class LimitedAxesImuSensor : public BaseSensor { + Sensor mImu3dSensor; + +public: + LimitedAxesImuSensor(sensor_t const* list, size_t count, int32_t imuSensorType); + virtual bool process(sensors_event_t* outEvent, const sensors_event_t& event) override; + virtual status_t activate(void* ident, bool enabled) override; + virtual status_t setDelay(void* ident, int handle, int64_t ns) override; + virtual bool isVirtual() const override { return true; } + +private: + int32_t convertImu3dToLimitedAxesSensorType(int32_t imu3dSensorType); + int32_t convertLimitedAxesSensorTypeToHandle(int32_t imuLimitedAxesSensorType); + const char* convertLimitedAxesSensorTypeToName(int32_t imuLimitedAxesSensorType); +}; + +}; // namespace android
\ No newline at end of file diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 517d383b5a..971491dbef 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -38,6 +38,7 @@ #include "BatteryService.h" #include "CorrectedGyroSensor.h" #include "GravitySensor.h" +#include "LimitedAxesImuSensor.h" #include "LinearAccelerationSensor.h" #include "OrientationSensor.h" #include "RotationVectorSensor.h" @@ -101,6 +102,33 @@ static const String16 sDumpPermission("android.permission.DUMP"); static const String16 sLocationHardwarePermission("android.permission.LOCATION_HARDWARE"); static const String16 sManageSensorsPermission("android.permission.MANAGE_SENSORS"); +static bool isAutomotive() { + sp<IServiceManager> serviceManager = defaultServiceManager(); + if (serviceManager.get() == nullptr) { + ALOGE("%s: unable to access native ServiceManager", __func__); + return false; + } + + sp<content::pm::IPackageManagerNative> packageManager; + sp<IBinder> binder = serviceManager->waitForService(String16("package_native")); + packageManager = interface_cast<content::pm::IPackageManagerNative>(binder); + if (packageManager == nullptr) { + ALOGE("%s: unable to access native PackageManager", __func__); + return false; + } + + bool isAutomotive = false; + binder::Status status = + packageManager->hasSystemFeature(String16("android.hardware.type.automotive"), 0, + &isAutomotive); + if (!status.isOk()) { + ALOGE("%s: hasSystemFeature failed: %s", __func__, status.exceptionMessage().c_str()); + return false; + } + + return isAutomotive; +} + SensorService::SensorService() : mInitCheck(NO_INIT), mSocketBufferSize(SOCKET_BUFFER_SIZE_NON_BATCHED), mWakeLockAcquired(false), mLastReportedProxIsActive(false) { @@ -165,6 +193,8 @@ void SensorService::onFirstRef() { ssize_t count = dev.getSensorList(&list); if (count > 0) { bool hasGyro = false, hasAccel = false, hasMag = false; + bool hasGyroUncalibrated = false; + bool hasAccelUncalibrated = false; uint32_t virtualSensorsNeeds = (1<<SENSOR_TYPE_GRAVITY) | (1<<SENSOR_TYPE_LINEAR_ACCELERATION) | @@ -179,13 +209,18 @@ void SensorService::onFirstRef() { case SENSOR_TYPE_ACCELEROMETER: hasAccel = true; break; + case SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED: + hasAccelUncalibrated = true; + break; case SENSOR_TYPE_MAGNETIC_FIELD: hasMag = true; break; case SENSOR_TYPE_GYROSCOPE: - case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED: hasGyro = true; break; + case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED: + hasGyroUncalibrated = true; + break; case SENSOR_TYPE_GRAVITY: case SENSOR_TYPE_LINEAR_ACCELERATION: case SENSOR_TYPE_ROTATION_VECTOR: @@ -216,7 +251,7 @@ void SensorService::onFirstRef() { // registered) SensorFusion::getInstance(); - if (hasGyro && hasAccel && hasMag) { + if ((hasGyro || hasGyroUncalibrated) && hasAccel && hasMag) { // Add Android virtual sensors if they're not already // available in the HAL bool needRotationVector = @@ -230,7 +265,7 @@ void SensorService::onFirstRef() { registerSensor( new GyroDriftSensor(), true, true); } - if (hasAccel && hasGyro) { + if (hasAccel && (hasGyro || hasGyroUncalibrated)) { bool needGravitySensor = (virtualSensorsNeeds & (1<<SENSOR_TYPE_GRAVITY)) != 0; registerSensor(new GravitySensor(list, count), !needGravitySensor, true); @@ -250,6 +285,30 @@ void SensorService::onFirstRef() { registerSensor(new GeoMagRotationVectorSensor(), !needGeoMagRotationVector, true); } + if (isAutomotive()) { + if (hasAccel) { + registerSensor(new LimitedAxesImuSensor(list, count, SENSOR_TYPE_ACCELEROMETER), + /*isDebug=*/false, /*isVirtual=*/true); + } + + if (hasGyro) { + registerSensor(new LimitedAxesImuSensor(list, count, SENSOR_TYPE_GYROSCOPE), + /*isDebug=*/false, /*isVirtual=*/true); + } + + if (hasAccelUncalibrated) { + registerSensor(new LimitedAxesImuSensor(list, count, + SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED), + /*isDebug=*/false, /*isVirtual=*/true); + } + + if (hasGyroUncalibrated) { + registerSensor(new LimitedAxesImuSensor(list, count, + SENSOR_TYPE_GYROSCOPE_UNCALIBRATED), + /*isDebug=*/false, /*isVirtual=*/true); + } + } + // Check if the device really supports batching by looking at the FIFO event // counts for each sensor. bool batchingSupported = false; diff --git a/services/sensorservice/SensorServiceUtils.cpp b/services/sensorservice/SensorServiceUtils.cpp index baa01c9ce3..c3043811f1 100644 --- a/services/sensorservice/SensorServiceUtils.cpp +++ b/services/sensorservice/SensorServiceUtils.cpp @@ -30,12 +30,18 @@ size_t eventSizeBySensorType(int type) { case SENSOR_TYPE_POSE_6DOF: return 16; + case SENSOR_TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED: + case SENSOR_TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED: + return 9; + case SENSOR_TYPE_ROTATION_VECTOR: case SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR: return 5; case SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED: case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED: + case SENSOR_TYPE_ACCELEROMETER_LIMITED_AXES: + case SENSOR_TYPE_GYROSCOPE_LIMITED_AXES: return 6; case SENSOR_TYPE_GAME_ROTATION_VECTOR: diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index af0f5240d3..d9958f31c5 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -118,9 +118,6 @@ cc_defaults { cc_defaults { name: "libsurfaceflinger_production_defaults", defaults: ["libsurfaceflinger_defaults"], - cflags: [ - "-fvisibility=hidden", - ], lto: { thin: true, }, diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp index e797b5d4bb..b7e2ff3b34 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp @@ -306,7 +306,7 @@ void BufferLayer::preparePerFrameCompositionState() { : aidl::android::hardware::graphics::composer3::Composition::DEVICE; } - compositionState->buffer = mBufferInfo.mBuffer->getBuffer(); + compositionState->buffer = getBuffer(); compositionState->bufferSlot = (mBufferInfo.mBufferSlot == BufferQueue::INVALID_BUFFER_SLOT) ? 0 : mBufferInfo.mBufferSlot; diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp index 0c938723ba..40fc342ec8 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -316,8 +316,7 @@ bool BufferStateLayer::updateGeometry() { return assignTransform(&mDrawingState.transform, t); } -bool BufferStateLayer::setMatrix(const layer_state_t::matrix22_t& matrix, - bool allowNonRectPreservingTransforms) { +bool BufferStateLayer::setMatrix(const layer_state_t::matrix22_t& matrix) { if (mRequestedTransform.dsdx() == matrix.dsdx && mRequestedTransform.dtdy() == matrix.dtdy && mRequestedTransform.dtdx() == matrix.dtdx && mRequestedTransform.dsdy() == matrix.dsdy) { return false; @@ -326,12 +325,6 @@ bool BufferStateLayer::setMatrix(const layer_state_t::matrix22_t& matrix, ui::Transform t; t.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy); - if (!allowNonRectPreservingTransforms && !t.preserveRects()) { - ALOGW("Attempt to set rotation matrix without permission ACCESS_SURFACE_FLINGER nor " - "ROTATE_SURFACE_FLINGER ignored"); - return false; - } - mRequestedTransform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy); mDrawingState.sequence++; diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h index 2f613d7f26..248e013ef3 100644 --- a/services/surfaceflinger/BufferStateLayer.h +++ b/services/surfaceflinger/BufferStateLayer.h @@ -70,8 +70,7 @@ public: bool addFrameEvent(const sp<Fence>& acquireFence, nsecs_t postedTime, nsecs_t requestedPresentTime) override; bool setPosition(float /*x*/, float /*y*/) override; - bool setMatrix(const layer_state_t::matrix22_t& /*matrix*/, - bool /*allowNonRectPreservingTransforms*/); + bool setMatrix(const layer_state_t::matrix22_t& /*matrix*/); // Override to ignore legacy layer state properties that are not used by BufferStateLayer bool setSize(uint32_t /*w*/, uint32_t /*h*/) override { return false; } diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h index 16cb41b024..47aacc99f6 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h @@ -56,6 +56,9 @@ public: // similar requests if needed. virtual void createClientCompositionCache(uint32_t cacheSize) = 0; + // Returns the boot display mode preferred by HWC. + virtual int32_t getPreferredBootModeId() const = 0; + protected: ~Display() = default; }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h index b777241af2..ebe112b20b 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h @@ -62,6 +62,7 @@ public: bool isSecure() const override; bool isVirtual() const override; void disconnect() override; + int32_t getPreferredBootModeId() const override; void createDisplayColorProfile( const compositionengine::DisplayColorProfileCreationArgs&) override; void createRenderSurface(const compositionengine::RenderSurfaceCreationArgs&) override; @@ -87,6 +88,7 @@ private: DisplayId mId; bool mIsDisconnected = false; Hwc2::PowerAdvisor* mPowerAdvisor = nullptr; + int32_t mPreferredBootDisplayModeId = -1; }; // This template factory function standardizes the implementation details of the diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h index 49cb91291f..08cfaa6198 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h @@ -90,6 +90,10 @@ struct OutputLayerCompositionState { // The dataspace for this layer ui::Dataspace dataspace{ui::Dataspace::UNKNOWN}; + // A hint to the HWC that this region is transparent and may be skipped in + // order to save power. + Region outputSpaceBlockingRegionHint; + // Overrides the buffer, acquire fence, and display frame stored in LayerFECompositionState struct { std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h index 6fb3e08e7a..84afb59510 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h @@ -34,6 +34,7 @@ public: MOCK_CONST_METHOD0(getId, DisplayId()); MOCK_CONST_METHOD0(isSecure, bool()); MOCK_CONST_METHOD0(isVirtual, bool()); + MOCK_CONST_METHOD0(getPreferredBootModeId, int32_t()); MOCK_METHOD0(disconnect, void()); diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp index 186e191447..29c146b013 100644 --- a/services/surfaceflinger/CompositionEngine/src/Display.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp @@ -57,6 +57,16 @@ void Display::setConfiguration(const compositionengine::DisplayCreationArgs& arg editState().isSecure = args.isSecure; editState().displaySpace.setBounds(args.pixels); setName(args.name); + bool isBootModeSupported = getCompositionEngine().getHwComposer().getBootDisplayModeSupport(); + const auto physicalId = PhysicalDisplayId::tryCast(mId); + if (!physicalId || !isBootModeSupported) { + return; + } + std::optional<hal::HWConfigId> preferredBootModeId = + getCompositionEngine().getHwComposer().getPreferredBootDisplayMode(*physicalId); + if (preferredBootModeId.has_value()) { + mPreferredBootDisplayModeId = static_cast<int32_t>(preferredBootModeId.value()); + } } bool Display::isValid() const { @@ -79,6 +89,10 @@ std::optional<DisplayId> Display::getDisplayId() const { return mId; } +int32_t Display::getPreferredBootModeId() const { + return mPreferredBootDisplayModeId; +} + void Display::disconnect() { if (mIsDisconnected) { return; diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index 192ee047ee..65f9731afe 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -50,6 +50,8 @@ #include "TracedOrdinal.h" +using aidl::android::hardware::graphics::composer3::Composition; + namespace android::compositionengine { Output::~Output() = default; @@ -529,11 +531,18 @@ void Output::ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>& layerFE, /* * transparentRegion: area of a surface that is hinted to be completely - * transparent. This is only used to tell when the layer has no visible non- - * transparent regions and can be removed from the layer list. It does not - * affect the visibleRegion of this layer or any layers beneath it. The hint - * may not be correct if apps don't respect the SurfaceView restrictions - * (which, sadly, some don't). + * transparent. + * This is used to tell when the layer has no visible non-transparent + * regions and can be removed from the layer list. It does not affect the + * visibleRegion of this layer or any layers beneath it. The hint may not + * be correct if apps don't respect the SurfaceView restrictions (which, + * sadly, some don't). + * + * In addition, it is used on DISPLAY_DECORATION layers to specify the + * blockingRegion, allowing the DPU to skip it to save power. Once we have + * hardware that supports a blockingRegion on frames with AFBC, it may be + * useful to use this for other layers, too, so long as we can prevent + * regressions on b/7179570. */ Region transparentRegion; @@ -674,6 +683,9 @@ void Output::ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>& layerFE, outputLayerState.outputSpaceVisibleRegion = outputState.transform.transform( visibleNonShadowRegion.intersect(outputState.layerStackSpace.getContent())); outputLayerState.shadowRegion = shadowRegion; + outputLayerState.outputSpaceBlockingRegionHint = + layerFEState->compositionType == Composition::DISPLAY_DECORATION ? transparentRegion + : Region(); } void Output::setReleasedLayers(const compositionengine::CompositionRefreshArgs&) { @@ -1059,9 +1071,11 @@ std::optional<base::unique_fd> Output::composeSurfaces( // If we have a valid current display brightness use that, otherwise fall back to the // display's max desired - clientCompositionDisplay.maxLuminance = outputState.displayBrightnessNits > 0.f + clientCompositionDisplay.currentLuminanceNits = outputState.displayBrightnessNits > 0.f ? outputState.displayBrightnessNits : mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance(); + clientCompositionDisplay.maxLuminance = + mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance(); clientCompositionDisplay.targetLuminanceNits = outputState.clientTargetWhitePointNits; // Compute the global color transform matrix. diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp index 95cc5a8984..4ccf11f528 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp @@ -484,6 +484,14 @@ void OutputLayer::writeOutputDependentPerFrameStateToHWC(HWC2::Layer* hwcLayer) visibleRegion.dump(LOG_TAG); } + if (auto error = + hwcLayer->setBlockingRegion(outputDependentState.outputSpaceBlockingRegionHint); + error != hal::Error::NONE) { + ALOGE("[%s] Failed to set blocking region: %s (%d)", getLayerFE().getDebugName(), + to_string(error).c_str(), static_cast<int32_t>(error)); + outputDependentState.outputSpaceBlockingRegionHint.dump(LOG_TAG); + } + const auto dataspace = outputDependentState.overrideInfo.buffer ? outputDependentState.overrideInfo.dataspace : outputDependentState.dataspace; diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp index 0918510eef..8d26747030 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp @@ -401,6 +401,19 @@ bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers return true; } +namespace { +bool isDisplayDecoration(const CachedSet& cachedSet) { + return cachedSet.getLayerCount() == 1 && + cachedSet.getFirstLayer() + .getState() + ->getOutputLayer() + ->getLayerFE() + .getCompositionState() + ->compositionType == + aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION; +} +} // namespace + std::vector<Flattener::Run> Flattener::findCandidateRuns(time_point now) const { ATRACE_CALL(); std::vector<Run> runs; @@ -424,7 +437,7 @@ std::vector<Flattener::Run> Flattener::findCandidateRuns(time_point now) const { } if (layerIsInactive && (firstLayer || runHasFirstLayer || !layerHasBlur) && - !currentSet->hasUnsupportedDataspace()) { + !currentSet->hasUnsupportedDataspace() && !isDisplayDecoration(*currentSet)) { if (isPartOfRun) { builder.increment(); } else { diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp index b27e30cd63..125ce7468e 100644 --- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp @@ -164,6 +164,7 @@ struct DisplayTestCommon : public testing::Test { EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine)); EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); + EXPECT_CALL(mHwComposer, getBootDisplayModeSupport()).WillRepeatedly(Return(false)); } DisplayCreationArgs getDisplayCreationArgsForPhysicalDisplay() { @@ -971,7 +972,8 @@ struct DisplayFunctionalTest : public testing::Test { DisplayFunctionalTest() { EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer)); - + mDisplay = createDisplay(); + mRenderSurface = createRenderSurface(); mDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface)); } @@ -980,24 +982,29 @@ struct DisplayFunctionalTest : public testing::Test { NiceMock<mock::CompositionEngine> mCompositionEngine; sp<mock::NativeWindow> mNativeWindow = new NiceMock<mock::NativeWindow>(); sp<mock::DisplaySurface> mDisplaySurface = new NiceMock<mock::DisplaySurface>(); + std::shared_ptr<Display> mDisplay; + impl::RenderSurface* mRenderSurface; + + std::shared_ptr<Display> createDisplay() { + return impl::createDisplayTemplated<Display>(mCompositionEngine, + DisplayCreationArgsBuilder() + .setId(DEFAULT_DISPLAY_ID) + .setPixels(DEFAULT_RESOLUTION) + .setIsSecure(true) + .setPowerAdvisor(&mPowerAdvisor) + .build()); + ; + } - std::shared_ptr<Display> mDisplay = impl::createDisplayTemplated< - Display>(mCompositionEngine, - DisplayCreationArgsBuilder() - .setId(DEFAULT_DISPLAY_ID) - .setPixels(DEFAULT_RESOLUTION) - .setIsSecure(true) - .setPowerAdvisor(&mPowerAdvisor) - .build()); - - impl::RenderSurface* mRenderSurface = - new impl::RenderSurface{mCompositionEngine, *mDisplay, - RenderSurfaceCreationArgsBuilder() - .setDisplayWidth(DEFAULT_RESOLUTION.width) - .setDisplayHeight(DEFAULT_RESOLUTION.height) - .setNativeWindow(mNativeWindow) - .setDisplaySurface(mDisplaySurface) - .build()}; + impl::RenderSurface* createRenderSurface() { + return new impl::RenderSurface{mCompositionEngine, *mDisplay, + RenderSurfaceCreationArgsBuilder() + .setDisplayWidth(DEFAULT_RESOLUTION.width) + .setDisplayHeight(DEFAULT_RESOLUTION.height) + .setNativeWindow(mNativeWindow) + .setDisplaySurface(mDisplaySurface) + .build()}; + } }; TEST_F(DisplayFunctionalTest, postFramebufferCriticalCallsAreOrdered) { diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index bd3022b425..660f66438f 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -39,7 +39,7 @@ public: HWComposer(); ~HWComposer() override; - MOCK_METHOD1(setCallback, void(HWC2::ComposerCallback*)); + MOCK_METHOD1(setCallback, void(HWC2::ComposerCallback&)); MOCK_CONST_METHOD3(getDisplayIdentificationData, bool(hal::HWDisplayId, uint8_t*, DisplayIdentificationData*)); MOCK_CONST_METHOD1(hasCapability, bool(hal::Capability)); @@ -108,7 +108,8 @@ public: hal::VsyncPeriodChangeTimeline*)); MOCK_METHOD2(setBootDisplayMode, status_t(PhysicalDisplayId, hal::HWConfigId)); MOCK_METHOD1(clearBootDisplayMode, status_t(PhysicalDisplayId)); - MOCK_METHOD1(getPreferredBootDisplayMode, hal::HWConfigId(PhysicalDisplayId)); + MOCK_METHOD1(getPreferredBootDisplayMode, std::optional<hal::HWConfigId>(PhysicalDisplayId)); + MOCK_METHOD0(getBootDisplayModeSupport, bool()); MOCK_METHOD2(setAutoLowLatencyMode, status_t(PhysicalDisplayId, bool)); MOCK_METHOD2(getSupportedContentTypes, status_t(PhysicalDisplayId, std::vector<hal::ContentType>*)); @@ -127,7 +128,6 @@ public: (const, override)); MOCK_METHOD(std::optional<hal::HWDisplayId>, fromPhysicalDisplayId, (PhysicalDisplayId), (const, override)); - MOCK_METHOD(bool, getBootDisplayModeSupport, (), (override)); }; } // namespace mock diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp index f67bf0670f..82dcc66833 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp @@ -846,7 +846,8 @@ struct OutputLayerWriteStateToHWCTest : public OutputLayerTest { ui::Dataspace dataspace = kDataspace, const Region& visibleRegion = kOutputSpaceVisibleRegion, const Region& surfaceDamage = kSurfaceDamage, - float whitePointNits = kWhitePointNits) { + float whitePointNits = kWhitePointNits, + const Region& blockingRegion = Region()) { EXPECT_CALL(*mHwcLayer, setVisibleRegion(RegionEq(visibleRegion))).WillOnce(Return(kError)); EXPECT_CALL(*mHwcLayer, setDataspace(dataspace)).WillOnce(Return(kError)); EXPECT_CALL(*mHwcLayer, setWhitePointNits(whitePointNits)).WillOnce(Return(kError)); @@ -855,6 +856,8 @@ struct OutputLayerWriteStateToHWCTest : public OutputLayerTest { ? hal::Error::UNSUPPORTED : hal::Error::NONE)); EXPECT_CALL(*mHwcLayer, setSurfaceDamage(RegionEq(surfaceDamage))).WillOnce(Return(kError)); + EXPECT_CALL(*mHwcLayer, setBlockingRegion(RegionEq(blockingRegion))) + .WillOnce(Return(kError)); } void expectSetCompositionTypeCall(Composition compositionType) { @@ -1278,6 +1281,23 @@ TEST_F(OutputLayerWriteStateToHWCTest, roundedCornersPeekingThroughAllowsDeviceC EXPECT_EQ(Composition::DEVICE, mOutputLayer.getState().hwc->hwcCompositionType); } +TEST_F(OutputLayerWriteStateToHWCTest, setBlockingRegion) { + mLayerFEState.compositionType = Composition::DISPLAY_DECORATION; + const auto blockingRegion = Region(Rect(0, 0, 1000, 1000)); + mOutputLayer.editState().outputSpaceBlockingRegionHint = blockingRegion; + + expectGeometryCommonCalls(); + expectPerFrameCommonCalls(SimulateUnsupported::None, kDataspace, kOutputSpaceVisibleRegion, + kSurfaceDamage, kWhitePointNits, blockingRegion); + expectSetHdrMetadataAndBufferCalls(); + EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false)); + expectSetCompositionTypeCall(Composition::DISPLAY_DECORATION); + + mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, + /*zIsOverridden*/ false, /*isPeekingThrough*/ + false); +} + /* * OutputLayer::writeCursorPositionToHWC() */ diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index 6e6552f9e1..e72bc9f66d 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -1291,7 +1291,7 @@ struct OutputEnsureOutputLayerIfVisibleTest : public testing::Test { mLayer.layerFEState.contentDirty = true; mLayer.layerFEState.geomLayerBounds = FloatRect{0, 0, 100, 200}; mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200); - mLayer.layerFEState.transparentRegionHint = Region(Rect(0, 0, 100, 100)); + mLayer.layerFEState.transparentRegionHint = kTransparentRegionHint; mLayer.outputLayerState.visibleRegion = Region(Rect(0, 0, 50, 200)); mLayer.outputLayerState.coveredRegion = Region(Rect(50, 0, 100, 200)); @@ -1309,6 +1309,7 @@ struct OutputEnsureOutputLayerIfVisibleTest : public testing::Test { static const Region kRightHalfBoundsNoRotation; static const Region kLowerHalfBoundsNoRotation; static const Region kFullBounds90Rotation; + static const Region kTransparentRegionHint; StrictMock<OutputPartialMock> mOutput; LayerFESet mGeomSnapshots; @@ -1326,6 +1327,8 @@ const Region OutputEnsureOutputLayerIfVisibleTest::kLowerHalfBoundsNoRotation = Region(Rect(50, 0, 100, 200)); const Region OutputEnsureOutputLayerIfVisibleTest::kFullBounds90Rotation = Region(Rect(0, 0, 200, 100)); +const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHint = + Region(Rect(0, 0, 100, 100)); TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerIncluded) { EXPECT_CALL(mOutput, includesLayer(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false)); @@ -1749,6 +1752,33 @@ TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesNotSoEarlyOutifLayerWithShadow ensureOutputLayerIfVisible(); } +TEST_F(OutputEnsureOutputLayerIfVisibleTest, displayDecorSetsBlockingFromTransparentRegion) { + mLayer.layerFEState.isOpaque = false; + mLayer.layerFEState.contentDirty = true; + mLayer.layerFEState.compositionType = + aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION; + + EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u)); + EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE))) + .WillOnce(Return(&mLayer.outputLayer)); + ensureOutputLayerIfVisible(); + + EXPECT_THAT(mLayer.outputLayerState.outputSpaceBlockingRegionHint, + RegionEq(kTransparentRegionHint)); +} + +TEST_F(OutputEnsureOutputLayerIfVisibleTest, normalLayersDoNotSetBlockingRegion) { + mLayer.layerFEState.isOpaque = false; + mLayer.layerFEState.contentDirty = true; + + EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u)); + EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE))) + .WillOnce(Return(&mLayer.outputLayer)); + ensureOutputLayerIfVisible(); + + EXPECT_THAT(mLayer.outputLayerState.outputSpaceBlockingRegionHint, RegionEq(Region())); +} + /* * Output::present() */ @@ -3072,6 +3102,8 @@ struct OutputComposeSurfacesTest : public testing::Test { static constexpr float kDefaultMaxLuminance = 0.9f; static constexpr float kDefaultAvgLuminance = 0.7f; static constexpr float kDefaultMinLuminance = 0.1f; + static constexpr float kUnknownLuminance = -1.f; + static constexpr float kDisplayLuminance = 80.f; static constexpr float kClientTargetLuminanceNits = 200.f; static const Rect kDefaultOutputFrame; @@ -3105,6 +3137,7 @@ const Rect OutputComposeSurfacesTest::kDefaultOutputDestinationClip{1013, 1014, const mat4 OutputComposeSurfacesTest::kDefaultColorTransformMat{mat4() * 0.5f}; const compositionengine::CompositionRefreshArgs OutputComposeSurfacesTest::kDefaultRefreshArgs; const Region OutputComposeSurfacesTest::kDebugRegion{Rect{100, 101, 102, 103}}; + const HdrCapabilities OutputComposeSurfacesTest:: kHdrCapabilities{{}, OutputComposeSurfacesTest::kDefaultMaxLuminance, @@ -3408,6 +3441,14 @@ struct OutputComposeSurfacesTest_UsesExpectedDisplaySettings : public OutputComp auto andIfUsesHdr(bool used) { EXPECT_CALL(*getInstance()->mDisplayColorProfile, hasWideColorGamut()) .WillOnce(Return(used)); + return nextState<OutputWithDisplayBrightnessNits>(); + } + }; + + struct OutputWithDisplayBrightnessNits + : public CallOrderStateMachineHelper<TestType, OutputWithDisplayBrightnessNits> { + auto withDisplayBrightnessNits(float nits) { + getInstance()->mOutput.mState.displayBrightnessNits = nits; return nextState<SkipColorTransformState>(); } }; @@ -3439,11 +3480,34 @@ struct OutputComposeSurfacesTest_UsesExpectedDisplaySettings : public OutputComp TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrMixedComposition) { verify().ifMixedCompositionIs(true) .andIfUsesHdr(true) + .withDisplayBrightnessNits(kUnknownLuminance) + .andIfSkipColorTransform(false) + .thenExpectDisplaySettingsUsed({.physicalDisplay = kDefaultOutputDestinationClip, + .clip = kDefaultOutputViewport, + .maxLuminance = kDefaultMaxLuminance, + .currentLuminanceNits = kDefaultMaxLuminance, + .outputDataspace = kDefaultOutputDataspace, + .colorTransform = mat4(), + .orientation = kDefaultOutputOrientationFlags, + .targetLuminanceNits = kClientTargetLuminanceNits}) + .execute() + .expectAFenceWasReturned(); +} + +TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, + forHdrMixedCompositionWithDisplayBrightness) { + verify().ifMixedCompositionIs(true) + .andIfUsesHdr(true) + .withDisplayBrightnessNits(kDisplayLuminance) .andIfSkipColorTransform(false) - .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport, - kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(), - kDefaultOutputOrientationFlags, - kClientTargetLuminanceNits}) + .thenExpectDisplaySettingsUsed({.physicalDisplay = kDefaultOutputDestinationClip, + .clip = kDefaultOutputViewport, + .maxLuminance = kDefaultMaxLuminance, + .currentLuminanceNits = kDisplayLuminance, + .outputDataspace = kDefaultOutputDataspace, + .colorTransform = mat4(), + .orientation = kDefaultOutputOrientationFlags, + .targetLuminanceNits = kClientTargetLuminanceNits}) .execute() .expectAFenceWasReturned(); } @@ -3451,11 +3515,16 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrMixedComposi TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrMixedComposition) { verify().ifMixedCompositionIs(true) .andIfUsesHdr(false) + .withDisplayBrightnessNits(kUnknownLuminance) .andIfSkipColorTransform(false) - .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport, - kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(), - kDefaultOutputOrientationFlags, - kClientTargetLuminanceNits}) + .thenExpectDisplaySettingsUsed({.physicalDisplay = kDefaultOutputDestinationClip, + .clip = kDefaultOutputViewport, + .maxLuminance = kDefaultMaxLuminance, + .currentLuminanceNits = kDefaultMaxLuminance, + .outputDataspace = kDefaultOutputDataspace, + .colorTransform = mat4(), + .orientation = kDefaultOutputOrientationFlags, + .targetLuminanceNits = kClientTargetLuminanceNits}) .execute() .expectAFenceWasReturned(); } @@ -3463,11 +3532,16 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrMixedComp TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrOnlyClientComposition) { verify().ifMixedCompositionIs(false) .andIfUsesHdr(true) + .withDisplayBrightnessNits(kUnknownLuminance) .andIfSkipColorTransform(false) - .thenExpectDisplaySettingsUsed( - {kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance, - kDefaultOutputDataspace, kDefaultColorTransformMat, - kDefaultOutputOrientationFlags, kClientTargetLuminanceNits}) + .thenExpectDisplaySettingsUsed({.physicalDisplay = kDefaultOutputDestinationClip, + .clip = kDefaultOutputViewport, + .maxLuminance = kDefaultMaxLuminance, + .currentLuminanceNits = kDefaultMaxLuminance, + .outputDataspace = kDefaultOutputDataspace, + .colorTransform = kDefaultColorTransformMat, + .orientation = kDefaultOutputOrientationFlags, + .targetLuminanceNits = kClientTargetLuminanceNits}) .execute() .expectAFenceWasReturned(); } @@ -3475,11 +3549,16 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrOnlyClientCo TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrOnlyClientComposition) { verify().ifMixedCompositionIs(false) .andIfUsesHdr(false) + .withDisplayBrightnessNits(kUnknownLuminance) .andIfSkipColorTransform(false) - .thenExpectDisplaySettingsUsed( - {kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance, - kDefaultOutputDataspace, kDefaultColorTransformMat, - kDefaultOutputOrientationFlags, kClientTargetLuminanceNits}) + .thenExpectDisplaySettingsUsed({.physicalDisplay = kDefaultOutputDestinationClip, + .clip = kDefaultOutputViewport, + .maxLuminance = kDefaultMaxLuminance, + .currentLuminanceNits = kDefaultMaxLuminance, + .outputDataspace = kDefaultOutputDataspace, + .colorTransform = kDefaultColorTransformMat, + .orientation = kDefaultOutputOrientationFlags, + .targetLuminanceNits = kClientTargetLuminanceNits}) .execute() .expectAFenceWasReturned(); } @@ -3488,11 +3567,16 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, usesExpectedDisplaySettingsForHdrOnlyClientCompositionWithSkipClientTransform) { verify().ifMixedCompositionIs(false) .andIfUsesHdr(true) + .withDisplayBrightnessNits(kUnknownLuminance) .andIfSkipColorTransform(true) - .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport, - kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(), - kDefaultOutputOrientationFlags, - kClientTargetLuminanceNits}) + .thenExpectDisplaySettingsUsed({.physicalDisplay = kDefaultOutputDestinationClip, + .clip = kDefaultOutputViewport, + .maxLuminance = kDefaultMaxLuminance, + .currentLuminanceNits = kDefaultMaxLuminance, + .outputDataspace = kDefaultOutputDataspace, + .colorTransform = mat4(), + .orientation = kDefaultOutputOrientationFlags, + .targetLuminanceNits = kClientTargetLuminanceNits}) .execute() .expectAFenceWasReturned(); } diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp index 58dc244402..656ef9abcb 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp @@ -1337,5 +1337,56 @@ TEST_F(FlattenerTest, flattenLayers_skipsColorLayers) { EXPECT_NE(nullptr, overrideBuffer4); } +TEST_F(FlattenerTest, flattenLayers_skips_DISPLAY_DECORATION) { + auto& layerState1 = mTestLayers[0]->layerState; + const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer; + + auto& layerState2 = mTestLayers[1]->layerState; + const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer; + + // The third layer uses DISPLAY_DECORATION, which should never be cached. + auto& layerState3 = mTestLayers[2]->layerState; + const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer; + mTestLayers[2]->layerFECompositionState.compositionType = + aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION; + mTestLayers[2]->layerState->update(&mTestLayers[2]->outputLayer); + + const std::vector<const LayerState*> layers = { + layerState1.get(), + layerState2.get(), + layerState3.get(), + }; + + initializeFlattener(layers); + + mTime += 200ms; + initializeOverrideBuffer(layers); + EXPECT_EQ(getNonBufferHash(layers), + mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); + + // This will render a CachedSet. + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + .WillOnce(Return(ByMove( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + mFlattener->renderCachedSets(mOutputState, std::nullopt); + + // We've rendered a CachedSet, but we haven't merged it in. + EXPECT_EQ(nullptr, overrideBuffer1); + EXPECT_EQ(nullptr, overrideBuffer2); + EXPECT_EQ(nullptr, overrideBuffer3); + + // This time we merge the CachedSet in, so we have a new hash, and we should + // only have two sets. + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0); + initializeOverrideBuffer(layers); + EXPECT_NE(getNonBufferHash(layers), + mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); + mFlattener->renderCachedSets(mOutputState, std::nullopt); + + EXPECT_NE(nullptr, overrideBuffer1); + EXPECT_EQ(overrideBuffer1, overrideBuffer2); + EXPECT_EQ(nullptr, overrideBuffer3); +} + } // namespace } // namespace android::compositionengine diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index a36ea72b9f..f542161b93 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -92,10 +92,12 @@ DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs& args) } mCompositionDisplay->createDisplayColorProfile( - compositionengine::DisplayColorProfileCreationArgs{args.hasWideColorGamut, - std::move(args.hdrCapabilities), - args.supportedPerFrameMetadata, - args.hwcColorModes}); + compositionengine::DisplayColorProfileCreationArgsBuilder() + .setHasWideColorGamut(args.hasWideColorGamut) + .setHdrCapabilities(std::move(args.hdrCapabilities)) + .setSupportedPerFrameMetadata(args.supportedPerFrameMetadata) + .setHwcColorModes(std::move(args.hwcColorModes)) + .Build()); if (!mCompositionDisplay->isValid()) { ALOGE("Composition Display did not validate!"); @@ -454,6 +456,10 @@ HdrCapabilities DisplayDevice::getHdrCapabilities() const { capabilities.getDesiredMinLuminance()); } +ui::DisplayModeId DisplayDevice::getPreferredBootModeId() const { + return mCompositionDisplay->getPreferredBootModeId(); +} + void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinnner) { if (!enable) { mRefreshRateOverlay.reset(); diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index 0c9063dbdb..3cae30fe92 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -157,6 +157,9 @@ public: // respectively if hardware composer doesn't return meaningful values. HdrCapabilities getHdrCapabilities() const; + // Returns the boot display mode preferred by the implementation. + ui::DisplayModeId getPreferredBootModeId() const; + // Return true if intent is supported by the display. bool hasRenderIntent(ui::RenderIntent intent) const; diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index 1448e5644e..b1057c3dd1 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -30,6 +30,8 @@ #include <algorithm> #include <cinttypes> +#include "HWC2.h" + namespace android { using hardware::hidl_handle; @@ -169,40 +171,47 @@ mat4 makeMat4(std::vector<float> in) { class AidlIComposerCallbackWrapper : public BnComposerCallback { public: - AidlIComposerCallbackWrapper(sp<V2_4::IComposerCallback> callback) - : mCallback(std::move(callback)) {} + AidlIComposerCallbackWrapper(HWC2::ComposerCallback& callback) : mCallback(callback) {} ::ndk::ScopedAStatus onHotplug(int64_t in_display, bool in_connected) override { const auto connection = in_connected ? V2_4::IComposerCallback::Connection::CONNECTED : V2_4::IComposerCallback::Connection::DISCONNECTED; - mCallback->onHotplug(translate<Display>(in_display), connection); + mCallback.onComposerHalHotplug(translate<Display>(in_display), connection); return ::ndk::ScopedAStatus::ok(); } ::ndk::ScopedAStatus onRefresh(int64_t in_display) override { - mCallback->onRefresh(translate<Display>(in_display)); + mCallback.onComposerHalRefresh(translate<Display>(in_display)); return ::ndk::ScopedAStatus::ok(); } + ::ndk::ScopedAStatus onSeamlessPossible(int64_t in_display) override { - mCallback->onSeamlessPossible(translate<Display>(in_display)); + mCallback.onComposerHalSeamlessPossible(translate<Display>(in_display)); return ::ndk::ScopedAStatus::ok(); } + ::ndk::ScopedAStatus onVsync(int64_t in_display, int64_t in_timestamp, int32_t in_vsyncPeriodNanos) override { - mCallback->onVsync_2_4(translate<Display>(in_display), in_timestamp, - static_cast<uint32_t>(in_vsyncPeriodNanos)); + mCallback.onComposerHalVsync(translate<Display>(in_display), in_timestamp, + static_cast<uint32_t>(in_vsyncPeriodNanos)); return ::ndk::ScopedAStatus::ok(); } + ::ndk::ScopedAStatus onVsyncPeriodTimingChanged( int64_t in_display, const AidlVsyncPeriodChangeTimeline& in_updatedTimeline) override { - mCallback->onVsyncPeriodTimingChanged(translate<Display>(in_display), - translate<V2_4::VsyncPeriodChangeTimeline>( - in_updatedTimeline)); + mCallback.onComposerHalVsyncPeriodTimingChanged(translate<Display>(in_display), + translate<V2_4::VsyncPeriodChangeTimeline>( + in_updatedTimeline)); + return ::ndk::ScopedAStatus::ok(); + } + + ::ndk::ScopedAStatus onVsyncIdle(int64_t in_display) override { + mCallback.onComposerHalVsyncIdle(translate<Display>(in_display)); return ::ndk::ScopedAStatus::ok(); } private: - sp<V2_4::IComposerCallback> mCallback; + HWC2::ComposerCallback& mCallback; }; std::string AidlComposer::instance(const std::string& serviceName) { @@ -262,10 +271,11 @@ std::string AidlComposer::dumpDebugInfo() { return info; } -void AidlComposer::registerCallback(const sp<IComposerCallback>& callback) { +void AidlComposer::registerCallback(HWC2::ComposerCallback& callback) { if (mAidlComposerCallback) { ALOGE("Callback already registered"); } + mAidlComposerCallback = ndk::SharedRefBase::make<AidlIComposerCallbackWrapper>(callback); AIBinder_setMinSchedulerPolicy(mAidlComposerCallback->asBinder().get(), SCHED_FIFO, 2); diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h index 677001700a..374a436141 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h @@ -63,7 +63,7 @@ public: std::vector<IComposer::Capability> getCapabilities() override; std::string dumpDebugInfo() override; - void registerCallback(const sp<IComposerCallback>& callback) override; + void registerCallback(HWC2::ComposerCallback& callback) override; // Reset all pending commands in the command buffer. Useful if you want to // skip a frame but have already queued some commands. diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index 22f424fd4a..fe55e6b657 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -34,11 +34,17 @@ #include <aidl/android/hardware/graphics/composer3/Color.h> #include <aidl/android/hardware/graphics/composer3/Composition.h> #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h> +#include <aidl/android/hardware/graphics/composer3/IComposerCallback.h> // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic pop // ignored "-Wconversion -Wextra" -namespace android::Hwc2 { +namespace android { +namespace HWC2 { +struct ComposerCallback; +} // namespace HWC2 + +namespace Hwc2 { namespace types = hardware::graphics::common; @@ -46,6 +52,7 @@ namespace V2_1 = hardware::graphics::composer::V2_1; namespace V2_2 = hardware::graphics::composer::V2_2; namespace V2_3 = hardware::graphics::composer::V2_3; namespace V2_4 = hardware::graphics::composer::V2_4; +namespace V3_0 = ::aidl::android::hardware::graphics::composer3; using types::V1_0::ColorTransform; using types::V1_0::Transform; @@ -89,7 +96,7 @@ public: virtual std::vector<IComposer::Capability> getCapabilities() = 0; virtual std::string dumpDebugInfo() = 0; - virtual void registerCallback(const sp<IComposerCallback>& callback) = 0; + virtual void registerCallback(HWC2::ComposerCallback& callback) = 0; // Reset all pending commands in the command buffer. Useful if you want to // skip a frame but have already queued some commands. @@ -109,9 +116,8 @@ public: virtual Error destroyLayer(Display display, Layer layer) = 0; virtual Error getActiveConfig(Display display, Config* outConfig) = 0; - virtual Error getChangedCompositionTypes( - Display display, std::vector<Layer>* outLayers, - std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes) = 0; + virtual Error getChangedCompositionTypes(Display display, std::vector<Layer>* outLayers, + std::vector<V3_0::Composition>* outTypes) = 0; virtual Error getColorModes(Display display, std::vector<ColorMode>* outModes) = 0; virtual Error getDisplayAttribute(Display display, Config config, IComposerClient::Attribute attribute, int32_t* outValue) = 0; @@ -225,10 +231,8 @@ public: const DisplayBrightnessOptions& options) = 0; // Composer HAL 2.4 - virtual Error getDisplayCapabilities( - Display display, - std::vector<aidl::android::hardware::graphics::composer3::DisplayCapability>* - outCapabilities) = 0; + virtual Error getDisplayCapabilities(Display display, + std::vector<V3_0::DisplayCapability>* outCapabilities) = 0; virtual V2_4::Error getDisplayConnectionType( Display display, IComposerClient::DisplayConnectionType* outType) = 0; virtual V2_4::Error getDisplayVsyncPeriod(Display display, @@ -263,4 +267,5 @@ public: virtual Error getPreferredBootDisplayConfig(Display displayId, Config*) = 0; }; -} // namespace android::Hwc2 +} // namespace Hwc2 +} // namespace android diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index 57eb12875b..0a605a8515 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -72,6 +72,7 @@ struct ComposerCallback { virtual void onComposerHalVsyncPeriodTimingChanged(hal::HWDisplayId, const hal::VsyncPeriodChangeTimeline&) = 0; virtual void onComposerHalSeamlessPossible(hal::HWDisplayId) = 0; + virtual void onComposerHalVsyncIdle(hal::HWDisplayId) = 0; protected: ~ComposerCallback() = default; diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 44e45973a1..2696bd8207 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -74,63 +74,6 @@ namespace hal = android::hardware::graphics::composer::hal; namespace android { -namespace { - -using android::hardware::Return; -using android::hardware::Void; -using android::HWC2::ComposerCallback; - -class ComposerCallbackBridge : public hal::IComposerCallback { -public: - ComposerCallbackBridge(ComposerCallback* callback, bool vsyncSwitchingSupported) - : mCallback(callback), mVsyncSwitchingSupported(vsyncSwitchingSupported) {} - - Return<void> onHotplug(hal::HWDisplayId display, hal::Connection connection) override { - mCallback->onComposerHalHotplug(display, connection); - return Void(); - } - - Return<void> onRefresh(hal::HWDisplayId display) override { - mCallback->onComposerHalRefresh(display); - return Void(); - } - - Return<void> onVsync(hal::HWDisplayId display, int64_t timestamp) override { - if (!mVsyncSwitchingSupported) { - mCallback->onComposerHalVsync(display, timestamp, std::nullopt); - } else { - ALOGW("Unexpected onVsync callback on composer >= 2.4, ignoring."); - } - return Void(); - } - - Return<void> onVsync_2_4(hal::HWDisplayId display, int64_t timestamp, - hal::VsyncPeriodNanos vsyncPeriodNanos) override { - if (mVsyncSwitchingSupported) { - mCallback->onComposerHalVsync(display, timestamp, vsyncPeriodNanos); - } else { - ALOGW("Unexpected onVsync_2_4 callback on composer <= 2.3, ignoring."); - } - return Void(); - } - - Return<void> onVsyncPeriodTimingChanged( - hal::HWDisplayId display, const hal::VsyncPeriodChangeTimeline& timeline) override { - mCallback->onComposerHalVsyncPeriodTimingChanged(display, timeline); - return Void(); - } - - Return<void> onSeamlessPossible(hal::HWDisplayId display) override { - mCallback->onComposerHalSeamlessPossible(display); - return Void(); - } - -private: - ComposerCallback* const mCallback; - const bool mVsyncSwitchingSupported; -}; - -} // namespace HWComposer::~HWComposer() = default; @@ -149,7 +92,7 @@ HWComposer::~HWComposer() { mDisplayData.clear(); } -void HWComposer::setCallback(HWC2::ComposerCallback* callback) { +void HWComposer::setCallback(HWC2::ComposerCallback& callback) { loadCapabilities(); loadLayerMetadataSupport(); @@ -159,10 +102,7 @@ void HWComposer::setCallback(HWC2::ComposerCallback* callback) { } mRegisteredCallback = true; - const bool vsyncSwitchingSupported = - mComposer->isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching); - mComposer->registerCallback( - sp<ComposerCallbackBridge>::make(callback, vsyncSwitchingSupported)); + mComposer->registerCallback(callback); } bool HWComposer::getDisplayIdentificationData(hal::HWDisplayId hwcDisplayId, uint8_t* outPort, @@ -832,18 +772,16 @@ status_t HWComposer::clearBootDisplayMode(PhysicalDisplayId displayId) { return NO_ERROR; } -hal::HWConfigId HWComposer::getPreferredBootDisplayMode(PhysicalDisplayId displayId) { - RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); - hal::HWConfigId displayModeId = -1; +std::optional<hal::HWConfigId> HWComposer::getPreferredBootDisplayMode( + PhysicalDisplayId displayId) { + RETURN_IF_INVALID_DISPLAY(displayId, std::nullopt); + hal::HWConfigId displayModeId; const auto error = mDisplayData[displayId].hwcDisplay->getPreferredBootDisplayConfig(&displayModeId); - if (error == hal::Error::UNSUPPORTED) { - RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION); - } - if (error == hal::Error::BAD_PARAMETER) { - RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE); + if (error != hal::Error::NONE) { + LOG_DISPLAY_ERROR(displayId, to_string(error).c_str()); + return std::nullopt; } - RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); return displayModeId; } diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 93773fa6c1..29335d56e5 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -105,7 +105,7 @@ public: virtual ~HWComposer(); - virtual void setCallback(HWC2::ComposerCallback*) = 0; + virtual void setCallback(HWC2::ComposerCallback&) = 0; virtual bool getDisplayIdentificationData(hal::HWDisplayId, uint8_t* outPort, DisplayIdentificationData* outData) const = 0; @@ -260,7 +260,7 @@ public: virtual bool getBootDisplayModeSupport() = 0; virtual status_t setBootDisplayMode(PhysicalDisplayId, hal::HWConfigId) = 0; virtual status_t clearBootDisplayMode(PhysicalDisplayId) = 0; - virtual hal::HWConfigId getPreferredBootDisplayMode(PhysicalDisplayId) = 0; + virtual std::optional<hal::HWConfigId> getPreferredBootDisplayMode(PhysicalDisplayId) = 0; }; namespace impl { @@ -272,7 +272,7 @@ public: ~HWComposer() override; - void setCallback(HWC2::ComposerCallback*) override; + void setCallback(HWC2::ComposerCallback&) override; bool getDisplayIdentificationData(hal::HWDisplayId, uint8_t* outPort, DisplayIdentificationData* outData) const override; @@ -391,7 +391,7 @@ public: bool getBootDisplayModeSupport() override; status_t setBootDisplayMode(PhysicalDisplayId, hal::HWConfigId) override; status_t clearBootDisplayMode(PhysicalDisplayId) override; - hal::HWConfigId getPreferredBootDisplayMode(PhysicalDisplayId) override; + std::optional<hal::HWConfigId> getPreferredBootDisplayMode(PhysicalDisplayId) override; // for debugging ---------------------------------------------------------- void dump(std::string& out) const override; diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp index d3acecb46c..746ac64865 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp @@ -30,6 +30,7 @@ #include <hidl/HidlTransportUtils.h> #include <log/log.h> #include <utils/Trace.h> +#include "HWC2.h" #include "Hal.h" #include <algorithm> @@ -44,6 +45,63 @@ using hardware::hidl_vec; using hardware::Return; namespace Hwc2 { +namespace { + +using android::hardware::Return; +using android::hardware::Void; +using android::HWC2::ComposerCallback; + +class ComposerCallbackBridge : public IComposerCallback { +public: + ComposerCallbackBridge(ComposerCallback& callback, bool vsyncSwitchingSupported) + : mCallback(callback), mVsyncSwitchingSupported(vsyncSwitchingSupported) {} + + Return<void> onHotplug(Display display, Connection connection) override { + mCallback.onComposerHalHotplug(display, connection); + return Void(); + } + + Return<void> onRefresh(Display display) override { + mCallback.onComposerHalRefresh(display); + return Void(); + } + + Return<void> onVsync(Display display, int64_t timestamp) override { + if (!mVsyncSwitchingSupported) { + mCallback.onComposerHalVsync(display, timestamp, std::nullopt); + } else { + ALOGW("Unexpected onVsync callback on composer >= 2.4, ignoring."); + } + return Void(); + } + + Return<void> onVsync_2_4(Display display, int64_t timestamp, + VsyncPeriodNanos vsyncPeriodNanos) override { + if (mVsyncSwitchingSupported) { + mCallback.onComposerHalVsync(display, timestamp, vsyncPeriodNanos); + } else { + ALOGW("Unexpected onVsync_2_4 callback on composer <= 2.3, ignoring."); + } + return Void(); + } + + Return<void> onVsyncPeriodTimingChanged(Display display, + const VsyncPeriodChangeTimeline& timeline) override { + mCallback.onComposerHalVsyncPeriodTimingChanged(display, timeline); + return Void(); + } + + Return<void> onSeamlessPossible(Display display) override { + mCallback.onComposerHalSeamlessPossible(display); + return Void(); + } + +private: + ComposerCallback& mCallback; + const bool mVsyncSwitchingSupported; +}; + +} // namespace HidlComposer::~HidlComposer() = default; @@ -1246,6 +1304,13 @@ Error HidlComposer::setLayerBlockingRegion(Display, Layer, return Error::NONE; } +void HidlComposer::registerCallback(ComposerCallback& callback) { + const bool vsyncSwitchingSupported = + isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching); + + registerCallback(sp<ComposerCallbackBridge>::make(callback, vsyncSwitchingSupported)); +} + CommandReader::~CommandReader() { resetData(); } diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h index c8c780039e..1ffca6e0ae 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h @@ -172,7 +172,7 @@ public: std::vector<IComposer::Capability> getCapabilities() override; std::string dumpDebugInfo() override; - void registerCallback(const sp<IComposerCallback>& callback) override; + void registerCallback(HWC2::ComposerCallback& callback) override; // Reset all pending commands in the command buffer. Useful if you want to // skip a frame but have already queued some commands. @@ -334,6 +334,8 @@ private: ~CommandWriter() override {} }; + void registerCallback(const sp<IComposerCallback>& callback); + // Many public functions above simply write a command into the command // queue to batch the calls. validateDisplay and presentDisplay will call // this function to execute the command queue. diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index fa2c92dd32..645d4d1b44 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -945,16 +945,10 @@ bool Layer::setBackgroundBlurRadius(int backgroundBlurRadius) { setTransactionFlags(eTransactionNeeded); return true; } -bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix, - bool allowNonRectPreservingTransforms) { +bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix) { ui::Transform t; t.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy); - if (!allowNonRectPreservingTransforms && !t.preserveRects()) { - ALOGW("Attempt to set rotation matrix without permission ACCESS_SURFACE_FLINGER nor " - "ROTATE_SURFACE_FLINGER ignored"); - return false; - } mDrawingState.sequence++; mDrawingState.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy); mDrawingState.modified = true; @@ -2166,101 +2160,71 @@ Rect Layer::getInputBounds() const { return getCroppedBufferSize(getDrawingState()); } -void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& displayTransform) { - // Transform layer size to screen space and inset it by surface insets. - // If this is a portal window, set the touchableRegion to the layerBounds. - Rect layerBounds = info.portalToDisplayId == ADISPLAY_ID_NONE - ? getInputBounds() - : info.touchableRegion.getBounds(); - if (!layerBounds.isValid()) { - layerBounds = getInputBounds(); - } - - if (!layerBounds.isValid()) { - // If the layer bounds is empty, set the frame to empty and clear the transform - info.frameLeft = 0; - info.frameTop = 0; - info.frameRight = 0; - info.frameBottom = 0; - info.transform.reset(); - info.touchableRegion = Region(); +void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& screenToDisplay) { + Rect tmpBounds = getInputBounds(); + if (!tmpBounds.isValid()) { info.flags = WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::NOT_FOCUSABLE; - return; - } - - const ui::Transform layerTransform = getInputTransform(); - // Transform that takes window coordinates to non-rotated display coordinates - ui::Transform t = displayTransform * layerTransform; - int32_t xSurfaceInset = info.surfaceInset; - int32_t ySurfaceInset = info.surfaceInset; - // Bring screenBounds into non-unrotated space - Rect screenBounds = displayTransform.transform(Rect{mScreenBounds}); - - const float xScale = t.getScaleX(); - const float yScale = t.getScaleY(); - if (xScale != 1.0f || yScale != 1.0f) { - xSurfaceInset = std::round(xSurfaceInset * xScale); - ySurfaceInset = std::round(ySurfaceInset * yScale); - } - - // Transform the layer bounds from layer coordinate space to display coordinate space. - Rect transformedLayerBounds = t.transform(layerBounds); - - // clamp inset to layer bounds - xSurfaceInset = (xSurfaceInset >= 0) - ? std::min(xSurfaceInset, transformedLayerBounds.getWidth() / 2) - : 0; - ySurfaceInset = (ySurfaceInset >= 0) - ? std::min(ySurfaceInset, transformedLayerBounds.getHeight() / 2) - : 0; - - // inset while protecting from overflow TODO(b/161235021): What is going wrong - // in the overflow scenario? - { - int32_t tmp; - if (!__builtin_add_overflow(transformedLayerBounds.left, xSurfaceInset, &tmp)) - transformedLayerBounds.left = tmp; - if (!__builtin_sub_overflow(transformedLayerBounds.right, xSurfaceInset, &tmp)) - transformedLayerBounds.right = tmp; - if (!__builtin_add_overflow(transformedLayerBounds.top, ySurfaceInset, &tmp)) - transformedLayerBounds.top = tmp; - if (!__builtin_sub_overflow(transformedLayerBounds.bottom, ySurfaceInset, &tmp)) - transformedLayerBounds.bottom = tmp; - } - - // Compute the correct transform to send to input. This will allow it to transform the - // input coordinates from display space into window space. Therefore, it needs to use the - // final layer frame to create the inverse transform. Since surface insets are added later, - // along with the overflow, the best way to ensure we get the correct transform is to use - // the final frame calculated. - // 1. Take the original transform set on the window and get the inverse transform. This is - // used to get the final bounds in display space (ignorning the transform). Apply the - // inverse transform on the layerBounds to get the untransformed frame (in layer space) - // 2. Take the top and left of the untransformed frame to get the real position on screen. - // Apply the layer transform on top/left so it includes any scale or rotation. These will - // be the new translation values for the transform. - // 3. Update the translation of the original transform to the new translation values. - // 4. Send the inverse transform to input so the coordinates can be transformed back into - // window space. - ui::Transform inverseTransform = t.inverse(); - Rect nonTransformedBounds = inverseTransform.transform(transformedLayerBounds); - vec2 translation = t.transform(nonTransformedBounds.left, nonTransformedBounds.top); - ui::Transform inputTransform(t); - inputTransform.set(translation.x, translation.y); - info.transform = inputTransform.inverse(); - - // We need to send the layer bounds cropped to the screenbounds since the layer can be cropped. - // The frame should be the area the user sees on screen since it's used for occlusion - // detection. - transformedLayerBounds.intersect(screenBounds, &transformedLayerBounds); - info.frameLeft = transformedLayerBounds.left; - info.frameTop = transformedLayerBounds.top; - info.frameRight = transformedLayerBounds.right; - info.frameBottom = transformedLayerBounds.bottom; - - // Position the touchable region relative to frame screen location and restrict it to frame - // bounds. - info.touchableRegion = inputTransform.transform(info.touchableRegion); + info.focusable = false; + info.touchableRegion.clear(); + // A layer could have invalid input bounds and still expect to receive touch input if it has + // replaceTouchableRegionWithCrop. For that case, the input transform needs to be calculated + // correctly to determine the coordinate space for input events. Use an empty rect so that + // the layer will receive input in its own layer space. + tmpBounds = Rect::EMPTY_RECT; + } + + // InputDispatcher works in the display device's coordinate space. Here, we calculate the + // frame and transform used for the layer, which determines the bounds and the coordinate space + // within which the layer will receive input. + // + // The coordinate space within which each of the bounds are specified is explicitly documented + // in the variable name. For example "inputBoundsInLayer" is specified in layer space. A + // Transform converts one coordinate space to another, which is apparent in its naming. For + // example, "layerToDisplay" transforms layer space to display space. + // + // Coordinate space definitions: + // - display: The display device's coordinate space. Correlates to pixels on the display. + // - screen: The post-rotation coordinate space for the display, a.k.a. logical display space. + // - layer: The coordinate space of this layer. + // - input: The coordinate space in which this layer will receive input events. This could be + // different than layer space if a surfaceInset is used, which changes the origin + // of the input space. + const FloatRect inputBoundsInLayer = tmpBounds.toFloatRect(); + + // Clamp surface inset to the input bounds. + const auto surfaceInset = static_cast<float>(info.surfaceInset); + const float xSurfaceInset = + std::max(0.f, std::min(surfaceInset, inputBoundsInLayer.getWidth() / 2.f)); + const float ySurfaceInset = + std::max(0.f, std::min(surfaceInset, inputBoundsInLayer.getHeight() / 2.f)); + + // Apply the insets to the input bounds. + const FloatRect insetBoundsInLayer(inputBoundsInLayer.left + xSurfaceInset, + inputBoundsInLayer.top + ySurfaceInset, + inputBoundsInLayer.right - xSurfaceInset, + inputBoundsInLayer.bottom - ySurfaceInset); + + // Crop the input bounds to ensure it is within the parent's bounds. + const FloatRect croppedInsetBoundsInLayer = mBounds.intersect(insetBoundsInLayer); + + const ui::Transform layerToScreen = getInputTransform(); + const ui::Transform layerToDisplay = screenToDisplay * layerToScreen; + + const Rect roundedFrameInDisplay{layerToDisplay.transform(croppedInsetBoundsInLayer)}; + info.frameLeft = roundedFrameInDisplay.left; + info.frameTop = roundedFrameInDisplay.top; + info.frameRight = roundedFrameInDisplay.right; + info.frameBottom = roundedFrameInDisplay.bottom; + + ui::Transform inputToLayer; + inputToLayer.set(insetBoundsInLayer.left, insetBoundsInLayer.top); + const ui::Transform inputToDisplay = layerToDisplay * inputToLayer; + + // InputDispatcher expects a display-to-input transform. + info.transform = inputToDisplay.inverse(); + + // The touchable region is specified in the input coordinate space. Change it to display space. + info.touchableRegion = inputToDisplay.transform(info.touchableRegion); } void Layer::fillTouchOcclusionMode(WindowInfo& info) { diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 4cdd8fa24b..605a27efa7 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -98,6 +98,7 @@ struct LayerCreationArgs { uid_t callingUid; uint32_t textureName; std::optional<uint32_t> sequence = std::nullopt; + bool addToRoot = true; }; class Layer : public virtual RefBase, compositionengine::LayerFE { @@ -366,8 +367,7 @@ public: // Set a 2x2 transformation matrix on the layer. This transform // will be applied after parent transforms, but before any final // producer specified transform. - virtual bool setMatrix(const layer_state_t::matrix22_t& matrix, - bool allowNonRectPreservingTransforms); + virtual bool setMatrix(const layer_state_t::matrix22_t& matrix); // This second set of geometry attributes are controlled by // setGeometryAppliesWithResize, and their default mode is to be @@ -959,6 +959,19 @@ protected: bool usingRelativeZ(LayerVector::StateSet) const; virtual ui::Transform getInputTransform() const; + /** + * Get the bounds in layer space within which this layer can receive input. + * + * These bounds are used to: + * - Determine the input frame for the layer to be used for occlusion detection; and + * - Determine the coordinate space within which the layer will receive input. The top-left of + * this rect will be the origin of the coordinate space that the input events sent to the + * layer will be in (prior to accounting for surface insets). + * + * The layer can still receive touch input if these bounds are invalid if + * "replaceTouchableRegionWithCrop" is specified. In this case, the layer will receive input + * in this layer's space, regardless of the specified crop layer. + */ virtual Rect getInputBounds() const; // constant @@ -1080,7 +1093,7 @@ private: void fillTouchOcclusionMode(gui::WindowInfo& info); // Fills in the frame and transform info for the gui::WindowInfo. - void fillInputFrameInfo(gui::WindowInfo&, const ui::Transform& displayTransform); + void fillInputFrameInfo(gui::WindowInfo&, const ui::Transform& screenToDisplay); // Cached properties computed from drawing state // Effective transform taking into account parent transforms and any parent scaling, which is diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp index 3becb5c559..3b9cfa6340 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp @@ -38,6 +38,8 @@ namespace android::scheduler { namespace { +constexpr RefreshRateConfigs::GlobalSignals kNoSignals; + std::string formatLayerInfo(const RefreshRateConfigs::LayerRequirement& layer, float weight) { return base::StringPrintf("%s (type=%s, weight=%.2f seamlessness=%s) %s", layer.name.c_str(), ftl::enum_string(layer.vote).c_str(), weight, @@ -235,63 +237,26 @@ struct RefreshRateScore { float score; }; -RefreshRate RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequirement>& layers, - GlobalSignals globalSignals, - GlobalSignals* outSignalsConsidered) const { +auto RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequirement>& layers, + GlobalSignals signals) const + -> std::pair<RefreshRate, GlobalSignals> { std::lock_guard lock(mLock); - if (auto cached = getCachedBestRefreshRate(layers, globalSignals, outSignalsConsidered)) { - return *cached; + if (mGetBestRefreshRateCache && + mGetBestRefreshRateCache->arguments == std::make_pair(layers, signals)) { + return mGetBestRefreshRateCache->result; } - GlobalSignals signalsConsidered; - RefreshRate result = getBestRefreshRateLocked(layers, globalSignals, &signalsConsidered); - lastBestRefreshRateInvocation.emplace( - GetBestRefreshRateInvocation{.layerRequirements = layers, - .globalSignals = globalSignals, - .outSignalsConsidered = signalsConsidered, - .resultingBestRefreshRate = result}); - if (outSignalsConsidered) { - *outSignalsConsidered = signalsConsidered; - } + const auto result = getBestRefreshRateLocked(layers, signals); + mGetBestRefreshRateCache = GetBestRefreshRateCache{{layers, signals}, result}; return result; } -std::optional<RefreshRate> RefreshRateConfigs::getCachedBestRefreshRate( - const std::vector<LayerRequirement>& layers, GlobalSignals globalSignals, - GlobalSignals* outSignalsConsidered) const { - const bool sameAsLastCall = lastBestRefreshRateInvocation && - lastBestRefreshRateInvocation->layerRequirements == layers && - lastBestRefreshRateInvocation->globalSignals == globalSignals; - - if (sameAsLastCall) { - if (outSignalsConsidered) { - *outSignalsConsidered = lastBestRefreshRateInvocation->outSignalsConsidered; - } - return lastBestRefreshRateInvocation->resultingBestRefreshRate; - } - - return {}; -} - -RefreshRate RefreshRateConfigs::getBestRefreshRateLocked( - const std::vector<LayerRequirement>& layers, GlobalSignals globalSignals, - GlobalSignals* outSignalsConsidered) const { +auto RefreshRateConfigs::getBestRefreshRateLocked(const std::vector<LayerRequirement>& layers, + GlobalSignals signals) const + -> std::pair<RefreshRate, GlobalSignals> { ATRACE_CALL(); - ALOGV("getBestRefreshRate %zu layers", layers.size()); - - if (outSignalsConsidered) *outSignalsConsidered = {}; - const auto setTouchConsidered = [&] { - if (outSignalsConsidered) { - outSignalsConsidered->touch = true; - } - }; - - const auto setIdleConsidered = [&] { - if (outSignalsConsidered) { - outSignalsConsidered->idle = true; - } - }; + ALOGV("%s: %zu layers", __func__, layers.size()); int noVoteLayers = 0; int minVoteLayers = 0; @@ -301,6 +266,7 @@ RefreshRate RefreshRateConfigs::getBestRefreshRateLocked( int explicitExact = 0; float maxExplicitWeight = 0; int seamedFocusedLayers = 0; + for (const auto& layer : layers) { switch (layer.vote) { case LayerVoteType::NoVote: @@ -349,10 +315,9 @@ RefreshRate RefreshRateConfigs::getBestRefreshRateLocked( // Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've // selected a refresh rate to see if we should apply touch boost. - if (globalSignals.touch && !hasExplicitVoteLayers) { + if (signals.touch && !hasExplicitVoteLayers) { ALOGV("TouchBoost - choose %s", getMaxRefreshRateByPolicyLocked().getName().c_str()); - setTouchConsidered(); - return getMaxRefreshRateByPolicyLocked(anchorGroup); + return {getMaxRefreshRateByPolicyLocked(anchorGroup), GlobalSignals{.touch = true}}; } // If the primary range consists of a single refresh rate then we can only @@ -361,23 +326,21 @@ RefreshRate RefreshRateConfigs::getBestRefreshRateLocked( const bool primaryRangeIsSingleRate = isApproxEqual(policy->primaryRange.min, policy->primaryRange.max); - if (!globalSignals.touch && globalSignals.idle && - !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) { + if (!signals.touch && signals.idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) { ALOGV("Idle - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str()); - setIdleConsidered(); - return getMinRefreshRateByPolicyLocked(); + return {getMinRefreshRateByPolicyLocked(), GlobalSignals{.idle = true}}; } if (layers.empty() || noVoteLayers == layers.size()) { const auto& refreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup); ALOGV("no layers with votes - choose %s", refreshRate.getName().c_str()); - return refreshRate; + return {refreshRate, kNoSignals}; } // Only if all layers want Min we should return Min if (noVoteLayers + minVoteLayers == layers.size()) { ALOGV("all layers Min - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str()); - return getMinRefreshRateByPolicyLocked(); + return {getMinRefreshRateByPolicyLocked(), kNoSignals}; } // Find the best refresh rate based on score @@ -466,9 +429,9 @@ RefreshRate RefreshRateConfigs::getBestRefreshRateLocked( [](RefreshRateScore score) { return score.score == 0; })) { const auto& refreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup); ALOGV("layers not scored - choose %s", refreshRate.getName().c_str()); - return refreshRate; + return {refreshRate, kNoSignals}; } else { - return *bestRefreshRate; + return {*bestRefreshRate, kNoSignals}; } } @@ -490,14 +453,13 @@ RefreshRate RefreshRateConfigs::getBestRefreshRateLocked( using fps_approx_ops::operator<; - if (globalSignals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact && + if (signals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact && bestRefreshRate->getFps() < touchRefreshRate.getFps()) { - setTouchConsidered(); ALOGV("TouchBoost - choose %s", touchRefreshRate.getName().c_str()); - return touchRefreshRate; + return {touchRefreshRate, GlobalSignals{.touch = true}}; } - return *bestRefreshRate; + return {*bestRefreshRate, kNoSignals}; } std::unordered_map<uid_t, std::vector<const RefreshRateConfigs::LayerRequirement*>> @@ -699,7 +661,7 @@ void RefreshRateConfigs::setCurrentModeId(DisplayModeId modeId) { // Invalidate the cached invocation to getBestRefreshRate. This forces // the refresh rate to be recomputed on the next call to getBestRefreshRate. - lastBestRefreshRateInvocation.reset(); + mGetBestRefreshRateCache.reset(); mCurrentRefreshRate = mRefreshRates.at(modeId).get(); } @@ -741,7 +703,7 @@ void RefreshRateConfigs::updateDisplayModes(const DisplayModes& modes, // Invalidate the cached invocation to getBestRefreshRate. This forces // the refresh rate to be recomputed on the next call to getBestRefreshRate. - lastBestRefreshRateInvocation.reset(); + mGetBestRefreshRateCache.reset(); mRefreshRates.clear(); for (const auto& mode : modes) { @@ -800,7 +762,7 @@ status_t RefreshRateConfigs::setDisplayManagerPolicy(const Policy& policy) { ALOGE("Invalid refresh rate policy: %s", policy.toString().c_str()); return BAD_VALUE; } - lastBestRefreshRateInvocation.reset(); + mGetBestRefreshRateCache.reset(); Policy previousPolicy = *getCurrentPolicyLocked(); mDisplayManagerPolicy = policy; if (*getCurrentPolicyLocked() == previousPolicy) { @@ -815,7 +777,7 @@ status_t RefreshRateConfigs::setOverridePolicy(const std::optional<Policy>& poli if (policy && !isPolicyValidLocked(*policy)) { return BAD_VALUE; } - lastBestRefreshRateInvocation.reset(); + mGetBestRefreshRateCache.reset(); Policy previousPolicy = *getCurrentPolicyLocked(); mOverridePolicy = policy; if (*getCurrentPolicyLocked() == previousPolicy) { diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h index 849d297f2c..ade1787c97 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h @@ -20,6 +20,7 @@ #include <numeric> #include <optional> #include <type_traits> +#include <utility> #include <android-base/stringprintf.h> #include <gui/DisplayEventReceiver.h> @@ -249,11 +250,10 @@ public: } }; - // Returns the refresh rate that best fits the given layers. outSignalsConsidered returns - // whether the refresh rate was chosen based on touch boost and/or idle timer. - RefreshRate getBestRefreshRate(const std::vector<LayerRequirement>&, GlobalSignals, - GlobalSignals* outSignalsConsidered = nullptr) const - EXCLUDES(mLock); + // Returns the refresh rate that best fits the given layers, and whether the refresh rate was + // chosen based on touch boost and/or idle timer. + std::pair<RefreshRate, GlobalSignals> getBestRefreshRate(const std::vector<LayerRequirement>&, + GlobalSignals) const EXCLUDES(mLock); FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) { std::lock_guard lock(mLock); @@ -310,6 +310,9 @@ public: .idleTimerTimeoutMs = 0, .supportKernelIdleTimer = false}); + RefreshRateConfigs(const RefreshRateConfigs&) = delete; + RefreshRateConfigs& operator=(const RefreshRateConfigs&) = delete; + // Returns whether switching modes (refresh rate or resolution) is possible. // TODO(b/158780872): Consider HAL support, and skip frame rate detection if the modes only // differ in resolution. @@ -391,11 +394,8 @@ public: void dump(std::string& result) const EXCLUDES(mLock); - RefreshRateConfigs(const RefreshRateConfigs&) = delete; - void operator=(const RefreshRateConfigs&) = delete; - private: - friend class RefreshRateConfigsTest; + friend struct TestableRefreshRateConfigs; void constructAvailableRefreshRates() REQUIRES(mLock); @@ -403,13 +403,8 @@ private: const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate, std::vector<const RefreshRate*>* outRefreshRates) REQUIRES(mLock); - std::optional<RefreshRate> getCachedBestRefreshRate(const std::vector<LayerRequirement>&, - GlobalSignals, - GlobalSignals* outSignalsConsidered) const - REQUIRES(mLock); - - RefreshRate getBestRefreshRateLocked(const std::vector<LayerRequirement>&, GlobalSignals, - GlobalSignals* outSignalsConsidered) const REQUIRES(mLock); + std::pair<RefreshRate, GlobalSignals> getBestRefreshRateLocked( + const std::vector<LayerRequirement>&, GlobalSignals) const REQUIRES(mLock); // Returns the refresh rate with the highest score in the collection specified from begin // to end. If there are more than one with the same highest refresh rate, the first one is @@ -497,14 +492,11 @@ private: const Config mConfig; bool mSupportsFrameRateOverrideByContent; - struct GetBestRefreshRateInvocation { - std::vector<LayerRequirement> layerRequirements; - GlobalSignals globalSignals; - GlobalSignals outSignalsConsidered; - RefreshRate resultingBestRefreshRate; + struct GetBestRefreshRateCache { + std::pair<std::vector<LayerRequirement>, GlobalSignals> arguments; + std::pair<RefreshRate, GlobalSignals> result; }; - mutable std::optional<GetBestRefreshRateInvocation> lastBestRefreshRateInvocation - GUARDED_BY(mLock); + mutable std::optional<GetBestRefreshRateCache> mGetBestRefreshRateCache GUARDED_BY(mLock); // Declare mIdleTimer last to ensure its thread joins before the mutex/callbacks are destroyed. std::mutex mIdleTimerCallbacksMutex; diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index a85e7487e0..665d36982a 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -537,18 +537,19 @@ void Scheduler::chooseRefreshRateForContent() { ATRACE_CALL(); - const auto refreshRateConfigs = holdRefreshRateConfigs(); - scheduler::LayerHistory::Summary summary = - mLayerHistory.summarize(*refreshRateConfigs, systemTime()); - scheduler::RefreshRateConfigs::GlobalSignals consideredSignals; DisplayModePtr newMode; + GlobalSignals consideredSignals; + bool frameRateChanged; bool frameRateOverridesChanged; + + const auto refreshRateConfigs = holdRefreshRateConfigs(); + LayerHistory::Summary summary = mLayerHistory.summarize(*refreshRateConfigs, systemTime()); { std::lock_guard<std::mutex> lock(mPolicyLock); - mPolicy.contentRequirements = summary; + mPolicy.contentRequirements = std::move(summary); - newMode = calculateRefreshRateModeId(&consideredSignals); + std::tie(newMode, consideredSignals) = chooseDisplayMode(); frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, newMode->getFps()); if (mPolicy.mode == newMode) { @@ -678,8 +679,7 @@ void Scheduler::dumpVsync(std::string& out) const { mVsyncSchedule->dump(out); } -bool Scheduler::updateFrameRateOverrides( - scheduler::RefreshRateConfigs::GlobalSignals consideredSignals, Fps displayRefreshRate) { +bool Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) { const auto refreshRateConfigs = holdRefreshRateConfigs(); if (!refreshRateConfigs->supportsFrameRateOverrideByContent()) { return false; @@ -697,9 +697,11 @@ bool Scheduler::updateFrameRateOverrides( template <class T> bool Scheduler::handleTimerStateChanged(T* currentState, T newState) { DisplayModePtr newMode; + GlobalSignals consideredSignals; + bool refreshRateChanged = false; bool frameRateOverridesChanged; - scheduler::RefreshRateConfigs::GlobalSignals consideredSignals; + const auto refreshRateConfigs = holdRefreshRateConfigs(); { std::lock_guard<std::mutex> lock(mPolicyLock); @@ -707,7 +709,7 @@ bool Scheduler::handleTimerStateChanged(T* currentState, T newState) { return false; } *currentState = newState; - newMode = calculateRefreshRateModeId(&consideredSignals); + std::tie(newMode, consideredSignals) = chooseDisplayMode(); frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, newMode->getFps()); if (mPolicy.mode == newMode) { // We don't need to change the display mode, but we might need to send an event @@ -733,33 +735,33 @@ bool Scheduler::handleTimerStateChanged(T* currentState, T newState) { return consideredSignals.touch; } -DisplayModePtr Scheduler::calculateRefreshRateModeId( - scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals) { +auto Scheduler::chooseDisplayMode() -> std::pair<DisplayModePtr, GlobalSignals> { ATRACE_CALL(); - if (consideredSignals) *consideredSignals = {}; - const auto refreshRateConfigs = holdRefreshRateConfigs(); + const auto configs = holdRefreshRateConfigs(); + // If Display Power is not in normal operation we want to be in performance mode. When coming // back to normal mode, a grace period is given with DisplayPowerTimer. if (mDisplayPowerTimer && (!mPolicy.isDisplayPowerStateNormal || mPolicy.displayPowerTimer == TimerState::Reset)) { - return refreshRateConfigs->getMaxRefreshRateByPolicy().getMode(); + constexpr GlobalSignals kNoSignals; + return {configs->getMaxRefreshRateByPolicy().getMode(), kNoSignals}; } - const bool touchActive = mTouchTimer && mPolicy.touch == TouchState::Active; - const bool idle = mPolicy.idleTimer == TimerState::Expired; + const GlobalSignals signals{.touch = mTouchTimer && mPolicy.touch == TouchState::Active, + .idle = mPolicy.idleTimer == TimerState::Expired}; + + const auto [refreshRate, consideredSignals] = + configs->getBestRefreshRate(mPolicy.contentRequirements, signals); - return refreshRateConfigs - ->getBestRefreshRate(mPolicy.contentRequirements, {.touch = touchActive, .idle = idle}, - consideredSignals) - .getMode(); + return {refreshRate.getMode(), consideredSignals}; } DisplayModePtr Scheduler::getPreferredDisplayMode() { std::lock_guard<std::mutex> lock(mPolicyLock); - // Make sure that the default mode ID is first updated, before returned. + // Make sure the stored mode is up to date. if (mPolicy.mode) { - mPolicy.mode = calculateRefreshRateModeId(); + mPolicy.mode = chooseDisplayMode().first; } return mPolicy.mode; } diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index bc9024a9f1..468c4cc865 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -269,15 +269,14 @@ private: void setVsyncPeriod(nsecs_t period); - // This function checks whether individual features that are affecting the refresh rate - // selection were initialized, prioritizes them, and calculates the DisplayModeId - // for the suggested refresh rate. - DisplayModePtr calculateRefreshRateModeId( - RefreshRateConfigs::GlobalSignals* consideredSignals = nullptr) REQUIRES(mPolicyLock); + using GlobalSignals = RefreshRateConfigs::GlobalSignals; + + // Returns the display mode that fulfills the policy, and the signals that were considered. + std::pair<DisplayModePtr, GlobalSignals> chooseDisplayMode() REQUIRES(mPolicyLock); + + bool updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) REQUIRES(mPolicyLock); void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mRefreshRateConfigsLock); - bool updateFrameRateOverrides(RefreshRateConfigs::GlobalSignals, Fps displayRefreshRate) - REQUIRES(mPolicyLock); impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const EXCLUDES(mRefreshRateConfigsLock); diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index aa371cbffe..3459a8ff5b 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -271,12 +271,6 @@ bool validateCompositionDataspace(Dataspace dataspace) { } -enum Permission { - ACCESS_SURFACE_FLINGER = 0x1, - ROTATE_SURFACE_FLINGER = 0x2, - INTERNAL_SYSTEM_WINDOW = 0x4, -}; - struct IdleTimerConfig { int32_t timeoutMs; bool supportKernelIdleTimer; @@ -855,7 +849,7 @@ void SurfaceFlinger::init() { mCompositionEngine->setTimeStats(mTimeStats); mCompositionEngine->setHwComposer(getFactory().createHWComposer(mHwcServiceName)); - mCompositionEngine->getHwComposer().setCallback(this); + mCompositionEngine->getHwComposer().setCallback(*this); ClientCache::getInstance().setRenderEngine(&getRenderEngine()); if (base::GetBoolProperty("debug.sf.enable_hwc_vds"s, false)) { @@ -1020,6 +1014,9 @@ status_t SurfaceFlinger::getStaticDisplayInfo(const sp<IBinder>& displayToken, info->secure = display->isSecure(); info->deviceProductInfo = display->getDeviceProductInfo(); + // TODO: Scale this to multiple displays. + info->installOrientation = display->isPrimary() ? internalDisplayOrientation : ui::ROTATION_0; + return NO_ERROR; } @@ -1112,6 +1109,8 @@ status_t SurfaceFlinger::getDynamicDisplayInfo(const sp<IBinder>& displayToken, return type == hal::ContentType::GAME; }); + info->preferredBootDisplayMode = display->getPreferredBootModeId(); + return NO_ERROR; } @@ -1457,20 +1456,6 @@ status_t SurfaceFlinger::clearBootDisplayMode(const sp<IBinder>& displayToken) { return future.get(); } -status_t SurfaceFlinger::getPreferredBootDisplayMode(const sp<IBinder>& displayToken, - ui::DisplayModeId* id) { - auto future = mScheduler->schedule([=]() MAIN_THREAD mutable -> status_t { - if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) { - *id = getHwComposer().getPreferredBootDisplayMode(*displayId); - return NO_ERROR; - } else { - ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get()); - return BAD_VALUE; - } - }); - return future.get(); -} - void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) { const char* const whence = __func__; static_cast<void>(mScheduler->schedule([=]() MAIN_THREAD { @@ -1965,6 +1950,11 @@ void SurfaceFlinger::onComposerHalRefresh(hal::HWDisplayId) { scheduleComposite(FrameHint::kNone); } +void SurfaceFlinger::onComposerHalVsyncIdle(hal::HWDisplayId) { + // TODO(b/198106220): force enable HWVsync to avoid drift problem during + // idle. +} + void SurfaceFlinger::setVsyncEnabled(bool enabled) { ATRACE_CALL(); @@ -2136,7 +2126,8 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected bool needsTraversal = false; if (clearTransactionFlags(eTransactionFlushNeeded)) { - needsTraversal = flushTransactionQueues(vsyncId); + needsTraversal |= commitCreatedLayers(); + needsTraversal |= flushTransactionQueues(vsyncId); } const bool shouldCommit = @@ -2178,6 +2169,11 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected updateCursorAsync(); updateInputFlinger(); + if (mLayerTracingEnabled && !mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) { + // This will block and tracing should only be enabled for debugging. + mLayerTracing.notify(mVisibleRegionsDirty, frameTime); + } + MAIN_THREAD_GUARD(persistDisplayBrightness(mustComposite)); return mustComposite && CC_LIKELY(mBootStage != BootStage::BOOTLOADER); @@ -2279,13 +2275,9 @@ void SurfaceFlinger::composite(nsecs_t frameTime) { modulateVsync(&VsyncModulator::onDisplayRefresh, usedGpuComposition); mLayersWithQueuedFrames.clear(); - if (mLayerTracingEnabled) { + if (mLayerTracingEnabled && mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) { // This will block and should only be used for debugging. - if (mVisibleRegionsDirty) { - mLayerTracing.notify("visibleRegionsDirty"); - } else if (mLayerTracing.flagIsSet(LayerTracing::TRACE_BUFFERS)) { - mLayerTracing.notify("bufferLatched"); - } + mLayerTracing.notify(mVisibleRegionsDirty, frameTime); } mVisibleRegionsWereDirtyThisFrame = mVisibleRegionsDirty; // Cache value for use in post-comp @@ -2531,6 +2523,7 @@ void SurfaceFlinger::postComposition() { mTimeStats->recordDisplayEventConnectionCount(sfConnections + appConnections); if (isDisplayConnected && !display->isPoweredOn()) { + getRenderEngine().cleanupPostRender(); return; } @@ -3602,7 +3595,7 @@ bool SurfaceFlinger::latchBuffers() { } status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBinder>& handle, - const sp<Layer>& lbc, const wp<Layer>& parent, + const sp<Layer>& layer, const wp<Layer>& parent, bool addToRoot, uint32_t* outTransformHint) { if (mNumLayers >= ISurfaceComposer::MAX_LAYERS) { ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(), @@ -3610,31 +3603,22 @@ status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBind return NO_MEMORY; } - setLayerCreatedState(handle, lbc, parent, addToRoot); - - // Create a transaction includes the initial parent and producer. - Vector<ComposerState> states; - Vector<DisplayState> displays; - - ComposerState composerState; - composerState.state.what = layer_state_t::eLayerCreated; - composerState.state.surface = handle; - states.add(composerState); + { + std::scoped_lock<std::mutex> lock(mCreatedLayersLock); + mCreatedLayers.emplace_back(layer, parent, addToRoot); + } - lbc->updateTransformHint(mActiveDisplayTransformHint); + layer->updateTransformHint(mActiveDisplayTransformHint); if (outTransformHint) { *outTransformHint = mActiveDisplayTransformHint; } // attach this layer to the client if (client != nullptr) { - client->attachLayer(handle, lbc); + client->attachLayer(handle, layer); } - int64_t transactionId = (((int64_t)mPid) << 32) | mUniqueTransactionId++; - return setTransactionState(FrameTimelineInfo{}, states, displays, 0 /* flags */, nullptr, - InputWindowCommands{}, -1 /* desiredPresentTime */, - true /* isAutoTimestamp */, {}, false /* hasListenerCallbacks */, {}, - transactionId); + setTransactionFlags(eTransactionNeeded); + return NO_ERROR; } uint32_t SurfaceFlinger::getTransactionFlags() const { @@ -3992,19 +3976,20 @@ status_t SurfaceFlinger::setTransactionState( ATRACE_CALL(); uint32_t permissions = - callingThreadHasUnscopedSurfaceFlingerAccess() ? Permission::ACCESS_SURFACE_FLINGER : 0; + callingThreadHasUnscopedSurfaceFlingerAccess() ? + layer_state_t::Permission::ACCESS_SURFACE_FLINGER : 0; // Avoid checking for rotation permissions if the caller already has ACCESS_SURFACE_FLINGER // permissions. - if ((permissions & Permission::ACCESS_SURFACE_FLINGER) || + if ((permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) || callingThreadHasRotateSurfaceFlingerAccess()) { - permissions |= Permission::ROTATE_SURFACE_FLINGER; + permissions |= layer_state_t::Permission::ROTATE_SURFACE_FLINGER; } if (callingThreadHasInternalSystemWindowAccess()) { - permissions |= Permission::INTERNAL_SYSTEM_WINDOW; + permissions |= layer_state_t::Permission::INTERNAL_SYSTEM_WINDOW; } - if (!(permissions & Permission::ACCESS_SURFACE_FLINGER) && + if (!(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) && (flags & (eEarlyWakeupStart | eEarlyWakeupEnd))) { ALOGE("Only WindowManager is allowed to use eEarlyWakeup[Start|End] flags"); flags &= ~(eEarlyWakeupStart | eEarlyWakeupEnd); @@ -4066,7 +4051,8 @@ bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelin uint32_t clientStateFlags = 0; for (const ComposerState& state : states) { - clientStateFlags |= setClientStateLocked(frameTimelineInfo, state, desiredPresentTime, + ComposerState stateCopy = state; + clientStateFlags |= setClientStateLocked(frameTimelineInfo, stateCopy, desiredPresentTime, isAutoTimestamp, postTime, permissions); if ((flags & eAnimation) && state.state.surface) { if (const auto layer = fromHandle(state.state.surface).promote()) { @@ -4080,7 +4066,7 @@ bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelin transactionFlags |= clientStateFlags; - if (permissions & Permission::ACCESS_SURFACE_FLINGER) { + if (permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) { transactionFlags |= addInputWindowCommands(inputWindowCommands); } else if (!inputWindowCommands.empty()) { ALOGE("Only privileged callers are allowed to send input commands."); @@ -4191,11 +4177,11 @@ bool SurfaceFlinger::callingThreadHasUnscopedSurfaceFlingerAccess(bool usePermis } uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTimelineInfo, - const ComposerState& composerState, + ComposerState& composerState, int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime, uint32_t permissions) { - const layer_state_t& s = composerState.state; - const bool privileged = permissions & Permission::ACCESS_SURFACE_FLINGER; + layer_state_t& s = composerState.state; + s.sanitize(permissions); std::vector<ListenerCallbacks> filteredListeners; for (auto& listener : s.listeners) { @@ -4219,15 +4205,7 @@ uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTime uint32_t flags = 0; sp<Layer> layer = nullptr; if (s.surface) { - if (what & layer_state_t::eLayerCreated) { - layer = handleLayerCreatedLocked(s.surface); - if (layer) { - flags |= eTransactionNeeded | eTraversalNeeded; - mLayersAdded = true; - } - } else { - layer = fromHandle(s.surface).promote(); - } + layer = fromHandle(s.surface).promote(); } else { // The client may provide us a null handle. Treat it as if the layer was removed. ALOGW("Attempt to set client state with a null layer handle"); @@ -4313,43 +4291,14 @@ uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTime } } if (what & layer_state_t::eMatrixChanged) { - // TODO: b/109894387 - // - // SurfaceFlinger's renderer is not prepared to handle cropping in the face of arbitrary - // rotation. To see the problem observe that if we have a square parent, and a child - // of the same size, then we rotate the child 45 degrees around it's center, the child - // must now be cropped to a non rectangular 8 sided region. - // - // Of course we can fix this in the future. For now, we are lucky, SurfaceControl is - // private API, and arbitrary rotation is used in limited use cases, for instance: - // - WindowManager only uses rotation in one case, which is on a top level layer in which - // cropping is not an issue. - // - Launcher, as a privileged app, uses this to transition an application to PiP - // (picture-in-picture) mode. - // - // However given that abuse of rotation matrices could lead to surfaces extending outside - // of cropped areas, we need to prevent non-root clients without permission - // ACCESS_SURFACE_FLINGER nor ROTATE_SURFACE_FLINGER - // (a.k.a. everyone except WindowManager / tests / Launcher) from setting non rectangle - // preserving transformations. - const bool allowNonRectPreservingTransforms = - permissions & Permission::ROTATE_SURFACE_FLINGER; - if (layer->setMatrix(s.matrix, allowNonRectPreservingTransforms)) flags |= eTraversalNeeded; + if (layer->setMatrix(s.matrix)) flags |= eTraversalNeeded; } if (what & layer_state_t::eTransparentRegionChanged) { if (layer->setTransparentRegionHint(s.transparentRegion)) flags |= eTraversalNeeded; } if (what & layer_state_t::eFlagsChanged) { - auto changedFlags = s.flags; - if (changedFlags & layer_state_t::eLayerIsDisplayDecoration) { - if ((permissions & Permission::INTERNAL_SYSTEM_WINDOW) == 0) { - changedFlags &= ~layer_state_t::eLayerIsDisplayDecoration; - ALOGE("Attempt to use eLayerIsDisplayDecoration without permission " - "INTERNAL_SYSTEM_WINDOW!"); - } - } - if (layer->setFlags(changedFlags, s.mask)) flags |= eTraversalNeeded; + if (layer->setFlags(s.flags, s.mask)) flags |= eTraversalNeeded; } if (what & layer_state_t::eCornerRadiusChanged) { if (layer->setCornerRadius(s.cornerRadius)) @@ -4407,12 +4356,8 @@ uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTime if (layer->setSidebandStream(s.sidebandStream)) flags |= eTraversalNeeded; } if (what & layer_state_t::eInputInfoChanged) { - if (privileged) { - layer->setInputInfo(*s.windowInfoHandle->getInfo()); - flags |= eTraversalNeeded; - } else { - ALOGE("Attempt to update WindowInfo without permission ACCESS_SURFACE_FLINGER"); - } + layer->setInputInfo(*s.windowInfoHandle->getInfo()); + flags |= eTraversalNeeded; } std::optional<nsecs_t> dequeueBufferTimestamp; if (what & layer_state_t::eMetadataChanged) { @@ -4436,22 +4381,19 @@ uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTime if (layer->setShadowRadius(s.shadowRadius)) flags |= eTraversalNeeded; } if (what & layer_state_t::eFrameRateSelectionPriority) { - if (privileged && layer->setFrameRateSelectionPriority(s.frameRateSelectionPriority)) { + if (layer->setFrameRateSelectionPriority(s.frameRateSelectionPriority)) { flags |= eTraversalNeeded; } } if (what & layer_state_t::eFrameRateChanged) { - if (ValidateFrameRate(s.frameRate, s.frameRateCompatibility, s.changeFrameRateStrategy, - "SurfaceFlinger::setClientStateLocked", privileged)) { - const auto compatibility = - Layer::FrameRate::convertCompatibility(s.frameRateCompatibility); - const auto strategy = - Layer::FrameRate::convertChangeFrameRateStrategy(s.changeFrameRateStrategy); + const auto compatibility = + Layer::FrameRate::convertCompatibility(s.frameRateCompatibility); + const auto strategy = + Layer::FrameRate::convertChangeFrameRateStrategy(s.changeFrameRateStrategy); - if (layer->setFrameRate( - Layer::FrameRate(Fps::fromValue(s.frameRate), compatibility, strategy))) { - flags |= eTraversalNeeded; - } + if (layer->setFrameRate( + Layer::FrameRate(Fps::fromValue(s.frameRate), compatibility, strategy))) { + flags |= eTraversalNeeded; } } if (what & layer_state_t::eFixedTransformHintChanged) { @@ -4463,12 +4405,8 @@ uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTime layer->setAutoRefresh(s.autoRefresh); } if (what & layer_state_t::eTrustedOverlayChanged) { - if (privileged) { - if (layer->setTrustedOverlay(s.isTrustedOverlay)) { - flags |= eTraversalNeeded; - } - } else { - ALOGE("Attempt to set trusted overlay without permission ACCESS_SURFACE_FLINGER"); + if (layer->setTrustedOverlay(s.isTrustedOverlay)) { + flags |= eTraversalNeeded; } } if (what & layer_state_t::eStretchChanged) { @@ -4487,13 +4425,9 @@ uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTime } } if (what & layer_state_t::eDropInputModeChanged) { - if (privileged) { - if (layer->setDropInputMode(s.dropInputMode)) { - flags |= eTraversalNeeded; - mInputInfoChanged = true; - } - } else { - ALOGE("Attempt to update DropInputMode without permission ACCESS_SURFACE_FLINGER"); + if (layer->setDropInputMode(s.dropInputMode)) { + flags |= eTraversalNeeded; + mInputInfoChanged = true; } } // This has to happen after we reparent children because when we reparent to null we remove @@ -4571,7 +4505,7 @@ status_t SurfaceFlinger::mirrorLayer(const LayerCreationArgs& args, args.name, mirrorFrom->sequence); } return addClientLayer(args.client, *outHandle, mirrorLayer /* layer */, nullptr /* parent */, - false /* addAsRoot */, nullptr /* outTransformHint */); + false /* addToRoot */, nullptr /* outTransformHint */); } status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, sp<IBinder>* outHandle, @@ -4611,7 +4545,7 @@ status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, sp<IBinder>* outHa return result; } - bool addToRoot = callingThreadHasUnscopedSurfaceFlingerAccess(); + bool addToRoot = args.addToRoot && callingThreadHasUnscopedSurfaceFlingerAccess(); wp<Layer> parent(parentHandle != nullptr ? fromHandle(parentHandle) : parentLayer); if (parentHandle != nullptr && parent == nullptr) { ALOGE("Invalid parent handle %p.", parentHandle.get()); @@ -4638,7 +4572,6 @@ status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, sp<IBinder>* outHa return result; } - setTransactionFlags(eTransactionNeeded); *outLayerId = layer->sequence; return result; } @@ -5463,7 +5396,6 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { case GET_BOOT_DISPLAY_MODE_SUPPORT: case SET_BOOT_DISPLAY_MODE: case CLEAR_BOOT_DISPLAY_MODE: - case GET_PREFERRED_BOOT_DISPLAY_MODE: case GET_AUTO_LOW_LATENCY_MODE_SUPPORT: case SET_AUTO_LOW_LATENCY_MODE: case GET_GAME_CONTENT_TYPE_SUPPORT: @@ -5602,9 +5534,9 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { code == IBinder::SYSPROPS_TRANSACTION) { return OK; } - // Numbers from 1000 to 1041 are currently used for backdoors. The code + // Numbers from 1000 to 1042 are currently used for backdoors. The code // in onTransact verifies that the user is root, and has access to use SF. - if (code >= 1000 && code <= 1041) { + if (code >= 1000 && code <= 1042) { ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code); return OK; } @@ -5791,12 +5723,18 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r } case 1025: { // Set layer tracing n = data.readInt32(); + int64_t fixedStartingTime = data.readInt64(); bool tracingEnabledChanged; if (n) { ALOGD("LayerTracing enabled"); tracingEnabledChanged = mLayerTracing.enable(); if (tracingEnabledChanged) { - mScheduler->schedule([&]() MAIN_THREAD { mLayerTracing.notify("start"); }) + int64_t startingTime = + (fixedStartingTime) ? fixedStartingTime : systemTime(); + mScheduler + ->schedule([&]() MAIN_THREAD { + mLayerTracing.notify("start", startingTime); + }) .wait(); } } else { @@ -6059,6 +5997,16 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r reply->writeInt32(NO_ERROR); return NO_ERROR; } + case 1042: { // Write layers trace or transaction trace to file + if (mTransactionTracing) { + mTransactionTracing->writeToFile(); + } + if (mLayerTracingEnabled) { + mLayerTracing.writeToFile(); + } + reply->writeInt32(NO_ERROR); + return NO_ERROR; + } } } return err; @@ -7087,53 +7035,19 @@ void TransactionState::traverseStatesWithBuffers( } } -void SurfaceFlinger::setLayerCreatedState(const sp<IBinder>& handle, const wp<Layer>& layer, - const wp<Layer> parent, bool addToRoot) { - Mutex::Autolock lock(mCreatedLayersLock); - mCreatedLayers[handle->localBinder()] = - std::make_unique<LayerCreatedState>(layer, parent, addToRoot); -} - -auto SurfaceFlinger::getLayerCreatedState(const sp<IBinder>& handle) { - Mutex::Autolock lock(mCreatedLayersLock); - BBinder* b = nullptr; - if (handle) { - b = handle->localBinder(); - } - - if (b == nullptr) { - return std::unique_ptr<LayerCreatedState>(nullptr); - } - - auto it = mCreatedLayers.find(b); - if (it == mCreatedLayers.end()) { - ALOGE("Can't find layer from handle %p", handle.get()); - return std::unique_ptr<LayerCreatedState>(nullptr); - } - - auto state = std::move(it->second); - mCreatedLayers.erase(it); - return state; -} - -sp<Layer> SurfaceFlinger::handleLayerCreatedLocked(const sp<IBinder>& handle) { - const auto& state = getLayerCreatedState(handle); - if (!state) { - return nullptr; - } - - sp<Layer> layer = state->layer.promote(); +void SurfaceFlinger::handleLayerCreatedLocked(const LayerCreatedState& state) { + sp<Layer> layer = state.layer.promote(); if (!layer) { - ALOGE("Invalid layer %p", state->layer.unsafe_get()); - return nullptr; + ALOGD("Layer was destroyed soon after creation %p", state.layer.unsafe_get()); + return; } sp<Layer> parent; - bool addToRoot = state->addToRoot; - if (state->initialParent != nullptr) { - parent = state->initialParent.promote(); + bool addToRoot = state.addToRoot; + if (state.initialParent != nullptr) { + parent = state.initialParent.promote(); if (parent == nullptr) { - ALOGE("Invalid parent %p", state->initialParent.unsafe_get()); + ALOGD("Parent was destroyed soon after creation %p", state.initialParent.unsafe_get()); addToRoot = false; } } @@ -7153,7 +7067,6 @@ sp<Layer> SurfaceFlinger::handleLayerCreatedLocked(const sp<IBinder>& handle) { layer->updateTransformHint(mActiveDisplayTransformHint); mInterceptor->saveSurfaceCreation(layer); - return layer; } void SurfaceFlinger::sample() { @@ -7235,6 +7148,26 @@ std::shared_ptr<renderengine::ExternalTexture> SurfaceFlinger::getExternalTextur layerName); return buffer; } + +bool SurfaceFlinger::commitCreatedLayers() { + std::vector<LayerCreatedState> createdLayers; + { + std::scoped_lock<std::mutex> lock(mCreatedLayersLock); + createdLayers = std::move(mCreatedLayers); + mCreatedLayers.clear(); + if (createdLayers.size() == 0) { + return false; + } + } + + Mutex::Autolock _l(mStateLock); + for (const auto& createdLayer : createdLayers) { + handleLayerCreatedLocked(createdLayer); + } + createdLayers.clear(); + mLayersAdded = true; + return true; +} } // namespace android #if defined(__gl_h_) diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 8ca9982b98..c6a4d8501d 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -537,8 +537,6 @@ private: status_t getBootDisplayModeSupport(bool* outSupport) const override; status_t setBootDisplayMode(const sp<IBinder>& displayToken, ui::DisplayModeId id) override; status_t clearBootDisplayMode(const sp<IBinder>& displayToken) override; - status_t getPreferredBootDisplayMode(const sp<IBinder>& displayToken, - ui::DisplayModeId* id) override; void setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) override; void setGameContentType(const sp<IBinder>& displayToken, bool on) override; void setPowerMode(const sp<IBinder>& displayToken, int mode) override; @@ -631,6 +629,7 @@ private: void onComposerHalVsyncPeriodTimingChanged(hal::HWDisplayId, const hal::VsyncPeriodChangeTimeline&) override; void onComposerHalSeamlessPossible(hal::HWDisplayId) override; + void onComposerHalVsyncIdle(hal::HWDisplayId) override; // ICompositor overrides: @@ -729,7 +728,7 @@ private: // Returns true if there is at least one transaction that needs to be flushed bool transactionFlushNeeded(); - uint32_t setClientStateLocked(const FrameTimelineInfo&, const ComposerState&, + uint32_t setClientStateLocked(const FrameTimelineInfo&, ComposerState&, int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime, uint32_t permissions) REQUIRES(mStateLock); @@ -1327,7 +1326,7 @@ private: std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners GUARDED_BY(mStateLock); - mutable Mutex mCreatedLayersLock; + mutable std::mutex mCreatedLayersLock; struct LayerCreatedState { LayerCreatedState(const wp<Layer>& layer, const wp<Layer> parent, bool addToRoot) : layer(layer), initialParent(parent), addToRoot(addToRoot) {} @@ -1343,11 +1342,9 @@ private: // A temporay pool that store the created layers and will be added to current state in main // thread. - std::unordered_map<BBinder*, std::unique_ptr<LayerCreatedState>> mCreatedLayers; - void setLayerCreatedState(const sp<IBinder>& handle, const wp<Layer>& layer, - const wp<Layer> parent, bool addToRoot); - auto getLayerCreatedState(const sp<IBinder>& handle); - sp<Layer> handleLayerCreatedLocked(const sp<IBinder>& handle) REQUIRES(mStateLock); + std::vector<LayerCreatedState> mCreatedLayers GUARDED_BY(mCreatedLayersLock); + bool commitCreatedLayers(); + void handleLayerCreatedLocked(const LayerCreatedState& state) REQUIRES(mStateLock); std::atomic<ui::Transform::RotationFlags> mActiveDisplayTransformHint; diff --git a/services/surfaceflinger/Tracing/LayerTracing.cpp b/services/surfaceflinger/Tracing/LayerTracing.cpp index d136e0b6c8..006efdfd61 100644 --- a/services/surfaceflinger/Tracing/LayerTracing.cpp +++ b/services/surfaceflinger/Tracing/LayerTracing.cpp @@ -98,16 +98,20 @@ void LayerTracing::dump(std::string& result) const { mBuffer->dump(result); } -void LayerTracing::notify(const char* where) { - ATRACE_CALL(); +void LayerTracing::notify(bool visibleRegionDirty, int64_t time) { std::scoped_lock lock(mTraceLock); if (!mEnabled) { return; } + if (!visibleRegionDirty && !flagIsSet(LayerTracing::TRACE_BUFFERS)) { + return; + } + ATRACE_CALL(); LayersTraceProto entry; - entry.set_elapsed_realtime_nanos(elapsedRealtimeNano()); + entry.set_elapsed_realtime_nanos(time); + const char* where = visibleRegionDirty ? "visibleRegionsDirty" : "bufferLatched"; entry.set_where(where); LayersProto layers(mFlinger.dumpDrawingStateProto(mFlags)); diff --git a/services/surfaceflinger/Tracing/LayerTracing.h b/services/surfaceflinger/Tracing/LayerTracing.h index 8ca3587dbc..bd448c956d 100644 --- a/services/surfaceflinger/Tracing/LayerTracing.h +++ b/services/surfaceflinger/Tracing/LayerTracing.h @@ -47,7 +47,7 @@ public: bool isEnabled() const; status_t writeToFile(); LayersTraceFileProto createTraceFileProto() const; - void notify(const char* where); + void notify(bool visibleRegionDirty, int64_t time); enum : uint32_t { TRACE_INPUT = 1 << 1, diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp index 5136295592..a46b79578f 100644 --- a/services/surfaceflinger/Tracing/TransactionTracing.cpp +++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp @@ -128,7 +128,9 @@ void TransactionTracing::loop() { mCommittedTransactions.clear(); } // unlock mMainThreadLock - addEntry(committedTransactions, removedLayers); + if (!committedTransactions.empty() || !removedLayers.empty()) { + addEntry(committedTransactions, removedLayers); + } } } @@ -275,6 +277,7 @@ int32_t TransactionTracing::getLayerIdLocked(const sp<IBinder>& layerHandle) { void TransactionTracing::updateStartingStateLocked( const proto::TransactionTraceEntry& removedEntry) { + mStartingTimestamp = removedEntry.elapsed_realtime_nanos(); // Keep track of layer starting state so we can reconstruct the layer state as we purge // transactions from the buffer. for (const proto::LayerCreationArgs& addedLayer : removedEntry.added_layers()) { @@ -303,13 +306,14 @@ void TransactionTracing::updateStartingStateLocked( } void TransactionTracing::addStartingStateToProtoLocked(proto::TransactionTraceFile& proto) { - proto::TransactionTraceEntry* entryProto = proto.add_entry(); - entryProto->set_elapsed_realtime_nanos(mStartingTimestamp); - entryProto->set_vsync_id(0); if (mStartingStates.size() == 0) { return; } + proto::TransactionTraceEntry* entryProto = proto.add_entry(); + entryProto->set_elapsed_realtime_nanos(mStartingTimestamp); + entryProto->set_vsync_id(0); + entryProto->mutable_added_layers()->Reserve(static_cast<int32_t>(mStartingStates.size())); for (auto& [layerId, state] : mStartingStates) { entryProto->mutable_added_layers()->Add(TransactionProtoParser::toProto(state.args)); diff --git a/services/surfaceflinger/fuzzer/Android.bp b/services/surfaceflinger/fuzzer/Android.bp new file mode 100644 index 0000000000..0ead1631e7 --- /dev/null +++ b/services/surfaceflinger/fuzzer/Android.bp @@ -0,0 +1,94 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +cc_defaults { + name: "surfaceflinger_fuzz_defaults", + include_dirs: [ + "frameworks/native/services/surfaceflinger/tests/unittests", + ], + static_libs: [ + "android.hardware.graphics.composer@2.1-resources", + "libgmock", + "libgui_mocks", + "libgmock_ndk", + "libgmock_main", + "libgtest_ndk_c++", + "libgmock_main_ndk", + "librenderengine_mocks", + "perfetto_trace_protos", + "libcompositionengine_mocks", + "perfetto_trace_protos", + ], + shared_libs: [ + "libprotoutil", + "libstatssocket", + "libstatspull", + "libtimestats", + "libtimestats_proto", + "libprotobuf-cpp-full", + "android.hardware.graphics.mapper@2.0", + "android.hardware.graphics.mapper@3.0", + "android.hardware.graphics.mapper@4.0", + ], + srcs: [ + ":libsurfaceflinger_sources", + ":libsurfaceflinger_mock_sources", + ], + defaults: [ + "libsurfaceflinger_defaults", + ], + header_libs: [ + "libui_fuzzableDataspaces_headers", + "libsurfaceflinger_headers", + "libui_headers", + ], + cflags: [ + "-Wno-unused-result", + "-Wno-conversion", + "-Wno-sign-compare", + ], + fuzz_config: { + cc: [ + "android-media-fuzzing-reports@google.com", + ], + componentid: 155276, + }, +} + +cc_fuzz { + name: "surfaceflinger_fuzzer", + defaults: [ + "surfaceflinger_fuzz_defaults", + ], + srcs: [ + "surfaceflinger_fuzzer.cpp", + ], +} + +cc_fuzz { + name: "surfaceflinger_displayhardware_fuzzer", + defaults: [ + "surfaceflinger_fuzz_defaults", + ], + srcs: [ + "surfaceflinger_displayhardware_fuzzer.cpp", + ], + header_libs: [ + "android.hardware.graphics.composer@2.4-command-buffer", + "android.hardware.graphics.composer@2.4-hal", + ], +} diff --git a/services/surfaceflinger/fuzzer/README.md b/services/surfaceflinger/fuzzer/README.md new file mode 100644 index 0000000000..4ecf770ca9 --- /dev/null +++ b/services/surfaceflinger/fuzzer/README.md @@ -0,0 +1,53 @@ +# Fuzzers for SurfaceFlinger +## Table of contents ++ [SurfaceFlinger](#SurfaceFlinger) ++ [DisplayHardware](#DisplayHardware) + +# <a name="SurfaceFlinger"></a> Fuzzer for SurfaceFlinger + +SurfaceFlinger supports the following data sources: +1. Pixel Formats (parameter name: `defaultCompositionPixelFormat`) +2. Data Spaces (parameter name: `defaultCompositionDataspace`) +3. Rotations (parameter name: `internalDisplayOrientation`) +3. Surface composer tags (parameter name: `onTransact`) + +You can find the possible values in the fuzzer's source code. + +#### Steps to run +1. Build the fuzzer +``` + $ mm -j$(nproc) surfaceflinger_fuzzer +``` +2. To run on device +``` + $ adb sync data + $ adb shell /data/fuzz/arm64/surfaceflinger_fuzzer/surfaceflinger_fuzzer +``` + +# <a name="DisplayHardware"></a> Fuzzer for DisplayHardware + +DisplayHardware supports the following parameters: +1. Hal Capability (parameter name: `hasCapability`) +2. Hal BlendMode (parameter name: `setBlendMode`) +3. Hal Composition (parameter name: `setCompositionType`) +4. Hal Display Capability (parameter name: `hasDisplayCapability`) +5. Composition Types (parameter name: `prepareFrame`) +6. Color Modes (parameter name: `setActiveColorMode`) +7. Render Intents (parameter name: `setActiveColorMode`) +8. Power Modes (parameter name: `setPowerMode`) +9. Content Types (parameter name: `setContentType`) +10. Data Space (parameter name: `setDataspace`) +11. Transforms (parameter name: `setLayerTransform`) + +You can find the possible values in the fuzzer's source code. + +#### Steps to run +1. Build the fuzzer +``` + $ mm -j$(nproc) surfaceflinger_displayhardware_fuzzer +``` +2. Run on device +``` + $ adb sync data + $ adb shell /data/fuzz/arm64/surfaceflinger_displayhardware_fuzzer/surfaceflinger_displayhardware_fuzzer +``` diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp new file mode 100644 index 0000000000..816d2f1f71 --- /dev/null +++ b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp @@ -0,0 +1,657 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/ProcessState.h> +#include <compositionengine/impl/OutputCompositionState.h> +#include <fuzzer/FuzzedDataProvider.h> +#include <gui/BLASTBufferQueue.h> +#include <gui/IGraphicBufferProducer.h> +#include <gui/IProducerListener.h> +#include <gui/LayerDebugInfo.h> +#include <gui/SurfaceComposerClient.h> +#include <hidl/ServiceManagement.h> +#include <hwbinder/ProcessState.h> +#include <ui/DisplayIdentification.h> + +#include "DisplayHardware/AidlComposerHal.h" +#include "DisplayHardware/DisplayMode.h" +#include "DisplayHardware/FramebufferSurface.h" +#include "DisplayHardware/HWComposer.h" +#include "DisplayHardware/PowerAdvisor.h" +#include "DisplayHardware/VirtualDisplaySurface.h" +#include "SurfaceFlinger.h" +#include "surfaceflinger_displayhardware_fuzzer_utils.h" + +#include <FuzzableDataspaces.h> + +namespace android::fuzz { + +using namespace android::hardware::graphics::common; +using namespace android::hardware::graphics::composer; +namespace hal = android::hardware::graphics::composer::hal; +using Config = hal::V2_1::Config; +using Display = hal::V2_1::Display; +using RenderIntent = V1_1::RenderIntent; +using IComposerClient = hal::V2_4::IComposerClient; +using VsyncPeriodChangeTimeline = hal::V2_4::VsyncPeriodChangeTimeline; +using PerFrameMetadata = IComposerClient::PerFrameMetadata; +using PerFrameMetadataBlob = IComposerClient::PerFrameMetadataBlob; +using Vsync = IComposerClient::Vsync; + +static constexpr hal::Transform kTransforms[] = {hal::Transform::FLIP_H, hal::Transform::FLIP_V, + hal::Transform::ROT_90, hal::Transform::ROT_180, + hal::Transform::ROT_270}; + +static constexpr hal::Capability kCapability[] = {hal::Capability::INVALID, + hal::Capability::SIDEBAND_STREAM, + hal::Capability::SKIP_CLIENT_COLOR_TRANSFORM, + hal::Capability::PRESENT_FENCE_IS_NOT_RELIABLE}; + +static constexpr hal::BlendMode kBlendModes[] = {hal::BlendMode::INVALID, hal::BlendMode::NONE, + hal::BlendMode::PREMULTIPLIED, + hal::BlendMode::COVERAGE}; + +static constexpr Composition kCompositions[] = {Composition::INVALID, Composition::CLIENT, + Composition::DEVICE, Composition::SOLID_COLOR, + Composition::CURSOR, Composition::SIDEBAND}; + +static constexpr DisplayCapability kDisplayCapability[] = + {DisplayCapability::INVALID, + DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM, + DisplayCapability::DOZE, + DisplayCapability::BRIGHTNESS, + DisplayCapability::PROTECTED_CONTENTS, + DisplayCapability::AUTO_LOW_LATENCY_MODE}; + +static constexpr VirtualDisplaySurface::CompositionType kCompositionTypes[] = + {VirtualDisplaySurface::CompositionType::Unknown, + VirtualDisplaySurface::CompositionType::Gpu, VirtualDisplaySurface::CompositionType::Hwc, + VirtualDisplaySurface::CompositionType::Mixed}; + +static constexpr ui::RenderIntent kRenderIntents[] = {ui::RenderIntent::COLORIMETRIC, + ui::RenderIntent::ENHANCE, + ui::RenderIntent::TONE_MAP_COLORIMETRIC, + ui::RenderIntent::TONE_MAP_ENHANCE}; + +static constexpr hal::PowerMode kPowerModes[] = {hal::PowerMode::OFF, hal::PowerMode::DOZE, + hal::PowerMode::DOZE_SUSPEND, hal::PowerMode::ON, + hal::PowerMode::ON_SUSPEND}; + +static constexpr hal::ContentType kContentTypes[] = {hal::ContentType::NONE, + hal::ContentType::GRAPHICS, + hal::ContentType::PHOTO, + hal::ContentType::CINEMA, + hal::ContentType::GAME}; + +const unsigned char kInternalEdid[] = + "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\xa3\x42\x31\x00\x00\x00\x00" + "\x00\x15\x01\x03\x80\x1a\x10\x78\x0a\xd3\xe5\x95\x5c\x60\x90\x27" + "\x19\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" + "\x01\x01\x01\x01\x01\x01\x9e\x1b\x00\xa0\x50\x20\x12\x30\x10\x30" + "\x13\x00\x05\xa3\x10\x00\x00\x19\x00\x00\x00\x0f\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x23\x87\x02\x64\x00\x00\x00\x00\xfe\x00\x53" + "\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x00\x00\x00\xfe" + "\x00\x31\x32\x31\x41\x54\x31\x31\x2d\x38\x30\x31\x0a\x20\x00\x45"; + +static constexpr hal::HWConfigId kActiveConfig = 0; + +class DisplayHardwareFuzzer { +public: + DisplayHardwareFuzzer(const uint8_t* data, size_t size) : mFdp(data, size) { + mPhysicalDisplayId = SurfaceComposerClient::getInternalDisplayId().value(); + }; + void process(); + +private: + void invokeComposer(); + void invokeDisplayIdentification(); + void invokeLayer(HWC2::Layer* layer); + void setSidebandStream(HWC2::Layer* layer); + void setCursorPosition(HWC2::Layer* layer); + void setBuffer(HWC2::Layer* layer); + void setSurfaceDamage(HWC2::Layer* layer); + void setDisplayFrame(HWC2::Layer* layer); + void setVisibleRegion(HWC2::Layer* layer); + void setLayerGenericMetadata(HWC2::Layer* layer); + void invokeFrameBufferSurface(); + void invokeVirtualDisplaySurface(); + void invokeAidlComposer(); + Display createVirtualDisplay(Hwc2::AidlComposer*); + void validateDisplay(Hwc2::AidlComposer*, Display); + void presentOrValidateDisplay(Hwc2::AidlComposer*, Display); + void setOutputBuffer(Hwc2::AidlComposer*, Display); + void setLayerSidebandStream(Hwc2::AidlComposer*, Display, Hwc2::V2_4::hal::Layer); + void invokeComposerHal2_2(Hwc2::AidlComposer*, Display, Hwc2::V2_4::hal::Layer); + void invokeComposerHal2_3(Hwc2::AidlComposer*, Display, Hwc2::V2_4::hal::Layer); + void invokeComposerHal2_4(Hwc2::AidlComposer*, Display, Hwc2::V2_4::hal::Layer); + void getDisplayVsyncPeriod(); + void setActiveModeWithConstraints(); + void getDisplayIdentificationData(); + void dumpHwc(); + void getDisplayedContentSamplingAttributes(HalDisplayId); + void getDeviceCompositionChanges(HalDisplayId); + void getHdrCapabilities(HalDisplayId); + void getDisplayedContentSample(HalDisplayId); + void getSupportedContentTypes(); + ui::Size getFuzzedSize(); + mat4 getFuzzedMatrix(); + + DisplayIdGenerator<HalVirtualDisplayId> mGenerator; + FuzzedDataProvider mFdp; + PhysicalDisplayId mPhysicalDisplayId; + android::impl::HWComposer mHwc{std::make_unique<Hwc2::mock::Composer>()}; +}; + +void DisplayHardwareFuzzer::validateDisplay(Hwc2::AidlComposer* composer, Display display) { + uint32_t outNumTypes, outNumRequests; + composer->validateDisplay(display, mFdp.ConsumeIntegral<nsecs_t>(), &outNumTypes, + &outNumRequests); +} + +void DisplayHardwareFuzzer::presentOrValidateDisplay(Hwc2::AidlComposer* composer, + Display display) { + int32_t outPresentFence; + uint32_t outNumTypes, outNumRequests, state; + composer->presentOrValidateDisplay(display, mFdp.ConsumeIntegral<nsecs_t>(), &outNumTypes, + &outNumRequests, &outPresentFence, &state); +} + +void DisplayHardwareFuzzer::setOutputBuffer(Hwc2::AidlComposer* composer, Display display) { + const native_handle_t buffer{}; + composer->setOutputBuffer(display, &buffer, mFdp.ConsumeIntegral<int32_t>() /*releaseFence*/); +} + +void DisplayHardwareFuzzer::setLayerSidebandStream(Hwc2::AidlComposer* composer, Display display, + Hwc2::V2_4::hal::Layer outLayer) { + const native_handle_t stream{}; + composer->setLayerSidebandStream(display, outLayer, &stream); +} + +Display DisplayHardwareFuzzer::createVirtualDisplay(Hwc2::AidlComposer* composer) { + namespace types = hardware::graphics::common; + using types::V1_2::PixelFormat; + PixelFormat format{}; + Display display; + composer->createVirtualDisplay(mFdp.ConsumeIntegral<uint32_t>() /*width*/, + mFdp.ConsumeIntegral<uint32_t>() /*height*/, &format, &display); + return display; +} + +void DisplayHardwareFuzzer::getDisplayVsyncPeriod() { + nsecs_t outVsyncPeriod; + mHwc.getDisplayVsyncPeriod(mPhysicalDisplayId, &outVsyncPeriod); +} + +void DisplayHardwareFuzzer::setActiveModeWithConstraints() { + hal::VsyncPeriodChangeTimeline outTimeline; + mHwc.setActiveModeWithConstraints(mPhysicalDisplayId, kActiveConfig, {} /*constraints*/, + &outTimeline); +} + +void DisplayHardwareFuzzer::getDisplayIdentificationData() { + uint8_t outPort; + DisplayIdentificationData outData; + mHwc.getDisplayIdentificationData(kHwDisplayId, &outPort, &outData); +} + +void DisplayHardwareFuzzer::dumpHwc() { + std::string string = mFdp.ConsumeRandomLengthString().c_str(); + mHwc.dump(string); +} + +void DisplayHardwareFuzzer::getDeviceCompositionChanges(HalDisplayId halDisplayID) { + std::optional<impl::HWComposer::DeviceRequestedChanges> outChanges; + mHwc.getDeviceCompositionChanges(halDisplayID, + mFdp.ConsumeBool() /*frameUsesClientComposition*/, + std::chrono::steady_clock::now(), FenceTime::NO_FENCE, + mFdp.ConsumeIntegral<nsecs_t>(), &outChanges); +} + +void DisplayHardwareFuzzer::getDisplayedContentSamplingAttributes(HalDisplayId halDisplayID) { + uint8_t outComponentMask; + ui::Dataspace dataSpace; + ui::PixelFormat pixelFormat; + mHwc.getDisplayedContentSamplingAttributes(halDisplayID, &pixelFormat, &dataSpace, + &outComponentMask); +} + +void DisplayHardwareFuzzer::getHdrCapabilities(HalDisplayId halDisplayID) { + HdrCapabilities outCapabilities; + mHwc.getHdrCapabilities(halDisplayID, &outCapabilities); +} + +void DisplayHardwareFuzzer::getDisplayedContentSample(HalDisplayId halDisplayID) { + DisplayedFrameStats outStats; + mHwc.getDisplayedContentSample(halDisplayID, mFdp.ConsumeIntegral<uint64_t>() /* maxFrames*/, + mFdp.ConsumeIntegral<uint64_t>() /*timestamps*/, &outStats); +} + +void DisplayHardwareFuzzer::getSupportedContentTypes() { + std::vector<hal::ContentType> contentType{}; + mHwc.getSupportedContentTypes(mPhysicalDisplayId, &contentType); +} + +void DisplayHardwareFuzzer::invokeAidlComposer() { + hardware::ProcessState::self()->startThreadPool(); + ProcessState::self()->startThreadPool(); + + if (!Hwc2::AidlComposer::isDeclared("default")) { + return; + } + + Hwc2::AidlComposer composer("default"); + + android::hardware::graphics::composer::hal::TestHWC2ComposerCallback composerCallback{}; + composer.registerCallback(composerCallback); + + Display display = createVirtualDisplay(&composer); + + composer.acceptDisplayChanges(display); + + Hwc2::V2_4::hal::Layer outLayer; + composer.createLayer(display, &outLayer); + + int32_t outPresentFence; + composer.presentDisplay(display, &outPresentFence); + + composer.setActiveConfig(display, Config{}); + + composer.setClientTarget(display, mFdp.ConsumeIntegral<uint32_t>(), sp<GraphicBuffer>(), + mFdp.ConsumeIntegral<int32_t>(), mFdp.PickValueInArray(kDataspaces), + {}); + + composer.setColorMode(display, mFdp.PickValueInArray(kColormodes), + mFdp.PickValueInArray(kRenderIntents)); + + setOutputBuffer(&composer, display); + + composer.setPowerMode(display, mFdp.PickValueInArray(kPowerModes)); + composer.setVsyncEnabled(display, mFdp.ConsumeBool() ? Vsync::ENABLE : Vsync::DISABLE); + + composer.setClientTargetSlotCount(display); + + validateDisplay(&composer, display); + + presentOrValidateDisplay(&composer, display); + + composer.setCursorPosition(display, outLayer, mFdp.ConsumeIntegral<uint8_t>() /*x*/, + mFdp.ConsumeIntegral<uint8_t>() /*y*/); + + composer.setLayerBuffer(display, outLayer, mFdp.ConsumeIntegral<uint32_t>() /*slot*/, + sp<GraphicBuffer>(), mFdp.ConsumeIntegral<int32_t>() /*acquireFence*/); + + composer.setLayerSurfaceDamage(display, outLayer, {} /*damage*/); + + composer.setLayerBlendMode(display, outLayer, mFdp.PickValueInArray(kBlendModes)); + + composer.setLayerColor(display, outLayer, + {mFdp.ConsumeFloatingPoint<float>() /*red*/, + mFdp.ConsumeFloatingPoint<float>() /*green*/, + mFdp.ConsumeFloatingPoint<float>() /*blue*/, + mFdp.ConsumeFloatingPoint<float>() /*alpha*/}); + composer.setLayerCompositionType(display, outLayer, mFdp.PickValueInArray(kCompositions)); + composer.setLayerDataspace(display, outLayer, mFdp.PickValueInArray(kDataspaces)); + composer.setLayerDisplayFrame(display, outLayer, {} /*frame*/); + composer.setLayerPlaneAlpha(display, outLayer, mFdp.ConsumeFloatingPoint<float>()); + + setLayerSidebandStream(&composer, display, outLayer); + + composer.setLayerSourceCrop(display, outLayer, {} /*crop*/); + + composer.setLayerTransform(display, outLayer, mFdp.PickValueInArray(kTransforms)); + + composer.setLayerVisibleRegion(display, outLayer, std::vector<IComposerClient::Rect>{}); + composer.setLayerZOrder(display, outLayer, mFdp.ConsumeIntegral<uint32_t>()); + + invokeComposerHal2_2(&composer, display, outLayer); + invokeComposerHal2_3(&composer, display, outLayer); + invokeComposerHal2_4(&composer, display, outLayer); + + composer.executeCommands(); + composer.resetCommands(); + + composer.destroyLayer(display, outLayer); + composer.destroyVirtualDisplay(display); +} + +void DisplayHardwareFuzzer::invokeComposerHal2_2(Hwc2::AidlComposer* composer, Display display, + Hwc2::V2_4::hal::Layer outLayer) { + const std::vector<PerFrameMetadata> perFrameMetadatas; + composer->setLayerPerFrameMetadata(display, outLayer, perFrameMetadatas); + + composer->getPerFrameMetadataKeys(display); + std::vector<RenderIntent> outRenderIntents; + + composer->getRenderIntents(display, mFdp.PickValueInArray(kColormodes), &outRenderIntents); + mat4 outMatrix; + composer->getDataspaceSaturationMatrix(mFdp.PickValueInArray(kDataspaces), &outMatrix); +} + +void DisplayHardwareFuzzer::invokeComposerHal2_3(Hwc2::AidlComposer* composer, Display display, + Hwc2::V2_4::hal::Layer outLayer) { + composer->setDisplayContentSamplingEnabled(display, mFdp.ConsumeBool() /*enabled*/, + mFdp.ConsumeIntegral<uint8_t>() /*componentMask*/, + mFdp.ConsumeIntegral<uint64_t>() /*maxFrames*/); + + DisplayedFrameStats outStats; + composer->getDisplayedContentSample(display, mFdp.ConsumeIntegral<uint64_t>() /*maxFrames*/, + mFdp.ConsumeIntegral<uint64_t>() /*timestamp*/, &outStats); + + composer->setLayerPerFrameMetadataBlobs(display, outLayer, std::vector<PerFrameMetadataBlob>{}); + + composer->setDisplayBrightness(display, mFdp.ConsumeFloatingPoint<float>(), + Hwc2::Composer::DisplayBrightnessOptions{ + .applyImmediately = mFdp.ConsumeIntegral<bool>()}); +} + +void DisplayHardwareFuzzer::invokeComposerHal2_4(Hwc2::AidlComposer* composer, Display display, + Hwc2::V2_4::hal::Layer outLayer) { + VsyncPeriodChangeTimeline outTimeline; + composer->setActiveConfigWithConstraints(display, Config{}, + IComposerClient::VsyncPeriodChangeConstraints{}, + &outTimeline); + + composer->setAutoLowLatencyMode(display, mFdp.ConsumeBool()); + + composer->setContentType(display, mFdp.PickValueInArray(kContentTypes)); + + std::vector<uint8_t> value; + value.push_back(mFdp.ConsumeIntegral<uint8_t>()); + composer->setLayerGenericMetadata(display, outLayer, mFdp.ConsumeRandomLengthString() /*key*/, + mFdp.ConsumeBool() /*mandatory*/, value); +} + +ui::Size DisplayHardwareFuzzer::getFuzzedSize() { + ui::Size size{mFdp.ConsumeIntegral<int32_t>() /*width*/, + mFdp.ConsumeIntegral<int32_t>() /*height*/}; + return size; +} + +mat4 DisplayHardwareFuzzer::getFuzzedMatrix() { + mat4 matrix{mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(), + mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(), + mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(), + mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(), + mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(), + mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(), + mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(), + mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>()}; + return matrix; +} + +void DisplayHardwareFuzzer::setCursorPosition(HWC2::Layer* layer) { + layer->setCursorPosition(mFdp.ConsumeIntegral<int32_t>() /*x*/, + mFdp.ConsumeIntegral<int32_t>() /*y*/); +} + +void DisplayHardwareFuzzer::setBuffer(HWC2::Layer* layer) { + layer->setBuffer(mFdp.ConsumeIntegral<uint32_t>() /*slot*/, sp<GraphicBuffer>(), + sp<Fence>::make()); +} + +void DisplayHardwareFuzzer::setSurfaceDamage(HWC2::Layer* layer) { + Rect rhs{mFdp.ConsumeIntegral<uint32_t>() /*width*/, + mFdp.ConsumeIntegral<uint32_t>() /*height*/}; + const Region damage{rhs}; + layer->setSurfaceDamage(damage); +} + +void DisplayHardwareFuzzer::setVisibleRegion(HWC2::Layer* layer) { + uint32_t width = mFdp.ConsumeIntegral<uint32_t>(); + uint32_t height = mFdp.ConsumeIntegral<uint32_t>(); + Rect rect{width, height}; + const Region region{rect}; + layer->setVisibleRegion(region); +} + +void DisplayHardwareFuzzer::setDisplayFrame(HWC2::Layer* layer) { + uint32_t width = mFdp.ConsumeIntegral<uint32_t>(); + uint32_t height = mFdp.ConsumeIntegral<uint32_t>(); + const Rect frame{width, height}; + layer->setDisplayFrame(frame); +} + +void DisplayHardwareFuzzer::setLayerGenericMetadata(HWC2::Layer* layer) { + std::vector<uint8_t> value; + value.push_back(mFdp.ConsumeIntegral<uint8_t>()); + layer->setLayerGenericMetadata(mFdp.ConsumeRandomLengthString().c_str() /*name*/, + mFdp.ConsumeBool() /*mandatory*/, value); +} + +void DisplayHardwareFuzzer::setSidebandStream(HWC2::Layer* layer) { + const native_handle_t stream{}; + layer->setSidebandStream(&stream); +} + +void DisplayHardwareFuzzer::invokeLayer(HWC2::Layer* layer) { + setCursorPosition(layer); + setBuffer(layer); + setSurfaceDamage(layer); + + layer->setBlendMode(mFdp.PickValueInArray(kBlendModes)); + layer->setColor({mFdp.ConsumeFloatingPoint<float>() /*red*/, + mFdp.ConsumeFloatingPoint<float>() /*green*/, + mFdp.ConsumeFloatingPoint<float>() /*blue*/, + mFdp.ConsumeFloatingPoint<float>() /*alpha*/}); + layer->setCompositionType(mFdp.PickValueInArray(kCompositions)); + layer->setDataspace(mFdp.PickValueInArray(kDataspaces)); + + layer->setPerFrameMetadata(mFdp.ConsumeIntegral<int32_t>(), getFuzzedHdrMetadata(&mFdp)); + setDisplayFrame(layer); + + layer->setPlaneAlpha(mFdp.ConsumeFloatingPoint<float>()); + + setSidebandStream(layer); + + layer->setSourceCrop(getFuzzedFloatRect(&mFdp)); + layer->setTransform(mFdp.PickValueInArray(kTransforms)); + + setVisibleRegion(layer); + + layer->setZOrder(mFdp.ConsumeIntegral<uint32_t>()); + + layer->setColorTransform(getFuzzedMatrix()); + + setLayerGenericMetadata(layer); +} + +void DisplayHardwareFuzzer::invokeFrameBufferSurface() { + sp<IGraphicBufferProducer> bqProducer = sp<mock::GraphicBufferProducer>::make(); + sp<IGraphicBufferConsumer> bqConsumer; + BufferQueue::createBufferQueue(&bqProducer, &bqConsumer); + + sp<FramebufferSurface> surface = + new FramebufferSurface(mHwc, mPhysicalDisplayId, bqConsumer, getFuzzedSize() /*size*/, + getFuzzedSize() /*maxSize*/); + surface->beginFrame(mFdp.ConsumeBool()); + + surface->prepareFrame(mFdp.PickValueInArray(kCompositionTypes)); + surface->advanceFrame(); + surface->onFrameCommitted(); + String8 result = String8(mFdp.ConsumeRandomLengthString().c_str()); + surface->dumpAsString(result); + surface->resizeBuffers(getFuzzedSize()); + surface->getClientTargetAcquireFence(); +} + +void DisplayHardwareFuzzer::invokeVirtualDisplaySurface() { + DisplayIdGenerator<HalVirtualDisplayId> mGenerator; + VirtualDisplayId VirtualDisplayId = mGenerator.generateId().value(); + + sp<SurfaceComposerClient> mClient = new SurfaceComposerClient(); + sp<SurfaceControl> mSurfaceControl = + mClient->createSurface(String8("TestSurface"), 100, 100, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceBufferState, + /*parent*/ nullptr); + + sp<BLASTBufferQueue> mBlastBufferQueueAdapter = + new BLASTBufferQueue("TestBLASTBufferQueue", mSurfaceControl, 100, 100, + PIXEL_FORMAT_RGBA_8888); + + sp<IGraphicBufferProducer> sink = mBlastBufferQueueAdapter->getIGraphicBufferProducer(); + sp<IGraphicBufferProducer> bqProducer = mBlastBufferQueueAdapter->getIGraphicBufferProducer(); + sp<IGraphicBufferConsumer> bqConsumer; + BufferQueue::createBufferQueue(&bqProducer, &bqConsumer); + BufferQueue::createBufferQueue(&sink, &bqConsumer); + + sp<VirtualDisplaySurface> surface = + new VirtualDisplaySurface(mHwc, VirtualDisplayId, sink, bqProducer, bqConsumer, + mFdp.ConsumeRandomLengthString().c_str() /*name*/); + + surface->beginFrame(mFdp.ConsumeBool()); + surface->prepareFrame(mFdp.PickValueInArray(kCompositionTypes)); + surface->resizeBuffers(getFuzzedSize()); + surface->getClientTargetAcquireFence(); + surface->advanceFrame(); + surface->onFrameCommitted(); + String8 result = String8(mFdp.ConsumeRandomLengthString().c_str()); + surface->dumpAsString(result); +} + +void DisplayHardwareFuzzer::invokeComposer() { + HalVirtualDisplayId halVirtualDisplayId = mGenerator.generateId().value(); + HalDisplayId halDisplayID = HalDisplayId{halVirtualDisplayId}; + + android::hardware::graphics::composer::hal::TestHWC2ComposerCallback composerCallback{}; + mHwc.setCallback(composerCallback); + + ui::PixelFormat pixelFormat{}; + if (!mHwc.allocateVirtualDisplay(halVirtualDisplayId, getFuzzedSize(), &pixelFormat)) { + return; + } + + getDisplayIdentificationData(); + + mHwc.hasDisplayCapability(halDisplayID, mFdp.PickValueInArray(kDisplayCapability)); + + mHwc.allocatePhysicalDisplay(kHwDisplayId, mPhysicalDisplayId); + + static auto hwcLayer = mHwc.createLayer(halDisplayID); + HWC2::Layer* layer = hwcLayer.get(); + invokeLayer(layer); + + getDeviceCompositionChanges(halDisplayID); + + mHwc.setClientTarget(halDisplayID, mFdp.ConsumeIntegral<uint32_t>(), Fence::NO_FENCE, + sp<GraphicBuffer>::make(), mFdp.PickValueInArray(kDataspaces)); + + mHwc.presentAndGetReleaseFences(halDisplayID, std::chrono::steady_clock::now(), + FenceTime::NO_FENCE); + + mHwc.setPowerMode(mPhysicalDisplayId, mFdp.PickValueInArray(kPowerModes)); + + mHwc.setColorTransform(halDisplayID, getFuzzedMatrix()); + + mHwc.getPresentFence(halDisplayID); + + mHwc.getLayerReleaseFence(halDisplayID, layer); + + mHwc.setOutputBuffer(halVirtualDisplayId, sp<Fence>::make().get(), sp<GraphicBuffer>::make()); + + mHwc.clearReleaseFences(halDisplayID); + + getHdrCapabilities(halDisplayID); + + mHwc.getSupportedPerFrameMetadata(halDisplayID); + + mHwc.getRenderIntents(halDisplayID, ui::ColorMode()); + + mHwc.getDataspaceSaturationMatrix(halDisplayID, ui::Dataspace()); + + getDisplayedContentSamplingAttributes(halDisplayID); + + mHwc.setDisplayContentSamplingEnabled(halDisplayID, mFdp.ConsumeBool() /*enabled*/, + mFdp.ConsumeIntegral<uint8_t>() /*componentMask*/, + mFdp.ConsumeIntegral<uint64_t>() /*maxFrames*/); + + getDisplayedContentSample(halDisplayID); + + mHwc.setDisplayBrightness(mPhysicalDisplayId, mFdp.ConsumeFloatingPoint<float>(), + Hwc2::Composer::DisplayBrightnessOptions{ + .applyImmediately = mFdp.ConsumeIntegral<bool>()}); + + mHwc.onHotplug(kHwDisplayId, hal::Connection::CONNECTED); + mHwc.updatesDeviceProductInfoOnHotplugReconnect(); + + mHwc.onVsync(kHwDisplayId, mFdp.ConsumeIntegral<int64_t>()); + mHwc.setVsyncEnabled(mPhysicalDisplayId, + mFdp.ConsumeBool() ? hal::Vsync::ENABLE : hal::Vsync::DISABLE); + + mHwc.isConnected(mPhysicalDisplayId); + mHwc.getModes(mPhysicalDisplayId); + mHwc.getActiveMode(mPhysicalDisplayId); + mHwc.getColorModes(mPhysicalDisplayId); + mHwc.hasCapability(mFdp.PickValueInArray(kCapability)); + + mHwc.setActiveColorMode(mPhysicalDisplayId, mFdp.PickValueInArray(kColormodes), + mFdp.PickValueInArray(kRenderIntents)); + + mHwc.getDisplayConnectionType(mPhysicalDisplayId); + mHwc.isVsyncPeriodSwitchSupported(mPhysicalDisplayId); + + getDisplayVsyncPeriod(); + + setActiveModeWithConstraints(); + + mHwc.setAutoLowLatencyMode(mPhysicalDisplayId, mFdp.ConsumeBool()); + + getSupportedContentTypes(); + + mHwc.setContentType(mPhysicalDisplayId, mFdp.PickValueInArray(kContentTypes)); + + dumpHwc(); + + mHwc.toPhysicalDisplayId(kHwDisplayId); + mHwc.fromPhysicalDisplayId(mPhysicalDisplayId); + mHwc.disconnectDisplay(halDisplayID); + + static hal::HWDisplayId displayId = mFdp.ConsumeIntegral<hal::HWDisplayId>(); + mHwc.onHotplug(displayId, + mFdp.ConsumeBool() ? hal::Connection::DISCONNECTED : hal::Connection::CONNECTED); +} + +template <size_t N> +DisplayIdentificationData asDisplayIdentificationData(const unsigned char (&bytes)[N]) { + return DisplayIdentificationData(bytes, bytes + N - 1); +} + +void DisplayHardwareFuzzer::invokeDisplayIdentification() { + static const DisplayIdentificationData data = asDisplayIdentificationData(kInternalEdid); + isEdid(data); + parseEdid(data); + parseDisplayIdentificationData(mFdp.ConsumeIntegral<uint8_t>(), data); + getPnpId(getVirtualDisplayId(mFdp.ConsumeIntegral<uint32_t>())); + getPnpId(mFdp.ConsumeIntegral<uint8_t>()); +} + +void DisplayHardwareFuzzer::process() { + invokeComposer(); + invokeAidlComposer(); + invokeDisplayIdentification(); + invokeFrameBufferSurface(); + invokeVirtualDisplaySurface(); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + DisplayHardwareFuzzer displayHardwareFuzzer(data, size); + displayHardwareFuzzer.process(); + return 0; +} + +} // namespace android::fuzz diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h new file mode 100644 index 0000000000..6a6e3db733 --- /dev/null +++ b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h @@ -0,0 +1,104 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <utils/Condition.h> +#include <chrono> +#include <vector> + +#include <android/hardware/graphics/composer/2.4/IComposer.h> +#include <composer-hal/2.1/ComposerClient.h> +#include <composer-hal/2.2/ComposerClient.h> +#include <composer-hal/2.3/ComposerClient.h> +#include <composer-hal/2.4/ComposerClient.h> + +#include "DisplayHardware/HWC2.h" +#include "surfaceflinger_fuzzers_utils.h" + +namespace { +class LayerImpl; +class Frame; +class DelayedEventGenerator; +} // namespace + +namespace android { +class SurfaceComposerClient; +} // namespace android + +namespace android::hardware::graphics::composer::hal { + +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::HWC2::ComposerCallback; + +class ComposerCallbackBridge : public IComposerCallback { +public: + ComposerCallbackBridge(ComposerCallback* callback, bool vsyncSwitchingSupported) + : mCallback(callback), mVsyncSwitchingSupported(vsyncSwitchingSupported) {} + + Return<void> onHotplug(HWDisplayId display, Connection connection) override { + mCallback->onComposerHalHotplug(display, connection); + return Void(); + } + + Return<void> onRefresh(HWDisplayId display) override { + mCallback->onComposerHalRefresh(display); + return Void(); + } + + Return<void> onVsync(HWDisplayId display, int64_t timestamp) override { + if (!mVsyncSwitchingSupported) { + mCallback->onComposerHalVsync(display, timestamp, std::nullopt); + } + return Void(); + } + + Return<void> onVsync_2_4(HWDisplayId display, int64_t timestamp, + VsyncPeriodNanos vsyncPeriodNanos) override { + if (mVsyncSwitchingSupported) { + mCallback->onComposerHalVsync(display, timestamp, vsyncPeriodNanos); + } + return Void(); + } + + Return<void> onVsyncPeriodTimingChanged(HWDisplayId display, + const VsyncPeriodChangeTimeline& timeline) override { + mCallback->onComposerHalVsyncPeriodTimingChanged(display, timeline); + return Void(); + } + + Return<void> onSeamlessPossible(HWDisplayId display) override { + mCallback->onComposerHalSeamlessPossible(display); + return Void(); + } + +private: + ComposerCallback* const mCallback; + const bool mVsyncSwitchingSupported; +}; + +struct TestHWC2ComposerCallback : public HWC2::ComposerCallback { + virtual ~TestHWC2ComposerCallback() = default; + void onComposerHalHotplug(HWDisplayId, Connection){}; + void onComposerHalRefresh(HWDisplayId) {} + void onComposerHalVsync(HWDisplayId, int64_t, std::optional<VsyncPeriodNanos>) {} + void onComposerHalVsyncPeriodTimingChanged(HWDisplayId, const VsyncPeriodChangeTimeline&) {} + void onComposerHalSeamlessPossible(HWDisplayId) {} + void onComposerHalVsyncIdle(HWDisplayId) {} +}; + +} // namespace android::hardware::graphics::composer::hal diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp new file mode 100644 index 0000000000..4f89cd9a45 --- /dev/null +++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp @@ -0,0 +1,294 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <FuzzableDataspaces.h> +#include <binder/IServiceManager.h> +#include <fuzzer/FuzzedDataProvider.h> +#include <ui/DisplayStatInfo.h> +#include "surfaceflinger_fuzzers_utils.h" + +namespace android::fuzz { + +static constexpr LatchUnsignaledConfig kLatchUnsignaledConfig[] = { + LatchUnsignaledConfig::Always, + LatchUnsignaledConfig::Auto, + LatchUnsignaledConfig::Disabled, +}; + +static constexpr ui::PixelFormat kPixelFormats[] = {ui::PixelFormat::RGBA_8888, + ui::PixelFormat::RGBX_8888, + ui::PixelFormat::RGB_888, + ui::PixelFormat::RGB_565, + ui::PixelFormat::BGRA_8888, + ui::PixelFormat::YCBCR_422_SP, + ui::PixelFormat::YCRCB_420_SP, + ui::PixelFormat::YCBCR_422_I, + ui::PixelFormat::RGBA_FP16, + ui::PixelFormat::RAW16, + ui::PixelFormat::BLOB, + ui::PixelFormat::IMPLEMENTATION_DEFINED, + ui::PixelFormat::YCBCR_420_888, + ui::PixelFormat::RAW_OPAQUE, + ui::PixelFormat::RAW10, + ui::PixelFormat::RAW12, + ui::PixelFormat::RGBA_1010102, + ui::PixelFormat::Y8, + ui::PixelFormat::Y16, + ui::PixelFormat::YV12, + ui::PixelFormat::DEPTH_16, + ui::PixelFormat::DEPTH_24, + ui::PixelFormat::DEPTH_24_STENCIL_8, + ui::PixelFormat::DEPTH_32F, + ui::PixelFormat::DEPTH_32F_STENCIL_8, + ui::PixelFormat::STENCIL_8, + ui::PixelFormat::YCBCR_P010, + ui::PixelFormat::HSV_888}; + +static constexpr ui::Rotation kRotations[] = {ui::Rotation::Rotation0, ui::Rotation::Rotation90, + ui::Rotation::Rotation180, ui::Rotation::Rotation270}; + +static constexpr BnSurfaceComposer::ISurfaceComposerTag kSurfaceComposerTags[]{ + BnSurfaceComposer::BOOT_FINISHED, + BnSurfaceComposer::CREATE_CONNECTION, + BnSurfaceComposer::GET_STATIC_DISPLAY_INFO, + BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION, + BnSurfaceComposer::CREATE_DISPLAY, + BnSurfaceComposer::DESTROY_DISPLAY, + BnSurfaceComposer::GET_PHYSICAL_DISPLAY_TOKEN, + BnSurfaceComposer::SET_TRANSACTION_STATE, + BnSurfaceComposer::AUTHENTICATE_SURFACE, + BnSurfaceComposer::GET_SUPPORTED_FRAME_TIMESTAMPS, + BnSurfaceComposer::GET_DISPLAY_MODES, + BnSurfaceComposer::GET_ACTIVE_DISPLAY_MODE, + BnSurfaceComposer::GET_DISPLAY_STATE, + BnSurfaceComposer::CAPTURE_DISPLAY, + BnSurfaceComposer::CAPTURE_LAYERS, + BnSurfaceComposer::CLEAR_ANIMATION_FRAME_STATS, + BnSurfaceComposer::GET_ANIMATION_FRAME_STATS, + BnSurfaceComposer::SET_POWER_MODE, + BnSurfaceComposer::GET_DISPLAY_STATS, + BnSurfaceComposer::GET_HDR_CAPABILITIES, + BnSurfaceComposer::GET_DISPLAY_COLOR_MODES, + BnSurfaceComposer::GET_ACTIVE_COLOR_MODE, + BnSurfaceComposer::SET_ACTIVE_COLOR_MODE, + BnSurfaceComposer::ENABLE_VSYNC_INJECTIONS, + BnSurfaceComposer::INJECT_VSYNC, + BnSurfaceComposer::GET_LAYER_DEBUG_INFO, + BnSurfaceComposer::GET_COMPOSITION_PREFERENCE, + BnSurfaceComposer::GET_COLOR_MANAGEMENT, + BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES, + BnSurfaceComposer::SET_DISPLAY_CONTENT_SAMPLING_ENABLED, + BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLE, + BnSurfaceComposer::GET_PROTECTED_CONTENT_SUPPORT, + BnSurfaceComposer::IS_WIDE_COLOR_DISPLAY, + BnSurfaceComposer::GET_DISPLAY_NATIVE_PRIMARIES, + BnSurfaceComposer::GET_PHYSICAL_DISPLAY_IDS, + BnSurfaceComposer::ADD_REGION_SAMPLING_LISTENER, + BnSurfaceComposer::REMOVE_REGION_SAMPLING_LISTENER, + BnSurfaceComposer::SET_DESIRED_DISPLAY_MODE_SPECS, + BnSurfaceComposer::GET_DESIRED_DISPLAY_MODE_SPECS, + BnSurfaceComposer::GET_DISPLAY_BRIGHTNESS_SUPPORT, + BnSurfaceComposer::SET_DISPLAY_BRIGHTNESS, + BnSurfaceComposer::CAPTURE_DISPLAY_BY_ID, + BnSurfaceComposer::NOTIFY_POWER_BOOST, + BnSurfaceComposer::SET_GLOBAL_SHADOW_SETTINGS, + BnSurfaceComposer::GET_AUTO_LOW_LATENCY_MODE_SUPPORT, + BnSurfaceComposer::SET_AUTO_LOW_LATENCY_MODE, + BnSurfaceComposer::GET_GAME_CONTENT_TYPE_SUPPORT, + BnSurfaceComposer::SET_GAME_CONTENT_TYPE, + BnSurfaceComposer::SET_FRAME_RATE, + BnSurfaceComposer::ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN, + BnSurfaceComposer::SET_FRAME_TIMELINE_INFO, + BnSurfaceComposer::ADD_TRANSACTION_TRACE_LISTENER, + BnSurfaceComposer::GET_GPU_CONTEXT_PRIORITY, + BnSurfaceComposer::GET_MAX_ACQUIRED_BUFFER_COUNT, + BnSurfaceComposer::GET_DYNAMIC_DISPLAY_INFO, + BnSurfaceComposer::ADD_FPS_LISTENER, + BnSurfaceComposer::REMOVE_FPS_LISTENER, + BnSurfaceComposer::OVERRIDE_HDR_TYPES, + BnSurfaceComposer::ADD_HDR_LAYER_INFO_LISTENER, + BnSurfaceComposer::REMOVE_HDR_LAYER_INFO_LISTENER, + BnSurfaceComposer::ON_PULL_ATOM, + BnSurfaceComposer::ADD_TUNNEL_MODE_ENABLED_LISTENER, + BnSurfaceComposer::REMOVE_TUNNEL_MODE_ENABLED_LISTENER, + BnSurfaceComposer::ADD_WINDOW_INFOS_LISTENER, + BnSurfaceComposer::REMOVE_WINDOW_INFOS_LISTENER, +}; + +static constexpr uint32_t kMinCode = 1000; +static constexpr uint32_t kMaxCode = 1050; + +class SurfaceFlingerFuzzer { +public: + SurfaceFlingerFuzzer(const uint8_t *data, size_t size) : mFdp(data, size) { + mFlinger = mTestableFlinger.flinger(); + }; + void process(const uint8_t *data, size_t size); + +private: + void setUp(); + void invokeFlinger(); + void setTransactionState(); + void setInternalDisplayPrimaries(); + void setDisplayStateLocked(); + void onTransact(const uint8_t *data, size_t size); + + FuzzedDataProvider mFdp; + TestableSurfaceFlinger mTestableFlinger; + sp<SurfaceFlinger> mFlinger = nullptr; +}; + +void SurfaceFlingerFuzzer::invokeFlinger() { + mFlinger->setSchedFifo(mFdp.ConsumeBool()); + mFlinger->setSchedAttr(mFdp.ConsumeBool()); + mFlinger->getServiceName(); + mFlinger->hasSyncFramework = mFdp.ConsumeBool(); + mFlinger->dispSyncPresentTimeOffset = mFdp.ConsumeIntegral<int64_t>(); + mFlinger->useHwcForRgbToYuv = mFdp.ConsumeBool(); + mFlinger->maxFrameBufferAcquiredBuffers = mFdp.ConsumeIntegral<int64_t>(); + mFlinger->maxGraphicsWidth = mFdp.ConsumeIntegral<uint32_t>(); + mFlinger->maxGraphicsHeight = mFdp.ConsumeIntegral<uint32_t>(); + mFlinger->hasWideColorDisplay = mFdp.ConsumeBool(); + mFlinger->internalDisplayOrientation = mFdp.PickValueInArray(kRotations); + mFlinger->useContextPriority = mFdp.ConsumeBool(); + + mFlinger->defaultCompositionDataspace = mFdp.PickValueInArray(kDataspaces); + mFlinger->defaultCompositionPixelFormat = mFdp.PickValueInArray(kPixelFormats); + mFlinger->wideColorGamutCompositionDataspace = mFdp.PickValueInArray(kDataspaces); + mFlinger->wideColorGamutCompositionPixelFormat = mFdp.PickValueInArray(kPixelFormats); + + mFlinger->enableLatchUnsignaledConfig = mFdp.PickValueInArray(kLatchUnsignaledConfig); + + mFlinger->scheduleComposite(mFdp.ConsumeBool() + ? scheduler::ISchedulerCallback::FrameHint::kActive + : scheduler::ISchedulerCallback::FrameHint::kNone); + + mFlinger->scheduleRepaint(); + mFlinger->scheduleSample(); + + uint32_t texture = mFlinger->getNewTexture(); + mFlinger->deleteTextureAsync(texture); + + sp<IBinder> handle = defaultServiceManager()->checkService( + String16(mFdp.ConsumeRandomLengthString().c_str())); + mFlinger->fromHandle(handle); + mFlinger->windowInfosReported(); + mFlinger->disableExpensiveRendering(); +} + +void SurfaceFlingerFuzzer::setInternalDisplayPrimaries() { + ui::DisplayPrimaries primaries; + primaries.red.X = mFdp.ConsumeFloatingPoint<float>(); + primaries.red.Y = mFdp.ConsumeFloatingPoint<float>(); + primaries.red.Z = mFdp.ConsumeFloatingPoint<float>(); + primaries.green.X = mFdp.ConsumeFloatingPoint<float>(); + primaries.green.Y = mFdp.ConsumeFloatingPoint<float>(); + primaries.green.Z = mFdp.ConsumeFloatingPoint<float>(); + primaries.blue.X = mFdp.ConsumeFloatingPoint<float>(); + primaries.blue.Y = mFdp.ConsumeFloatingPoint<float>(); + primaries.blue.Z = mFdp.ConsumeFloatingPoint<float>(); + primaries.white.X = mFdp.ConsumeFloatingPoint<float>(); + primaries.white.Y = mFdp.ConsumeFloatingPoint<float>(); + primaries.white.Z = mFdp.ConsumeFloatingPoint<float>(); + mTestableFlinger.setInternalDisplayPrimaries(primaries); +} + +void SurfaceFlingerFuzzer::setTransactionState() { + Vector<ComposerState> states; + Vector<DisplayState> displays; + ComposerState composerState; + composerState.state.what = layer_state_t::eLayerChanged; + composerState.state.surface = nullptr; + states.add(composerState); + uint32_t flags = mFdp.ConsumeIntegral<uint32_t>(); + const sp<IBinder> applyToken = nullptr; + int64_t desiredPresentTime = mFdp.ConsumeIntegral<int64_t>(); + bool isAutoTimestamp = mFdp.ConsumeBool(); + bool hasListenerCallbacks = mFdp.ConsumeBool(); + std::vector<ListenerCallbacks> listenerCallbacks{}; + uint64_t transactionId = mFdp.ConsumeIntegral<uint64_t>(); + + mTestableFlinger.setTransactionState(FrameTimelineInfo{}, states, displays, flags, applyToken, + InputWindowCommands{}, desiredPresentTime, isAutoTimestamp, + {}, hasListenerCallbacks, listenerCallbacks, + transactionId); +} + +void SurfaceFlingerFuzzer::setDisplayStateLocked() { + DisplayState state{}; + mTestableFlinger.setDisplayStateLocked(state); +} + +void SurfaceFlingerFuzzer::onTransact(const uint8_t *data, size_t size) { + Parcel fuzzedData, reply; + fuzzedData.writeInterfaceToken(String16("android.ui.ISurfaceComposer")); + fuzzedData.setData(data, size); + fuzzedData.setDataPosition(0); + uint32_t code = mFdp.ConsumeBool() ? mFdp.PickValueInArray(kSurfaceComposerTags) + : mFdp.ConsumeIntegralInRange<uint32_t>(kMinCode, kMaxCode); + mTestableFlinger.onTransact(code, fuzzedData, &reply, 0); +} + +void SurfaceFlingerFuzzer::setUp() { + mTestableFlinger.setupScheduler(std::make_unique<android::mock::VsyncController>(), + std::make_unique<android::mock::VSyncTracker>(), + std::make_unique<android::mock::EventThread>(), + std::make_unique<android::mock::EventThread>()); + + mTestableFlinger.setupTimeStats(std::make_unique<android::mock::TimeStats>()); + + std::unique_ptr<android::renderengine::RenderEngine> renderEngine = + std::make_unique<android::renderengine::mock::RenderEngine>(); + mTestableFlinger.setupRenderEngine(std::move(renderEngine)); + mTestableFlinger.setupComposer(std::make_unique<android::Hwc2::mock::Composer>()); +} + +void SurfaceFlingerFuzzer::process(const uint8_t *data, size_t size) { + setUp(); + + invokeFlinger(); + + mTestableFlinger.fuzzSurfaceFlinger(data, size); + + mTestableFlinger.setCreateBufferQueueFunction( + surfaceflinger::test::Factory::CreateBufferQueueFunction()); + mTestableFlinger.setCreateNativeWindowSurface( + surfaceflinger::test::Factory::CreateNativeWindowSurfaceFunction()); + + setInternalDisplayPrimaries(); + + mTestableFlinger.enableHalVirtualDisplays(mFdp.ConsumeBool()); + + mTestableFlinger.commitTransactionsLocked(mFdp.ConsumeIntegral<uint32_t>()); + + mTestableFlinger.notifyPowerBoost(mFdp.ConsumeIntegral<int32_t>()); + + setDisplayStateLocked(); + + setTransactionState(); + mTestableFlinger.flushTransactionQueues(); + + onTransact(data, size); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + android::fuzz::SurfaceFlingerFuzzer surfaceFlingerFuzzer(data, size); + surfaceFlingerFuzzer.process(data, size); + return 0; +} + +} // namespace android::fuzz diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h new file mode 100644 index 0000000000..0a458c2810 --- /dev/null +++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h @@ -0,0 +1,803 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <compositionengine/Display.h> +#include <compositionengine/LayerFECompositionState.h> +#include <compositionengine/OutputLayer.h> +#include <compositionengine/impl/CompositionEngine.h> +#include <compositionengine/impl/Display.h> +#include <compositionengine/impl/OutputLayerCompositionState.h> +#include <gui/LayerDebugInfo.h> +#include <gui/ScreenCaptureResults.h> +#include <gui/SurfaceComposerClient.h> +#include <gui/mock/GraphicBufferProducer.h> +#include <ui/DisplayStatInfo.h> +#include <ui/DynamicDisplayInfo.h> + +#include "BufferQueueLayer.h" +#include "BufferStateLayer.h" +#include "ContainerLayer.h" +#include "DisplayDevice.h" +#include "DisplayHardware/ComposerHal.h" +#include "EffectLayer.h" +#include "FrameTimeline/FrameTimeline.h" +#include "FrameTracer/FrameTracer.h" +#include "Layer.h" +#include "NativeWindowSurface.h" +#include "Scheduler/EventThread.h" +#include "Scheduler/MessageQueue.h" +#include "Scheduler/RefreshRateConfigs.h" +#include "Scheduler/VSyncTracker.h" +#include "Scheduler/VsyncConfiguration.h" +#include "Scheduler/VsyncController.h" +#include "Scheduler/VsyncModulator.h" +#include "StartPropertySetThread.h" +#include "SurfaceFlinger.h" +#include "SurfaceFlingerDefaultFactory.h" +#include "SurfaceInterceptor.h" +#include "TimeStats/TimeStats.h" + +#include "renderengine/mock/RenderEngine.h" +#include "scheduler/TimeKeeper.h" +#include "tests/unittests/mock/DisplayHardware/MockComposer.h" +#include "tests/unittests/mock/DisplayHardware/MockHWC2.h" +#include "tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h" +#include "tests/unittests/mock/MockEventThread.h" +#include "tests/unittests/mock/MockFrameTimeline.h" +#include "tests/unittests/mock/MockFrameTracer.h" +#include "tests/unittests/mock/MockNativeWindowSurface.h" +#include "tests/unittests/mock/MockSurfaceInterceptor.h" +#include "tests/unittests/mock/MockTimeStats.h" +#include "tests/unittests/mock/MockVSyncTracker.h" +#include "tests/unittests/mock/MockVsyncController.h" + +namespace android { +namespace Hwc2 { + +class Composer; + +namespace types = hardware::graphics::common; + +namespace V2_1 = hardware::graphics::composer::V2_1; +namespace V2_2 = hardware::graphics::composer::V2_2; +namespace V2_3 = hardware::graphics::composer::V2_3; +namespace V2_4 = hardware::graphics::composer::V2_4; + +using types::V1_0::ColorTransform; +using types::V1_0::Transform; +using types::V1_1::RenderIntent; +using types::V1_2::ColorMode; +using types::V1_2::Dataspace; +using types::V1_2::Hdr; +using types::V1_2::PixelFormat; + +using V2_1::Config; +using V2_1::Display; +using V2_1::Error; +using V2_1::Layer; +using V2_4::CommandReaderBase; +using V2_4::CommandWriterBase; +using V2_4::IComposer; +using V2_4::IComposerCallback; +using V2_4::IComposerClient; +using V2_4::VsyncPeriodChangeTimeline; +using V2_4::VsyncPeriodNanos; +using DisplayCapability = IComposerClient::DisplayCapability; +using PerFrameMetadata = IComposerClient::PerFrameMetadata; +using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey; +using PerFrameMetadataBlob = IComposerClient::PerFrameMetadataBlob; +}; // namespace Hwc2 + +static constexpr hal::HWDisplayId kHwDisplayId = 1000; + +static constexpr ui::Hdr kHdrTypes[] = {ui::Hdr::DOLBY_VISION, ui::Hdr::HDR10, ui::Hdr::HLG, + ui::Hdr::HDR10_PLUS}; + +static constexpr ui::ColorMode kColormodes[] = {ui::ColorMode::NATIVE, + ui::ColorMode::STANDARD_BT601_625, + ui::ColorMode::STANDARD_BT601_625_UNADJUSTED, + ui::ColorMode::STANDARD_BT601_525, + ui::ColorMode::STANDARD_BT601_525_UNADJUSTED, + ui::ColorMode::STANDARD_BT709, + ui::ColorMode::DCI_P3, + ui::ColorMode::SRGB, + ui::ColorMode::ADOBE_RGB, + ui::ColorMode::DISPLAY_P3, + ui::ColorMode::BT2020, + ui::ColorMode::BT2100_PQ, + ui::ColorMode::BT2100_HLG, + ui::ColorMode::DISPLAY_BT2020}; + +FloatRect getFuzzedFloatRect(FuzzedDataProvider *fdp) { + return FloatRect(fdp->ConsumeFloatingPoint<float>() /*left*/, + fdp->ConsumeFloatingPoint<float>() /*right*/, + fdp->ConsumeFloatingPoint<float>() /*top*/, + fdp->ConsumeFloatingPoint<float>() /*bottom*/); +} + +HdrMetadata getFuzzedHdrMetadata(FuzzedDataProvider *fdp) { + HdrMetadata hdrMetadata; + if (fdp->ConsumeBool()) { + hdrMetadata.cta8613.maxContentLightLevel = fdp->ConsumeFloatingPoint<float>(); + hdrMetadata.cta8613.maxFrameAverageLightLevel = fdp->ConsumeFloatingPoint<float>(); + + hdrMetadata.validTypes |= HdrMetadata::CTA861_3; + } else { + hdrMetadata.smpte2086.displayPrimaryRed.x = fdp->ConsumeFloatingPoint<float>(); + hdrMetadata.smpte2086.displayPrimaryRed.y = fdp->ConsumeFloatingPoint<float>(); + hdrMetadata.smpte2086.displayPrimaryGreen.x = fdp->ConsumeFloatingPoint<float>(); + hdrMetadata.smpte2086.displayPrimaryGreen.y = fdp->ConsumeFloatingPoint<float>(); + hdrMetadata.smpte2086.displayPrimaryBlue.x = fdp->ConsumeFloatingPoint<float>(); + hdrMetadata.smpte2086.displayPrimaryBlue.y = fdp->ConsumeFloatingPoint<float>(); + hdrMetadata.smpte2086.whitePoint.x = fdp->ConsumeFloatingPoint<float>(); + hdrMetadata.smpte2086.whitePoint.y = fdp->ConsumeFloatingPoint<float>(); + hdrMetadata.smpte2086.minLuminance = fdp->ConsumeFloatingPoint<float>(); + hdrMetadata.smpte2086.maxLuminance = fdp->ConsumeFloatingPoint<float>(); + + hdrMetadata.validTypes |= HdrMetadata::SMPTE2086; + } + return hdrMetadata; +} + +class EventThread; + +namespace hal = android::hardware::graphics::composer::hal; + +struct FakePhaseOffsets : scheduler::VsyncConfiguration { + static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0; + static constexpr auto FAKE_DURATION_OFFSET_NS = std::chrono::nanoseconds(0); + + VsyncConfigSet getConfigsForRefreshRate(Fps) const override { return getCurrentConfigs(); } + + VsyncConfigSet getCurrentConfigs() const override { + return {{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS, + FAKE_DURATION_OFFSET_NS}, + {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS, + FAKE_DURATION_OFFSET_NS}, + {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS, + FAKE_DURATION_OFFSET_NS}, + FAKE_DURATION_OFFSET_NS}; + } + + void reset() override {} + void setRefreshRateFps(Fps) override {} + void dump(std::string &) const override {} +}; +namespace scheduler { +class TestableScheduler : public Scheduler, private ICompositor { +public: + TestableScheduler(const std::shared_ptr<scheduler::RefreshRateConfigs> &refreshRateConfigs, + ISchedulerCallback &callback) + : TestableScheduler(std::make_unique<android::mock::VsyncController>(), + std::make_unique<android::mock::VSyncTracker>(), refreshRateConfigs, + callback) {} + + void scheduleFrame(){}; + void postMessage(sp<MessageHandler> &&){}; + + TestableScheduler(std::unique_ptr<VsyncController> controller, + std::unique_ptr<VSyncTracker> tracker, + std::shared_ptr<RefreshRateConfigs> configs, ISchedulerCallback &callback) + : Scheduler(*this, callback, Feature::kContentDetection) { + mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller))); + setRefreshRateConfigs(std::move(configs)); + } + + ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) { + return Scheduler::createConnection(std::move(eventThread)); + } + + auto &mutablePrimaryHWVsyncEnabled() { return mPrimaryHWVsyncEnabled; } + auto &mutableHWVsyncAvailable() { return mHWVsyncAvailable; } + + auto &mutableLayerHistory() { return mLayerHistory; } + + auto refreshRateConfigs() { return holdRefreshRateConfigs(); } + + void replaceTouchTimer(int64_t millis) { + if (mTouchTimer) { + mTouchTimer.reset(); + } + mTouchTimer.emplace( + "Testable Touch timer", std::chrono::milliseconds(millis), + [this] { touchTimerCallback(TimerState::Reset); }, + [this] { touchTimerCallback(TimerState::Expired); }); + mTouchTimer->start(); + } + + bool isTouchActive() { + std::lock_guard<std::mutex> lock(mPolicyLock); + return mPolicy.touch == Scheduler::TouchState::Active; + } + + void dispatchCachedReportedMode() { + std::lock_guard<std::mutex> lock(mPolicyLock); + return Scheduler::dispatchCachedReportedMode(); + } + + void clearCachedReportedMode() { + std::lock_guard<std::mutex> lock(mPolicyLock); + mPolicy.cachedModeChangedParams.reset(); + } + + void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) { + return Scheduler::onNonPrimaryDisplayModeChanged(handle, mode); + } + +private: + // ICompositor overrides: + bool commit(nsecs_t, int64_t, nsecs_t) override { return false; } + void composite(nsecs_t) override {} + void sample() override {} +}; +}; // namespace scheduler + +namespace surfaceflinger::test { + +class Factory final : public surfaceflinger::Factory { +public: + ~Factory() = default; + + std::unique_ptr<HWComposer> createHWComposer(const std::string &) override { return nullptr; } + + std::unique_ptr<MessageQueue> createMessageQueue(ICompositor &compositor) { + return std::make_unique<android::impl::MessageQueue>(compositor); + } + + std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration( + Fps /*currentRefreshRate*/) override { + return std::make_unique<FakePhaseOffsets>(); + } + + std::unique_ptr<scheduler::Scheduler> createScheduler( + const std::shared_ptr<scheduler::RefreshRateConfigs> &, + scheduler::ISchedulerCallback &) { + return nullptr; + } + + sp<SurfaceInterceptor> createSurfaceInterceptor() override { + return new android::impl::SurfaceInterceptor(); + } + + sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override { + return new StartPropertySetThread(timestampPropertyValue); + } + + sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs &creationArgs) override { + return new DisplayDevice(creationArgs); + } + + sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format, + uint32_t layerCount, uint64_t usage, + std::string requestorName) override { + return new GraphicBuffer(width, height, format, layerCount, usage, requestorName); + } + + void createBufferQueue(sp<IGraphicBufferProducer> *outProducer, + sp<IGraphicBufferConsumer> *outConsumer, + bool consumerIsSurfaceFlinger) override { + if (!mCreateBufferQueue) { + BufferQueue::createBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger); + return; + } + mCreateBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger); + } + + sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer> &producer, + const sp<SurfaceFlinger> &flinger, + const wp<Layer> &layer) override { + return new MonitoredProducer(producer, flinger, layer); + } + + sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer> &consumer, + renderengine::RenderEngine &renderEngine, + uint32_t textureName, Layer *layer) override { + return new BufferLayerConsumer(consumer, renderEngine, textureName, layer); + } + + std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface( + const sp<IGraphicBufferProducer> &producer) override { + if (!mCreateNativeWindowSurface) return nullptr; + return mCreateNativeWindowSurface(producer); + } + + std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() override { + return compositionengine::impl::createCompositionEngine(); + } + + sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs &) override { + return nullptr; + } + + sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs &) override { + return nullptr; + } + + sp<EffectLayer> createEffectLayer(const LayerCreationArgs &args) override { + return new EffectLayer(args); + } + + sp<ContainerLayer> createContainerLayer(const LayerCreationArgs &args) override { + return new ContainerLayer(args); + } + + std::unique_ptr<FrameTracer> createFrameTracer() override { + return std::make_unique<android::mock::FrameTracer>(); + } + + std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline( + std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid = 0) override { + return std::make_unique<android::mock::FrameTimeline>(timeStats, surfaceFlingerPid); + } + + using CreateBufferQueueFunction = + std::function<void(sp<IGraphicBufferProducer> * /* outProducer */, + sp<IGraphicBufferConsumer> * /* outConsumer */, + bool /* consumerIsSurfaceFlinger */)>; + CreateBufferQueueFunction mCreateBufferQueue; + + using CreateNativeWindowSurfaceFunction = + std::function<std::unique_ptr<surfaceflinger::NativeWindowSurface>( + const sp<IGraphicBufferProducer> &)>; + CreateNativeWindowSurfaceFunction mCreateNativeWindowSurface; + + using CreateCompositionEngineFunction = + std::function<std::unique_ptr<compositionengine::CompositionEngine>()>; + CreateCompositionEngineFunction mCreateCompositionEngine; +}; + +} // namespace surfaceflinger::test + +// TODO(b/189053744) : Create a common test/mock library for surfaceflinger +class TestableSurfaceFlinger final : private scheduler::ISchedulerCallback { +public: + using HotplugEvent = SurfaceFlinger::HotplugEvent; + + SurfaceFlinger *flinger() { return mFlinger.get(); } + scheduler::TestableScheduler *scheduler() { return mScheduler; } + + // Allow reading display state without locking, as if called on the SF main thread. + auto onInitializeDisplays() NO_THREAD_SAFETY_ANALYSIS { + return mFlinger->onInitializeDisplays(); + } + + void scheduleComposite(FrameHint){}; + + void setGlobalShadowSettings(FuzzedDataProvider *fdp) { + const half4 ambientColor{fdp->ConsumeFloatingPoint<float>(), + fdp->ConsumeFloatingPoint<float>(), + fdp->ConsumeFloatingPoint<float>(), + fdp->ConsumeFloatingPoint<float>()}; + const half4 spotColor{fdp->ConsumeFloatingPoint<float>(), + fdp->ConsumeFloatingPoint<float>(), + fdp->ConsumeFloatingPoint<float>(), + fdp->ConsumeFloatingPoint<float>()}; + float lightPosY = fdp->ConsumeFloatingPoint<float>(); + float lightPosZ = fdp->ConsumeFloatingPoint<float>(); + float lightRadius = fdp->ConsumeFloatingPoint<float>(); + mFlinger->setGlobalShadowSettings(ambientColor, spotColor, lightPosY, lightPosZ, + lightRadius); + } + + void onPullAtom(FuzzedDataProvider *fdp) { + const int32_t atomId = fdp->ConsumeIntegral<uint8_t>(); + std::string pulledData = fdp->ConsumeRandomLengthString().c_str(); + bool success = fdp->ConsumeBool(); + mFlinger->onPullAtom(atomId, &pulledData, &success); + } + + void fuzzDumpsysAndDebug(FuzzedDataProvider *fdp) { + std::string result = fdp->ConsumeRandomLengthString().c_str(); + mFlinger->appendSfConfigString(result); + result = fdp->ConsumeRandomLengthString().c_str(); + mFlinger->listLayersLocked(result); + + using DumpArgs = Vector<String16>; + DumpArgs dumpArgs; + dumpArgs.push_back(String16(fdp->ConsumeRandomLengthString().c_str())); + mFlinger->clearStatsLocked(dumpArgs, result); + + mFlinger->dumpTimeStats(dumpArgs, fdp->ConsumeBool(), result); + mFlinger->logFrameStats(); + + result = fdp->ConsumeRandomLengthString().c_str(); + mFlinger->dumpFrameTimeline(dumpArgs, result); + + result = fdp->ConsumeRandomLengthString().c_str(); + mFlinger->dumpStaticScreenStats(result); + + result = fdp->ConsumeRandomLengthString().c_str(); + mFlinger->dumpFrameEventsLocked(result); + + result = fdp->ConsumeRandomLengthString().c_str(); + mFlinger->dumpRawDisplayIdentificationData(dumpArgs, result); + + LayersProto layersProto = mFlinger->dumpDrawingStateProto(fdp->ConsumeIntegral<uint32_t>()); + mFlinger->dumpOffscreenLayersProto(layersProto); + LayersTraceProto layersTraceProto{}; + mFlinger->dumpDisplayProto(layersTraceProto); + + result = fdp->ConsumeRandomLengthString().c_str(); + mFlinger->dumpHwc(result); + + mFlinger->calculateColorMatrix(fdp->ConsumeFloatingPoint<float>()); + mFlinger->updateColorMatrixLocked(); + mFlinger->CheckTransactCodeCredentials(fdp->ConsumeIntegral<uint32_t>()); + + const CountDownLatch transactionCommittedSignal(fdp->ConsumeIntegral<uint32_t>()); + mFlinger->waitForSynchronousTransaction(transactionCommittedSignal); + mFlinger->signalSynchronousTransactions(fdp->ConsumeIntegral<uint32_t>()); + } + + void getCompositionPreference() { + ui::Dataspace outDataspace; + ui::PixelFormat outPixelFormat; + ui::Dataspace outWideColorGamutDataspace; + ui::PixelFormat outWideColorGamutPixelFormat; + mFlinger->getCompositionPreference(&outDataspace, &outPixelFormat, + &outWideColorGamutDataspace, + &outWideColorGamutPixelFormat); + } + + void overrideHdrTypes(sp<IBinder> &display, FuzzedDataProvider *fdp) { + std::vector<ui::Hdr> hdrTypes; + hdrTypes.push_back(fdp->PickValueInArray(kHdrTypes)); + mFlinger->overrideHdrTypes(display, hdrTypes); + } + + void getDisplayedContentSample(sp<IBinder> &display, FuzzedDataProvider *fdp) { + DisplayedFrameStats outDisplayedFrameStats; + mFlinger->getDisplayedContentSample(display, fdp->ConsumeIntegral<uint64_t>(), + fdp->ConsumeIntegral<uint64_t>(), + &outDisplayedFrameStats); + } + + void getDisplayStats(sp<IBinder> &display) { + android::DisplayStatInfo stats; + mFlinger->getDisplayStats(display, &stats); + } + + void getDisplayState(sp<IBinder> &display) { + ui::DisplayState displayState; + mFlinger->getDisplayState(display, &displayState); + } + + void getStaticDisplayInfo(sp<IBinder> &display) { + ui::StaticDisplayInfo staticDisplayInfo; + mFlinger->getStaticDisplayInfo(display, &staticDisplayInfo); + } + + void getDynamicDisplayInfo(sp<IBinder> &display) { + android::ui::DynamicDisplayInfo dynamicDisplayInfo; + mFlinger->getDynamicDisplayInfo(display, &dynamicDisplayInfo); + } + void getDisplayNativePrimaries(sp<IBinder> &display) { + android::ui::DisplayPrimaries displayPrimaries; + mFlinger->getDisplayNativePrimaries(display, displayPrimaries); + } + + void getDesiredDisplayModeSpecs(sp<IBinder> &display) { + ui::DisplayModeId outDefaultMode; + bool outAllowGroupSwitching; + float outPrimaryRefreshRateMin; + float outPrimaryRefreshRateMax; + float outAppRequestRefreshRateMin; + float outAppRequestRefreshRateMax; + mFlinger->getDesiredDisplayModeSpecs(display, &outDefaultMode, &outAllowGroupSwitching, + &outPrimaryRefreshRateMin, &outPrimaryRefreshRateMax, + &outAppRequestRefreshRateMin, + &outAppRequestRefreshRateMax); + } + + void setVsyncConfig(FuzzedDataProvider *fdp) { + const scheduler::VsyncModulator::VsyncConfig vsyncConfig{}; + mFlinger->setVsyncConfig(vsyncConfig, fdp->ConsumeIntegral<nsecs_t>()); + } + + void updateCompositorTiming(FuzzedDataProvider *fdp) { + std::shared_ptr<FenceTime> presentFenceTime = FenceTime::NO_FENCE; + mFlinger->updateCompositorTiming({}, fdp->ConsumeIntegral<nsecs_t>(), presentFenceTime); + } + + void getCompositorTiming() { + CompositorTiming compositorTiming; + mFlinger->getCompositorTiming(&compositorTiming); + } + + sp<IBinder> fuzzBoot(FuzzedDataProvider *fdp) { + mFlinger->callingThreadHasUnscopedSurfaceFlingerAccess(fdp->ConsumeBool()); + mFlinger->createConnection(); + + DisplayIdGenerator<HalVirtualDisplayId> kGenerator; + HalVirtualDisplayId halVirtualDisplayId = kGenerator.generateId().value(); + + ui::Size uiSize{fdp->ConsumeIntegral<int32_t>(), fdp->ConsumeIntegral<int32_t>()}; + ui::PixelFormat pixelFormat{}; + mFlinger->getHwComposer().allocateVirtualDisplay(halVirtualDisplayId, uiSize, &pixelFormat); + + PhysicalDisplayId physicalDisplayId = SurfaceComposerClient::getInternalDisplayId().value(); + mFlinger->getHwComposer().allocatePhysicalDisplay(kHwDisplayId, physicalDisplayId); + + sp<IBinder> display = + mFlinger->createDisplay(String8(fdp->ConsumeRandomLengthString().c_str()), + fdp->ConsumeBool()); + + onInitializeDisplays(); + mFlinger->getPhysicalDisplayToken(physicalDisplayId); + + mFlinger->mStartPropertySetThread = + mFlinger->getFactory().createStartPropertySetThread(fdp->ConsumeBool()); + + mFlinger->bootFinished(); + + return display; + } + + void fuzzSurfaceFlinger(const uint8_t *data, size_t size) { + FuzzedDataProvider mFdp(data, size); + + sp<IBinder> display = fuzzBoot(&mFdp); + + sp<IGraphicBufferProducer> bufferProducer = sp<mock::GraphicBufferProducer>::make(); + mFlinger->authenticateSurfaceTexture(bufferProducer.get()); + + mFlinger->createDisplayEventConnection(); + + getDisplayStats(display); + getDisplayState(display); + getStaticDisplayInfo(display); + getDynamicDisplayInfo(display); + getDisplayNativePrimaries(display); + + mFlinger->setAutoLowLatencyMode(display, mFdp.ConsumeBool()); + mFlinger->setGameContentType(display, mFdp.ConsumeBool()); + mFlinger->setPowerMode(display, mFdp.ConsumeIntegral<int>()); + mFlinger->clearAnimationFrameStats(); + + overrideHdrTypes(display, &mFdp); + + onPullAtom(&mFdp); + + mFlinger->injectVSync(mFdp.ConsumeIntegral<nsecs_t>()); + + getCompositionPreference(); + getDisplayedContentSample(display, &mFdp); + getDesiredDisplayModeSpecs(display); + + bool outSupport; + mFlinger->getDisplayBrightnessSupport(display, &outSupport); + + mFlinger->notifyPowerBoost(mFdp.ConsumeIntegral<int32_t>()); + + setGlobalShadowSettings(&mFdp); + + mFlinger->binderDied(display); + mFlinger->onFirstRef(); + + mFlinger->commitTransactions(); + mFlinger->updateInputFlinger(); + mFlinger->updateCursorAsync(); + + setVsyncConfig(&mFdp); + + mFlinger->flushTransactionQueues(0); + + mFlinger->setTransactionFlags(mFdp.ConsumeIntegral<uint32_t>()); + mFlinger->clearTransactionFlags(mFdp.ConsumeIntegral<uint32_t>()); + mFlinger->commitOffscreenLayers(); + + mFlinger->frameIsEarly(mFdp.ConsumeIntegral<nsecs_t>(), mFdp.ConsumeIntegral<int64_t>()); + mFlinger->computeLayerBounds(); + mFlinger->startBootAnim(); + + mFlinger->readPersistentProperties(); + + mFlinger->exceedsMaxRenderTargetSize(mFdp.ConsumeIntegral<uint32_t>(), + mFdp.ConsumeIntegral<uint32_t>()); + + mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mFdp.ConsumeIntegral<uid_t>()); + + mFlinger->postComposition(); + + getCompositorTiming(); + + updateCompositorTiming(&mFdp); + + mFlinger->setCompositorTimingSnapped({}, mFdp.ConsumeIntegral<nsecs_t>()); + mFlinger->postFrame(); + mFlinger->calculateExpectedPresentTime({}); + + mFlinger->enableHalVirtualDisplays(mFdp.ConsumeBool()); + + fuzzDumpsysAndDebug(&mFdp); + + mFlinger->destroyDisplay(display); + } + + void setupRenderEngine(std::unique_ptr<renderengine::RenderEngine> renderEngine) { + mFlinger->mCompositionEngine->setRenderEngine(std::move(renderEngine)); + } + + void setupComposer(std::unique_ptr<Hwc2::Composer> composer) { + mFlinger->mCompositionEngine->setHwComposer( + std::make_unique<impl::HWComposer>(std::move(composer))); + } + + void setupTimeStats(const std::shared_ptr<TimeStats> &timeStats) { + mFlinger->mCompositionEngine->setTimeStats(timeStats); + } + + // The ISchedulerCallback argument can be nullptr for a no-op implementation. + void setupScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController, + std::unique_ptr<scheduler::VSyncTracker> vsyncTracker, + std::unique_ptr<EventThread> appEventThread, + std::unique_ptr<EventThread> sfEventThread, + scheduler::ISchedulerCallback *callback = nullptr, + bool hasMultipleModes = false) { + DisplayModes modes{DisplayMode::Builder(0) + .setId(DisplayModeId(0)) + .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0)) + .setVsyncPeriod(16'666'667) + .setGroup(0) + .build()}; + + if (hasMultipleModes) { + modes.emplace_back(DisplayMode::Builder(1) + .setId(DisplayModeId(1)) + .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0)) + .setVsyncPeriod(11'111'111) + .setGroup(0) + .build()); + } + + const auto currMode = DisplayModeId(0); + mRefreshRateConfigs = std::make_shared<scheduler::RefreshRateConfigs>(modes, currMode); + const auto currFps = mRefreshRateConfigs->getCurrentRefreshRate().getFps(); + mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(currFps); + mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make( + mFlinger->mVsyncConfiguration->getCurrentConfigs()); + mFlinger->mRefreshRateStats = + std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, currFps, + /*powerMode=*/hal::PowerMode::OFF); + + mScheduler = new scheduler::TestableScheduler(std::move(vsyncController), + std::move(vsyncTracker), mRefreshRateConfigs, + *(callback ?: this)); + + mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread)); + mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread)); + resetScheduler(mScheduler); + } + + void resetScheduler(scheduler::Scheduler *scheduler) { mFlinger->mScheduler.reset(scheduler); } + + scheduler::TestableScheduler &mutableScheduler() const { return *mScheduler; } + + using CreateBufferQueueFunction = surfaceflinger::test::Factory::CreateBufferQueueFunction; + void setCreateBufferQueueFunction(CreateBufferQueueFunction f) { + mFactory.mCreateBufferQueue = f; + } + + using CreateNativeWindowSurfaceFunction = + surfaceflinger::test::Factory::CreateNativeWindowSurfaceFunction; + void setCreateNativeWindowSurface(CreateNativeWindowSurfaceFunction f) { + mFactory.mCreateNativeWindowSurface = f; + } + + void setInternalDisplayPrimaries(const ui::DisplayPrimaries &primaries) { + memcpy(&mFlinger->mInternalDisplayPrimaries, &primaries, sizeof(ui::DisplayPrimaries)); + } + + static auto &mutableLayerDrawingState(const sp<Layer> &layer) { return layer->mDrawingState; } + + auto &mutableStateLock() { return mFlinger->mStateLock; } + + static auto findOutputLayerForDisplay(const sp<Layer> &layer, + const sp<const DisplayDevice> &display) { + return layer->findOutputLayerForDisplay(display.get()); + } + + /* ------------------------------------------------------------------------ + * Forwarding for functions being tested + */ + + void enableHalVirtualDisplays(bool enable) { mFlinger->enableHalVirtualDisplays(enable); } + + auto commitTransactionsLocked(uint32_t transactionFlags) { + Mutex::Autolock lock(mFlinger->mStateLock); + return mFlinger->commitTransactionsLocked(transactionFlags); + } + + auto setDisplayStateLocked(const DisplayState &s) { + Mutex::Autolock lock(mFlinger->mStateLock); + return mFlinger->setDisplayStateLocked(s); + } + + auto notifyPowerBoost(int32_t boostId) { return mFlinger->notifyPowerBoost(boostId); } + + // Allow reading display state without locking, as if called on the SF main thread. + auto setPowerModeInternal(const sp<DisplayDevice> &display, + hal::PowerMode mode) NO_THREAD_SAFETY_ANALYSIS { + return mFlinger->setPowerModeInternal(display, mode); + } + + auto onMessageReceived(int32_t /*what*/) { return 0; } + + auto &getTransactionQueue() { return mFlinger->mTransactionQueue; } + auto &getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; } + + auto setTransactionState( + const FrameTimelineInfo &frameTimelineInfo, const Vector<ComposerState> &states, + const Vector<DisplayState> &displays, uint32_t flags, const sp<IBinder> &applyToken, + const InputWindowCommands &inputWindowCommands, int64_t desiredPresentTime, + bool isAutoTimestamp, const client_cache_t &uncacheBuffer, bool hasListenerCallbacks, + std::vector<ListenerCallbacks> &listenerCallbacks, uint64_t transactionId) { + return mFlinger->setTransactionState(frameTimelineInfo, states, displays, flags, applyToken, + inputWindowCommands, desiredPresentTime, + isAutoTimestamp, uncacheBuffer, hasListenerCallbacks, + listenerCallbacks, transactionId); + } + + auto flushTransactionQueues() { return mFlinger->flushTransactionQueues(0); }; + + auto onTransact(uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) { + return mFlinger->onTransact(code, data, reply, flags); + } + + auto getGPUContextPriority() { return mFlinger->getGPUContextPriority(); } + + auto calculateMaxAcquiredBufferCount(Fps refreshRate, + std::chrono::nanoseconds presentLatency) const { + return SurfaceFlinger::calculateMaxAcquiredBufferCount(refreshRate, presentLatency); + } + + /* Read-write access to private data to set up preconditions and assert + * post-conditions. + */ + + auto &mutableCurrentState() { return mFlinger->mCurrentState; } + auto &mutableDisplays() { return mFlinger->mDisplays; } + auto &mutableDrawingState() { return mFlinger->mDrawingState; } + auto &mutableInterceptor() { return mFlinger->mInterceptor; } + + auto fromHandle(const sp<IBinder> &handle) { return mFlinger->fromHandle(handle); } + + ~TestableSurfaceFlinger() { + mutableDisplays().clear(); + mutableCurrentState().displays.clear(); + mutableDrawingState().displays.clear(); + mutableInterceptor().clear(); + mFlinger->mScheduler.reset(); + mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>()); + mFlinger->mCompositionEngine->setRenderEngine( + std::unique_ptr<renderengine::RenderEngine>()); + } + +private: + void scheduleRefresh(FrameHint) {} + void setVsyncEnabled(bool) override {} + void changeRefreshRate(const RefreshRate &, DisplayModeEvent) override {} + void kernelTimerChanged(bool) override {} + void triggerOnFrameRateOverridesChanged() {} + + surfaceflinger::test::Factory mFactory; + sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization); + scheduler::TestableScheduler *mScheduler = nullptr; + std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs; +}; +} // namespace android diff --git a/services/surfaceflinger/layerproto/transactions.proto b/services/surfaceflinger/layerproto/transactions.proto index 9b076bd282..fcf4499dcb 100644 --- a/services/surfaceflinger/layerproto/transactions.proto +++ b/services/surfaceflinger/layerproto/transactions.proto @@ -89,7 +89,6 @@ message LayerState { eReleaseBufferListenerChanged = 0x00000400; eShadowRadiusChanged = 0x00000800; - eLayerCreated = 0x00001000; eBufferCropChanged = 0x00002000; eRelativeLayerChanged = 0x00004000; eReparent = 0x00008000; diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index 5af17ecaf1..48f18b9bed 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -21,6 +21,24 @@ package { default_applicable_licenses: ["frameworks_native_license"], } +filegroup { + name: "libsurfaceflinger_mock_sources", + srcs: [ + "mock/DisplayHardware/MockComposer.cpp", + "mock/DisplayHardware/MockHWC2.cpp", + "mock/DisplayHardware/MockPowerAdvisor.cpp", + "mock/MockEventThread.cpp", + "mock/MockFrameTimeline.cpp", + "mock/MockFrameTracer.cpp", + "mock/MockNativeWindowSurface.cpp", + "mock/MockSurfaceInterceptor.cpp", + "mock/MockTimeStats.cpp", + "mock/MockVsyncController.cpp", + "mock/MockVSyncTracker.cpp", + "mock/system/window/MockNativeWindow.cpp", + ], +} + cc_test { name: "libsurfaceflinger_unittest", defaults: [ @@ -45,6 +63,7 @@ cc_test { address: true, }, srcs: [ + ":libsurfaceflinger_mock_sources", ":libsurfaceflinger_sources", "libsurfaceflinger_unittest_main.cpp", "CachingTest.cpp", @@ -101,18 +120,6 @@ cc_test { "VSyncPredictorTest.cpp", "VSyncReactorTest.cpp", "VsyncConfigurationTest.cpp", - "mock/DisplayHardware/MockComposer.cpp", - "mock/DisplayHardware/MockHWC2.cpp", - "mock/DisplayHardware/MockPowerAdvisor.cpp", - "mock/MockEventThread.cpp", - "mock/MockFrameTimeline.cpp", - "mock/MockFrameTracer.cpp", - "mock/MockNativeWindowSurface.cpp", - "mock/MockSurfaceInterceptor.cpp", - "mock/MockTimeStats.cpp", - "mock/MockVsyncController.cpp", - "mock/MockVSyncTracker.cpp", - "mock/system/window/MockNativeWindow.cpp", ], static_libs: [ "android.hardware.common-V2-ndk", diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp index 2af0480cd0..0069441499 100644 --- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp +++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp @@ -89,6 +89,7 @@ struct MockHWC2ComposerCallback final : StrictMock<HWC2::ComposerCallback> { MOCK_METHOD2(onComposerHalVsyncPeriodTimingChanged, void(hal::HWDisplayId, const hal::VsyncPeriodChangeTimeline&)); MOCK_METHOD1(onComposerHalSeamlessPossible, void(hal::HWDisplayId)); + MOCK_METHOD1(onComposerHalVsyncIdle, void(hal::HWDisplayId)); }; struct HWComposerSetCallbackTest : testing::Test { @@ -110,11 +111,9 @@ TEST_F(HWComposerSetCallbackTest, loadsLayerMetadataSupport) { }), Return(hardware::graphics::composer::V2_4::Error::NONE))); EXPECT_CALL(*mHal, registerCallback(_)); - EXPECT_CALL(*mHal, isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching)) - .WillOnce(Return(false)); impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)}; - hwc.setCallback(&mCallback); + hwc.setCallback(mCallback); const auto& supported = hwc.getSupportedLayerGenericMetadata(); EXPECT_EQ(2u, supported.size()); @@ -129,11 +128,9 @@ TEST_F(HWComposerSetCallbackTest, handlesUnsupportedCallToGetLayerGenericMetadat EXPECT_CALL(*mHal, getLayerGenericMetadataKeys(_)) .WillOnce(Return(hardware::graphics::composer::V2_4::Error::UNSUPPORTED)); EXPECT_CALL(*mHal, registerCallback(_)); - EXPECT_CALL(*mHal, isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching)) - .WillOnce(Return(false)); impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)}; - hwc.setCallback(&mCallback); + hwc.setCallback(mCallback); const auto& supported = hwc.getSupportedLayerGenericMetadata(); EXPECT_EQ(0u, supported.size()); diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp index 2bafabddf3..4efcc051bb 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp @@ -14,10 +14,6 @@ * limitations under the License. */ -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wextra" - #undef LOG_TAG #define LOG_TAG "SchedulerUnittests" @@ -33,6 +29,21 @@ using namespace std::chrono_literals; namespace android::scheduler { +namespace { + +DisplayModePtr createDisplayMode(DisplayModeId modeId, Fps refreshRate, int32_t group = 0, + ui::Size resolution = ui::Size()) { + return DisplayMode::Builder(hal::HWConfigId(modeId.value())) + .setId(modeId) + .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0)) + .setVsyncPeriod(static_cast<int32_t>(refreshRate.getPeriodNsecs())) + .setGroup(group) + .setHeight(resolution.height) + .setWidth(resolution.width) + .build(); +} + +} // namespace namespace hal = android::hardware::graphics::composer::hal; @@ -40,143 +51,107 @@ using RefreshRate = RefreshRateConfigs::RefreshRate; using LayerVoteType = RefreshRateConfigs::LayerVoteType; using LayerRequirement = RefreshRateConfigs::LayerRequirement; -class RefreshRateConfigsTest : public testing::Test { -protected: - using GetBestRefreshRateInvocation = RefreshRateConfigs::GetBestRefreshRateInvocation; - - RefreshRateConfigsTest(); - ~RefreshRateConfigsTest(); +struct TestableRefreshRateConfigs : RefreshRateConfigs { + using RefreshRateConfigs::RefreshRateConfigs; - RefreshRate createRefreshRate(DisplayModePtr displayMode) { - return {displayMode, RefreshRate::ConstructorTag(0)}; + RefreshRate getMinSupportedRefreshRate() const { + std::lock_guard lock(mLock); + return *mMinSupportedRefreshRate; } - Fps findClosestKnownFrameRate(const RefreshRateConfigs& refreshRateConfigs, Fps frameRate) { - return refreshRateConfigs.findClosestKnownFrameRate(frameRate); + RefreshRate getMaxSupportedRefreshRate() const { + std::lock_guard lock(mLock); + return *mMaxSupportedRefreshRate; } - std::vector<Fps> getKnownFrameRate(const RefreshRateConfigs& refreshRateConfigs) { - return refreshRateConfigs.mKnownFrameRates; + RefreshRate getMinRefreshRateByPolicy() const { + std::lock_guard lock(mLock); + return getMinRefreshRateByPolicyLocked(); } - RefreshRate getMinRefreshRateByPolicy(const RefreshRateConfigs& refreshRateConfigs) { - std::lock_guard lock(refreshRateConfigs.mLock); - return refreshRateConfigs.getMinRefreshRateByPolicyLocked(); - } + const std::vector<Fps>& knownFrameRates() const { return mKnownFrameRates; } - RefreshRate getMinSupportedRefreshRate(const RefreshRateConfigs& refreshRateConfigs) { - std::lock_guard lock(refreshRateConfigs.mLock); - return *refreshRateConfigs.mMinSupportedRefreshRate; - } + using RefreshRateConfigs::GetBestRefreshRateCache; + auto& mutableGetBestRefreshRateCache() { return mGetBestRefreshRateCache; } - RefreshRate getMaxSupportedRefreshRate(const RefreshRateConfigs& refreshRateConfigs) { - std::lock_guard lock(refreshRateConfigs.mLock); - return *refreshRateConfigs.mMaxSupportedRefreshRate; + auto getBestRefreshRateAndSignals(const std::vector<LayerRequirement>& layers, + GlobalSignals signals) const { + return RefreshRateConfigs::getBestRefreshRate(layers, signals); } - void setLastBestRefreshRateInvocation(RefreshRateConfigs& refreshRateConfigs, - const GetBestRefreshRateInvocation& invocation) { - std::lock_guard lock(refreshRateConfigs.mLock); - refreshRateConfigs.lastBestRefreshRateInvocation.emplace( - GetBestRefreshRateInvocation(invocation)); + RefreshRate getBestRefreshRate(const std::vector<LayerRequirement>& layers = {}, + GlobalSignals signals = {}) const { + return getBestRefreshRateAndSignals(layers, signals).first; } +}; + +class RefreshRateConfigsTest : public testing::Test { +protected: + RefreshRateConfigsTest(); + ~RefreshRateConfigsTest(); - std::optional<GetBestRefreshRateInvocation> getLastBestRefreshRateInvocation( - const RefreshRateConfigs& refreshRateConfigs) { - std::lock_guard lock(refreshRateConfigs.mLock); - return refreshRateConfigs.lastBestRefreshRateInvocation; + static RefreshRate asRefreshRate(DisplayModePtr displayMode) { + return {displayMode, RefreshRate::ConstructorTag(0)}; } - // Test config IDs - static inline const DisplayModeId HWC_CONFIG_ID_60 = DisplayModeId(0); - static inline const DisplayModeId HWC_CONFIG_ID_90 = DisplayModeId(1); - static inline const DisplayModeId HWC_CONFIG_ID_72 = DisplayModeId(2); - static inline const DisplayModeId HWC_CONFIG_ID_120 = DisplayModeId(3); - static inline const DisplayModeId HWC_CONFIG_ID_30 = DisplayModeId(4); - static inline const DisplayModeId HWC_CONFIG_ID_25 = DisplayModeId(5); - static inline const DisplayModeId HWC_CONFIG_ID_50 = DisplayModeId(6); - static inline const DisplayModeId HWC_CONFIG_ID_24 = DisplayModeId(7); - static inline const DisplayModeId HWC_CONFIG_ID_24_FRAC = DisplayModeId(8); - static inline const DisplayModeId HWC_CONFIG_ID_30_FRAC = DisplayModeId(9); - static inline const DisplayModeId HWC_CONFIG_ID_60_FRAC = DisplayModeId(10); - - // Test configs - DisplayModePtr mConfig60 = createDisplayMode(HWC_CONFIG_ID_60, 0, (60_Hz).getPeriodNsecs()); - DisplayModePtr mConfig60Frac = - createDisplayMode(HWC_CONFIG_ID_60_FRAC, 0, (59.94_Hz).getPeriodNsecs()); - DisplayModePtr mConfig90 = createDisplayMode(HWC_CONFIG_ID_90, 0, (90_Hz).getPeriodNsecs()); - DisplayModePtr mConfig90DifferentGroup = - createDisplayMode(HWC_CONFIG_ID_90, 1, (90_Hz).getPeriodNsecs()); - DisplayModePtr mConfig90DifferentResolution = - createDisplayMode(HWC_CONFIG_ID_90, 0, (90_Hz).getPeriodNsecs(), ui::Size(111, 222)); - DisplayModePtr mConfig72 = createDisplayMode(HWC_CONFIG_ID_72, 0, (72_Hz).getPeriodNsecs()); - DisplayModePtr mConfig72DifferentGroup = - createDisplayMode(HWC_CONFIG_ID_72, 1, (72_Hz).getPeriodNsecs()); - DisplayModePtr mConfig120 = createDisplayMode(HWC_CONFIG_ID_120, 0, (120_Hz).getPeriodNsecs()); - DisplayModePtr mConfig120DifferentGroup = - createDisplayMode(HWC_CONFIG_ID_120, 1, (120_Hz).getPeriodNsecs()); - DisplayModePtr mConfig30 = createDisplayMode(HWC_CONFIG_ID_30, 0, (30_Hz).getPeriodNsecs()); - DisplayModePtr mConfig30DifferentGroup = - createDisplayMode(HWC_CONFIG_ID_30, 1, (30_Hz).getPeriodNsecs()); - DisplayModePtr mConfig30Frac = - createDisplayMode(HWC_CONFIG_ID_30_FRAC, 0, (29.97_Hz).getPeriodNsecs()); - DisplayModePtr mConfig25 = createDisplayMode(HWC_CONFIG_ID_25, 0, (25_Hz).getPeriodNsecs()); - DisplayModePtr mConfig25DifferentGroup = - createDisplayMode(HWC_CONFIG_ID_25, 1, (25_Hz).getPeriodNsecs()); - DisplayModePtr mConfig50 = createDisplayMode(HWC_CONFIG_ID_50, 0, (50_Hz).getPeriodNsecs()); - DisplayModePtr mConfig24 = createDisplayMode(HWC_CONFIG_ID_24, 0, (24_Hz).getPeriodNsecs()); - DisplayModePtr mConfig24Frac = - createDisplayMode(HWC_CONFIG_ID_24_FRAC, 0, (23.976_Hz).getPeriodNsecs()); - - // Test device configurations - // The positions of the configs in the arrays below MUST match their IDs. For example, - // the first config should always be 60Hz, the second 90Hz etc. - DisplayModes m60OnlyConfigDevice = {mConfig60}; - DisplayModes m60_90Device = {mConfig60, mConfig90}; - DisplayModes m60_90DeviceWithDifferentGroups = {mConfig60, mConfig90DifferentGroup}; - DisplayModes m60_90DeviceWithDifferentResolutions = {mConfig60, mConfig90DifferentResolution}; - DisplayModes m60_72_90Device = {mConfig60, mConfig90, mConfig72}; - DisplayModes m60_90_72_120Device = {mConfig60, mConfig90, mConfig72, mConfig120}; - DisplayModes m30_60_72_90_120Device = {mConfig60, mConfig90, mConfig72, mConfig120, mConfig30}; - DisplayModes m30_60Device = {mConfig60, mConfig90DifferentGroup, mConfig72DifferentGroup, - mConfig120DifferentGroup, mConfig30}; - DisplayModes m30_60_72_90Device = {mConfig60, mConfig90, mConfig72, mConfig120DifferentGroup, - mConfig30}; - DisplayModes m30_60_90Device = {mConfig60, mConfig90, mConfig72DifferentGroup, - mConfig120DifferentGroup, mConfig30}; - DisplayModes m25_30_50_60Device = {mConfig60, - mConfig90, - mConfig72DifferentGroup, - mConfig120DifferentGroup, - mConfig30DifferentGroup, - mConfig25DifferentGroup, - mConfig50}; - DisplayModes m60_120Device = {mConfig60, mConfig120}; + static constexpr DisplayModeId kModeId60{0}; + static constexpr DisplayModeId kModeId90{1}; + static constexpr DisplayModeId kModeId72{2}; + static constexpr DisplayModeId kModeId120{3}; + static constexpr DisplayModeId kModeId30{4}; + static constexpr DisplayModeId kModeId25{5}; + static constexpr DisplayModeId kModeId50{6}; + static constexpr DisplayModeId kModeId24{7}; + static constexpr DisplayModeId kModeId24Frac{8}; + static constexpr DisplayModeId kModeId30Frac{9}; + static constexpr DisplayModeId kModeId60Frac{10}; + + static inline const DisplayModePtr kMode60 = createDisplayMode(kModeId60, 60_Hz); + static inline const DisplayModePtr kMode60Frac = createDisplayMode(kModeId60Frac, 59.94_Hz); + static inline const DisplayModePtr kMode90 = createDisplayMode(kModeId90, 90_Hz); + static inline const DisplayModePtr kMode90_G1 = createDisplayMode(kModeId90, 90_Hz, 1); + static inline const DisplayModePtr kMode90_4K = + createDisplayMode(kModeId90, 90_Hz, 0, {3840, 2160}); + static inline const DisplayModePtr kMode72 = createDisplayMode(kModeId72, 72_Hz); + static inline const DisplayModePtr kMode72_G1 = createDisplayMode(kModeId72, 72_Hz, 1); + static inline const DisplayModePtr kMode120 = createDisplayMode(kModeId120, 120_Hz); + static inline const DisplayModePtr kMode120_G1 = createDisplayMode(kModeId120, 120_Hz, 1); + static inline const DisplayModePtr kMode30 = createDisplayMode(kModeId30, 30_Hz); + static inline const DisplayModePtr kMode30_G1 = createDisplayMode(kModeId30, 30_Hz, 1); + static inline const DisplayModePtr kMode30Frac = createDisplayMode(kModeId30Frac, 29.97_Hz); + static inline const DisplayModePtr kMode25 = createDisplayMode(kModeId25, 25_Hz); + static inline const DisplayModePtr kMode25_G1 = createDisplayMode(kModeId25, 25_Hz, 1); + static inline const DisplayModePtr kMode50 = createDisplayMode(kModeId50, 50_Hz); + static inline const DisplayModePtr kMode24 = createDisplayMode(kModeId24, 24_Hz); + static inline const DisplayModePtr kMode24Frac = createDisplayMode(kModeId24Frac, 23.976_Hz); + + // Test configurations. + static inline const DisplayModes kModes_60 = {kMode60}; + static inline const DisplayModes kModes_60_90 = {kMode60, kMode90}; + static inline const DisplayModes kModes_60_90_G1 = {kMode60, kMode90_G1}; + static inline const DisplayModes kModes_60_90_4K = {kMode60, kMode90_4K}; + static inline const DisplayModes kModes_60_72_90 = {kMode60, kMode90, kMode72}; + static inline const DisplayModes kModes_60_90_72_120 = {kMode60, kMode90, kMode72, kMode120}; + static inline const DisplayModes kModes_30_60_72_90_120 = {kMode60, kMode90, kMode72, kMode120, + kMode30}; + + static inline const DisplayModes kModes_30_60 = {kMode60, kMode90_G1, kMode72_G1, kMode120_G1, + kMode30}; + static inline const DisplayModes kModes_30_60_72_90 = {kMode60, kMode90, kMode72, kMode120_G1, + kMode30}; + static inline const DisplayModes kModes_30_60_90 = {kMode60, kMode90, kMode72_G1, kMode120_G1, + kMode30}; + static inline const DisplayModes kModes_25_30_50_60 = {kMode60, kMode90, kMode72_G1, + kMode120_G1, kMode30_G1, kMode25_G1, + kMode50}; + static inline const DisplayModes kModes_60_120 = {kMode60, kMode120}; // This is a typical TV configuration. - DisplayModes m24_25_30_50_60WithFracDevice = {mConfig24, mConfig24Frac, mConfig25, - mConfig30, mConfig30Frac, mConfig50, - mConfig60, mConfig60Frac}; - - // Expected RefreshRate objects - RefreshRate mExpected60Config = {mConfig60, RefreshRate::ConstructorTag(0)}; - RefreshRate mExpectedAlmost60Config = {createDisplayMode(HWC_CONFIG_ID_60, 0, 16666665), - RefreshRate::ConstructorTag(0)}; - RefreshRate mExpected90Config = {mConfig90, RefreshRate::ConstructorTag(0)}; - RefreshRate mExpected90DifferentGroupConfig = {mConfig90DifferentGroup, - RefreshRate::ConstructorTag(0)}; - RefreshRate mExpected90DifferentResolutionConfig = {mConfig90DifferentResolution, - RefreshRate::ConstructorTag(0)}; - RefreshRate mExpected72Config = {mConfig72, RefreshRate::ConstructorTag(0)}; - RefreshRate mExpected30Config = {mConfig30, RefreshRate::ConstructorTag(0)}; - RefreshRate mExpected120Config = {mConfig120, RefreshRate::ConstructorTag(0)}; - - DisplayModePtr createDisplayMode(DisplayModeId modeId, int32_t group, int64_t vsyncPeriod, - ui::Size resolution = ui::Size()); + static inline const DisplayModes kModes_24_25_30_50_60_Frac = {kMode24, kMode24Frac, kMode25, + kMode30, kMode30Frac, kMode50, + kMode60, kMode60Frac}; }; -using Builder = DisplayMode::Builder; - RefreshRateConfigsTest::RefreshRateConfigsTest() { const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); @@ -189,348 +164,315 @@ RefreshRateConfigsTest::~RefreshRateConfigsTest() { ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); } -DisplayModePtr RefreshRateConfigsTest::createDisplayMode(DisplayModeId modeId, int32_t group, - int64_t vsyncPeriod, ui::Size resolution) { - return DisplayMode::Builder(hal::HWConfigId(modeId.value())) - .setId(modeId) - .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0)) - .setVsyncPeriod(int32_t(vsyncPeriod)) - .setGroup(group) - .setHeight(resolution.height) - .setWidth(resolution.width) - .build(); -} - namespace { -TEST_F(RefreshRateConfigsTest, oneDeviceConfig_SwitchingSupported) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60OnlyConfigDevice, - /*currentConfigId=*/HWC_CONFIG_ID_60); +TEST_F(RefreshRateConfigsTest, oneMode_canSwitch) { + RefreshRateConfigs configs(kModes_60, kModeId60); + EXPECT_FALSE(configs.canSwitch()); } TEST_F(RefreshRateConfigsTest, invalidPolicy) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60OnlyConfigDevice, - /*currentConfigId=*/HWC_CONFIG_ID_60); - ASSERT_LT(refreshRateConfigs->setDisplayManagerPolicy({DisplayModeId(10), {60_Hz, 60_Hz}}), 0); - ASSERT_LT(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {20_Hz, 40_Hz}}), 0); + RefreshRateConfigs configs(kModes_60, kModeId60); + EXPECT_LT(configs.setDisplayManagerPolicy({DisplayModeId(10), {60_Hz, 60_Hz}}), 0); + EXPECT_LT(configs.setDisplayManagerPolicy({kModeId60, {20_Hz, 40_Hz}}), 0); } -TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90Device, - /*currentConfigId=*/HWC_CONFIG_ID_60); +TEST_F(RefreshRateConfigsTest, twoModes_storesFullRefreshRateMap) { + TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); + + const auto minRate = configs.getMinSupportedRefreshRate(); + const auto performanceRate = configs.getMaxSupportedRefreshRate(); - const auto& minRate = getMinSupportedRefreshRate(*refreshRateConfigs); - const auto& performanceRate = getMaxSupportedRefreshRate(*refreshRateConfigs); + EXPECT_EQ(asRefreshRate(kMode60), minRate); + EXPECT_EQ(asRefreshRate(kMode90), performanceRate); - ASSERT_EQ(mExpected60Config, minRate); - ASSERT_EQ(mExpected90Config, performanceRate); + const auto minRateByPolicy = configs.getMinRefreshRateByPolicy(); + const auto performanceRateByPolicy = configs.getMaxRefreshRateByPolicy(); - const auto& minRateByPolicy = getMinRefreshRateByPolicy(*refreshRateConfigs); - const auto& performanceRateByPolicy = refreshRateConfigs->getMaxRefreshRateByPolicy(); - ASSERT_EQ(minRateByPolicy, minRate); - ASSERT_EQ(performanceRateByPolicy, performanceRate); + EXPECT_EQ(minRateByPolicy, minRate); + EXPECT_EQ(performanceRateByPolicy, performanceRate); } -TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap_differentGroups) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups, - /*currentConfigId=*/HWC_CONFIG_ID_60); +TEST_F(RefreshRateConfigsTest, twoModes_storesFullRefreshRateMap_differentGroups) { + TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); - const auto& minRate = getMinRefreshRateByPolicy(*refreshRateConfigs); - const auto& performanceRate = getMaxSupportedRefreshRate(*refreshRateConfigs); - const auto& minRate60 = getMinRefreshRateByPolicy(*refreshRateConfigs); - const auto& performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy(); + const auto minRate = configs.getMinRefreshRateByPolicy(); + const auto performanceRate = configs.getMaxSupportedRefreshRate(); + const auto minRate60 = configs.getMinRefreshRateByPolicy(); + const auto performanceRate60 = configs.getMaxRefreshRateByPolicy(); - ASSERT_EQ(mExpected60Config, minRate); - ASSERT_EQ(mExpected60Config, minRate60); - ASSERT_EQ(mExpected60Config, performanceRate60); + EXPECT_EQ(asRefreshRate(kMode60), minRate); + EXPECT_EQ(asRefreshRate(kMode60), minRate60); + EXPECT_EQ(asRefreshRate(kMode60), performanceRate60); - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {60_Hz, 90_Hz}}), 0); - refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90); + EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}), 0); + configs.setCurrentModeId(kModeId90); - const auto& minRate90 = getMinRefreshRateByPolicy(*refreshRateConfigs); - const auto& performanceRate90 = refreshRateConfigs->getMaxRefreshRateByPolicy(); + const auto minRate90 = configs.getMinRefreshRateByPolicy(); + const auto performanceRate90 = configs.getMaxRefreshRateByPolicy(); - ASSERT_EQ(mExpected90DifferentGroupConfig, performanceRate); - ASSERT_EQ(mExpected90DifferentGroupConfig, minRate90); - ASSERT_EQ(mExpected90DifferentGroupConfig, performanceRate90); + EXPECT_EQ(asRefreshRate(kMode90_G1), performanceRate); + EXPECT_EQ(asRefreshRate(kMode90_G1), minRate90); + EXPECT_EQ(asRefreshRate(kMode90_G1), performanceRate90); } -TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap_differentResolutions) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentResolutions, - /*currentConfigId=*/HWC_CONFIG_ID_60); +TEST_F(RefreshRateConfigsTest, twoModes_storesFullRefreshRateMap_differentResolutions) { + TestableRefreshRateConfigs configs(kModes_60_90_4K, kModeId60); - const auto& minRate = getMinRefreshRateByPolicy(*refreshRateConfigs); - const auto& performanceRate = getMaxSupportedRefreshRate(*refreshRateConfigs); - const auto& minRate60 = getMinRefreshRateByPolicy(*refreshRateConfigs); - const auto& performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy(); + const auto minRate = configs.getMinRefreshRateByPolicy(); + const auto performanceRate = configs.getMaxSupportedRefreshRate(); + const auto minRate60 = configs.getMinRefreshRateByPolicy(); + const auto performanceRate60 = configs.getMaxRefreshRateByPolicy(); - ASSERT_EQ(mExpected60Config, minRate); - ASSERT_EQ(mExpected60Config, minRate60); - ASSERT_EQ(mExpected60Config, performanceRate60); + EXPECT_EQ(asRefreshRate(kMode60), minRate); + EXPECT_EQ(asRefreshRate(kMode60), minRate60); + EXPECT_EQ(asRefreshRate(kMode60), performanceRate60); - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {60_Hz, 90_Hz}}), 0); - refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90); + EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}), 0); + configs.setCurrentModeId(kModeId90); - const auto& minRate90 = getMinRefreshRateByPolicy(*refreshRateConfigs); - const auto& performanceRate90 = refreshRateConfigs->getMaxRefreshRateByPolicy(); + const auto minRate90 = configs.getMinRefreshRateByPolicy(); + const auto performanceRate90 = configs.getMaxRefreshRateByPolicy(); - ASSERT_EQ(mExpected90DifferentResolutionConfig, performanceRate); - ASSERT_EQ(mExpected90DifferentResolutionConfig, minRate90); - ASSERT_EQ(mExpected90DifferentResolutionConfig, performanceRate90); + EXPECT_EQ(asRefreshRate(kMode90_4K), performanceRate); + EXPECT_EQ(asRefreshRate(kMode90_4K), minRate90); + EXPECT_EQ(asRefreshRate(kMode90_4K), performanceRate90); } -TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_policyChange) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90Device, - /*currentConfigId=*/HWC_CONFIG_ID_60); +TEST_F(RefreshRateConfigsTest, twoModes_policyChange) { + TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); - auto minRate = getMinRefreshRateByPolicy(*refreshRateConfigs); - auto performanceRate = refreshRateConfigs->getMaxRefreshRateByPolicy(); + const auto minRate = configs.getMinRefreshRateByPolicy(); + const auto performanceRate = configs.getMaxRefreshRateByPolicy(); - ASSERT_EQ(mExpected60Config, minRate); - ASSERT_EQ(mExpected90Config, performanceRate); + EXPECT_EQ(asRefreshRate(kMode60), minRate); + EXPECT_EQ(asRefreshRate(kMode90), performanceRate); - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 60_Hz}}), 0); + EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0); - auto minRate60 = getMinRefreshRateByPolicy(*refreshRateConfigs); - auto performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy(); - ASSERT_EQ(mExpected60Config, minRate60); - ASSERT_EQ(mExpected60Config, performanceRate60); + const auto minRate60 = configs.getMinRefreshRateByPolicy(); + const auto performanceRate60 = configs.getMaxRefreshRateByPolicy(); + + EXPECT_EQ(asRefreshRate(kMode60), minRate60); + EXPECT_EQ(asRefreshRate(kMode60), performanceRate60); } -TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getCurrentRefreshRate) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90Device, - /*currentConfigId=*/HWC_CONFIG_ID_60); +TEST_F(RefreshRateConfigsTest, twoModes_getCurrentRefreshRate) { + TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); { - auto current = refreshRateConfigs->getCurrentRefreshRate(); - EXPECT_EQ(current.getModeId(), HWC_CONFIG_ID_60); + const auto current = configs.getCurrentRefreshRate(); + EXPECT_EQ(current.getModeId(), kModeId60); } - refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90); + configs.setCurrentModeId(kModeId90); { - auto current = refreshRateConfigs->getCurrentRefreshRate(); - EXPECT_EQ(current.getModeId(), HWC_CONFIG_ID_90); + const auto current = configs.getCurrentRefreshRate(); + EXPECT_EQ(current.getModeId(), kModeId90); } - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90_Hz, 90_Hz}}), 0); + EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}), 0); { - auto current = refreshRateConfigs->getCurrentRefreshRate(); - EXPECT_EQ(current.getModeId(), HWC_CONFIG_ID_90); + const auto current = configs.getCurrentRefreshRate(); + EXPECT_EQ(current.getModeId(), kModeId90); } } TEST_F(RefreshRateConfigsTest, getBestRefreshRate_noLayers) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_72_90Device, /*currentConfigId=*/ - HWC_CONFIG_ID_72); - - // If there are no layers we select the default frame rate, which is the max of the primary - // range. - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate({}, {})); + { + TestableRefreshRateConfigs configs(kModes_60_72_90, kModeId72); - ASSERT_EQ(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 60_Hz}}), - NO_ERROR); - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate({}, {})); + // If there are no layers we select the default frame rate, which is the max of the primary + // range. + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate()); - // We select max even when this will cause a non-seamless switch. - refreshRateConfigs = std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups, - /*currentConfigId=*/HWC_CONFIG_ID_60); - ASSERT_EQ(refreshRateConfigs->setDisplayManagerPolicy( - {HWC_CONFIG_ID_90, /*allowGroupSwitching*/ true, {0_Hz, 90_Hz}}), - NO_ERROR); - EXPECT_EQ(mExpected90DifferentGroupConfig, refreshRateConfigs->getBestRefreshRate({}, {})); + EXPECT_EQ(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), NO_ERROR); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate()); + } + { + // We select max even when this will cause a non-seamless switch. + TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); + constexpr bool kAllowGroupSwitching = true; + EXPECT_EQ(configs.setDisplayManagerPolicy({kModeId90, kAllowGroupSwitching, {0_Hz, 90_Hz}}), + NO_ERROR); + EXPECT_EQ(asRefreshRate(kMode90_G1), configs.getBestRefreshRate()); + } } TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_90) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90Device, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; lr.vote = LayerVoteType::Min; lr.name = "Min"; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr.vote = LayerVoteType::Max; lr.name = "Max"; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 90_Hz; lr.vote = LayerVoteType::Heuristic; lr.name = "90Hz Heuristic"; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 60_Hz; lr.name = "60Hz Heuristic"; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 45_Hz; lr.name = "45Hz Heuristic"; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 30_Hz; lr.name = "30Hz Heuristic"; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 24_Hz; lr.name = "24Hz Heuristic"; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr.name = ""; - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 60_Hz}}), 0); + EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0); lr.vote = LayerVoteType::Min; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr.vote = LayerVoteType::Max; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 90_Hz; lr.vote = LayerVoteType::Heuristic; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 60_Hz; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 45_Hz; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 30_Hz; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 24_Hz; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90_Hz, 90_Hz}}), 0); + EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}), 0); lr.vote = LayerVoteType::Min; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.vote = LayerVoteType::Max; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 90_Hz; lr.vote = LayerVoteType::Heuristic; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 60_Hz; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 45_Hz; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 30_Hz; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 24_Hz; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {0_Hz, 120_Hz}}), 0); + EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {0_Hz, 120_Hz}}), 0); lr.vote = LayerVoteType::Min; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr.vote = LayerVoteType::Max; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 90_Hz; lr.vote = LayerVoteType::Heuristic; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 60_Hz; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 45_Hz; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 30_Hz; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 24_Hz; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); } TEST_F(RefreshRateConfigsTest, getBestRefreshRate_multipleThreshold_60_90) { - RefreshRateConfigs::Config config = {.frameRateMultipleThreshold = 90}; - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90Device, - /*currentConfigId=*/HWC_CONFIG_ID_60, config); + TestableRefreshRateConfigs configs(kModes_60_90, kModeId60, {.frameRateMultipleThreshold = 90}); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; lr.vote = LayerVoteType::Min; lr.name = "Min"; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr.vote = LayerVoteType::Max; lr.name = "Max"; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 90_Hz; lr.vote = LayerVoteType::Heuristic; lr.name = "90Hz Heuristic"; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 60_Hz; lr.name = "60Hz Heuristic"; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 45_Hz; lr.name = "45Hz Heuristic"; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 30_Hz; lr.name = "30Hz Heuristic"; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 24_Hz; lr.name = "24Hz Heuristic"; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); } TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_72_90) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_72_90Device, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_60_72_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; lr.vote = LayerVoteType::Min; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr.vote = LayerVoteType::Max; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 90_Hz; lr.vote = LayerVoteType::Heuristic; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 60_Hz; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 45_Hz; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 30_Hz; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 24_Hz; - EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers)); } TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90_120) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; auto& lr1 = layers[0]; @@ -540,25 +482,23 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90_120) { lr1.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 60_Hz; lr2.vote = LayerVoteType::Heuristic; - EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 48_Hz; lr2.vote = LayerVoteType::Heuristic; - EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 48_Hz; lr2.vote = LayerVoteType::Heuristic; - EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers)); } TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; auto& lr1 = layers[0]; @@ -570,7 +510,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) { lr2.desiredRefreshRate = 60_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.name = "60Hz Heuristic"; - EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -578,7 +518,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) { lr2.desiredRefreshRate = 60_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.name = "60Hz Heuristic"; - EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -586,7 +526,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) { lr2.desiredRefreshRate = 60_Hz; lr2.vote = LayerVoteType::ExplicitDefault; lr2.name = "60Hz ExplicitDefault"; - EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -594,7 +534,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) { lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.name = "90Hz Heuristic"; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -602,7 +542,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) { lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitDefault; lr2.name = "90Hz Heuristic"; - EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitDefault; @@ -610,7 +550,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) { lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.name = "90Hz Heuristic"; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::Heuristic; @@ -618,7 +558,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) { lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitDefault; lr2.name = "90Hz ExplicitDefault"; - EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -626,7 +566,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) { lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitDefault; lr2.name = "90Hz ExplicitDefault"; - EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitDefault; @@ -634,14 +574,12 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) { lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitExactOrMultiple; lr2.name = "90Hz ExplicitExactOrMultiple"; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); } TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_multipleThreshold) { - RefreshRateConfigs::Config config = {.frameRateMultipleThreshold = 120}; - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, - /*currentConfigId=*/HWC_CONFIG_ID_60, config); + TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60, + {.frameRateMultipleThreshold = 120}); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; auto& lr1 = layers[0]; @@ -653,7 +591,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_mu lr2.desiredRefreshRate = 60_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.name = "60Hz Heuristic"; - EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -661,7 +599,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_mu lr2.desiredRefreshRate = 60_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.name = "60Hz Heuristic"; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -669,7 +607,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_mu lr2.desiredRefreshRate = 60_Hz; lr2.vote = LayerVoteType::ExplicitDefault; lr2.name = "60Hz ExplicitDefault"; - EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -677,7 +615,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_mu lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.name = "90Hz Heuristic"; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -685,7 +623,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_mu lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitDefault; lr2.name = "90Hz Heuristic"; - EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitDefault; @@ -693,7 +631,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_mu lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.name = "90Hz Heuristic"; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::Heuristic; @@ -701,7 +639,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_mu lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitDefault; lr2.name = "90Hz ExplicitDefault"; - EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -709,7 +647,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_mu lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitDefault; lr2.name = "90Hz ExplicitDefault"; - EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitDefault; @@ -717,92 +655,86 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_mu lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitExactOrMultiple; lr2.name = "90Hz ExplicitExactOrMultiple"; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); } TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m30_60Device, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_30_60, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; lr.vote = LayerVoteType::Min; - EXPECT_EQ(mExpected30Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode30), configs.getBestRefreshRate(layers)); lr.vote = LayerVoteType::Max; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 90_Hz; lr.vote = LayerVoteType::Heuristic; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 60_Hz; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 45_Hz; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 30_Hz; - EXPECT_EQ(mExpected30Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode30), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 24_Hz; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); } TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m30_60_72_90Device, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_30_60_72_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; lr.vote = LayerVoteType::Min; lr.name = "Min"; - EXPECT_EQ(mExpected30Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode30), configs.getBestRefreshRate(layers)); lr.vote = LayerVoteType::Max; lr.name = "Max"; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 90_Hz; lr.vote = LayerVoteType::Heuristic; lr.name = "90Hz Heuristic"; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.desiredRefreshRate = 60_Hz; lr.name = "60Hz Heuristic"; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers, {.touch = true})); lr.desiredRefreshRate = 45_Hz; lr.name = "45Hz Heuristic"; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers, {.touch = true})); lr.desiredRefreshRate = 30_Hz; lr.name = "30Hz Heuristic"; - EXPECT_EQ(mExpected30Config, refreshRateConfigs->getBestRefreshRate(layers, {})); - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(asRefreshRate(kMode30), configs.getBestRefreshRate(layers)); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers, {.touch = true})); lr.desiredRefreshRate = 24_Hz; lr.name = "24Hz Heuristic"; - EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {})); - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers)); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers, {.touch = true})); lr.desiredRefreshRate = 24_Hz; lr.vote = LayerVoteType::ExplicitExactOrMultiple; lr.name = "24Hz ExplicitExactOrMultiple"; - EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {})); - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers)); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers, {.touch = true})); } TEST_F(RefreshRateConfigsTest, getBestRefreshRate_PriorityTest) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m30_60_90Device, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; auto& lr1 = layers[0]; @@ -810,45 +742,43 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_PriorityTest) { lr1.vote = LayerVoteType::Min; lr2.vote = LayerVoteType::Max; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr1.vote = LayerVoteType::Min; lr2.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 24_Hz; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr1.vote = LayerVoteType::Min; lr2.vote = LayerVoteType::ExplicitExactOrMultiple; lr2.desiredRefreshRate = 24_Hz; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr1.vote = LayerVoteType::Max; lr2.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 60_Hz; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr1.vote = LayerVoteType::Max; lr2.vote = LayerVoteType::ExplicitExactOrMultiple; lr2.desiredRefreshRate = 60_Hz; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr1.vote = LayerVoteType::Heuristic; lr1.desiredRefreshRate = 15_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 45_Hz; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr1.vote = LayerVoteType::Heuristic; lr1.desiredRefreshRate = 30_Hz; lr2.vote = LayerVoteType::ExplicitExactOrMultiple; lr2.desiredRefreshRate = 45_Hz; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); } TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90Device, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; @@ -856,17 +786,15 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo) { lr.vote = LayerVoteType::ExplicitExactOrMultiple; for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) { lr.desiredRefreshRate = Fps::fromValue(fps); - const auto refreshRate = refreshRateConfigs->getBestRefreshRate(layers, {}); - EXPECT_EQ(mExpected60Config, refreshRate) + const auto refreshRate = configs.getBestRefreshRate(layers); + EXPECT_EQ(asRefreshRate(kMode60), refreshRate) << lr.desiredRefreshRate << " chooses " << refreshRate.getName(); } } TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo_multipleThreshold_60_120) { - RefreshRateConfigs::Config config = {.frameRateMultipleThreshold = 120}; - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_120Device, - /*currentConfigId=*/HWC_CONFIG_ID_60, config); + TestableRefreshRateConfigs configs(kModes_60_120, kModeId60, + {.frameRateMultipleThreshold = 120}); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; @@ -874,16 +802,14 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo_multipleThreshold_6 lr.vote = LayerVoteType::ExplicitExactOrMultiple; for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) { lr.desiredRefreshRate = Fps::fromValue(fps); - const auto refreshRate = refreshRateConfigs->getBestRefreshRate(layers, {}); - EXPECT_EQ(mExpected60Config, refreshRate) + const auto refreshRate = configs.getBestRefreshRate(layers); + EXPECT_EQ(asRefreshRate(kMode60), refreshRate) << lr.desiredRefreshRate << " chooses " << refreshRate.getName(); } } -TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getBestRefreshRate_Explicit) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90Device, - /*currentConfigId=*/HWC_CONFIG_ID_60); +TEST_F(RefreshRateConfigsTest, twoModes_getBestRefreshRate_Explicit) { + TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; auto& lr1 = layers[0]; @@ -893,33 +819,34 @@ TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getBestRefreshRate_Explicit) { lr1.desiredRefreshRate = 60_Hz; lr2.vote = LayerVoteType::ExplicitExactOrMultiple; lr2.desiredRefreshRate = 90_Hz; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr1.vote = LayerVoteType::ExplicitDefault; lr1.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitExactOrMultiple; lr2.desiredRefreshRate = 60_Hz; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr1.vote = LayerVoteType::Heuristic; lr1.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitExactOrMultiple; lr2.desiredRefreshRate = 60_Hz; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); } TEST_F(RefreshRateConfigsTest, testInPolicy) { - ASSERT_TRUE(mExpectedAlmost60Config.inPolicy(60.000004_Hz, 60.000004_Hz)); - ASSERT_TRUE(mExpectedAlmost60Config.inPolicy(59_Hz, 60.1_Hz)); - ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(75_Hz, 90_Hz)); - ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(60.0011_Hz, 90_Hz)); - ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(50_Hz, 59.998_Hz)); + const auto refreshRate = + asRefreshRate(createDisplayMode(kModeId60, Fps::fromPeriodNsecs(16'666'665))); + + EXPECT_TRUE(refreshRate.inPolicy(60.000004_Hz, 60.000004_Hz)); + EXPECT_TRUE(refreshRate.inPolicy(59_Hz, 60.1_Hz)); + EXPECT_FALSE(refreshRate.inPolicy(75_Hz, 90_Hz)); + EXPECT_FALSE(refreshRate.inPolicy(60.0011_Hz, 90_Hz)); + EXPECT_FALSE(refreshRate.inPolicy(50_Hz, 59.998_Hz)); } TEST_F(RefreshRateConfigsTest, getBestRefreshRate_75HzContent) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90Device, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; @@ -927,16 +854,14 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_75HzContent) { lr.vote = LayerVoteType::ExplicitExactOrMultiple; for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) { lr.desiredRefreshRate = Fps::fromValue(fps); - const auto refreshRate = refreshRateConfigs->getBestRefreshRate(layers, {}); - EXPECT_EQ(mExpected90Config, refreshRate) + const auto refreshRate = configs.getBestRefreshRate(layers, {}); + EXPECT_EQ(asRefreshRate(kMode90), refreshRate) << lr.desiredRefreshRate << " chooses " << refreshRate.getName(); } } TEST_F(RefreshRateConfigsTest, getBestRefreshRate_Multiples) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90Device, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; auto& lr1 = layers[0]; @@ -948,7 +873,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_Multiples) { lr2.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 90_Hz; lr2.name = "90Hz Heuristic"; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr1.vote = LayerVoteType::ExplicitExactOrMultiple; lr1.desiredRefreshRate = 60_Hz; @@ -956,14 +881,14 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_Multiples) { lr2.vote = LayerVoteType::ExplicitDefault; lr2.desiredRefreshRate = 90_Hz; lr2.name = "90Hz ExplicitDefault"; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr1.vote = LayerVoteType::ExplicitExactOrMultiple; lr1.desiredRefreshRate = 60_Hz; lr1.name = "60Hz ExplicitExactOrMultiple"; lr2.vote = LayerVoteType::Max; lr2.name = "Max"; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr1.vote = LayerVoteType::ExplicitExactOrMultiple; lr1.desiredRefreshRate = 30_Hz; @@ -971,20 +896,18 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_Multiples) { lr2.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 90_Hz; lr2.name = "90Hz Heuristic"; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr1.vote = LayerVoteType::ExplicitExactOrMultiple; lr1.desiredRefreshRate = 30_Hz; lr1.name = "30Hz ExplicitExactOrMultiple"; lr2.vote = LayerVoteType::Max; lr2.name = "Max"; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); } TEST_F(RefreshRateConfigsTest, scrollWhileWatching60fps_60_90) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90Device, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; auto& lr1 = layers[0]; @@ -995,28 +918,28 @@ TEST_F(RefreshRateConfigsTest, scrollWhileWatching60fps_60_90) { lr1.name = "60Hz ExplicitExactOrMultiple"; lr2.vote = LayerVoteType::NoVote; lr2.name = "NoVote"; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr1.vote = LayerVoteType::ExplicitExactOrMultiple; lr1.desiredRefreshRate = 60_Hz; lr1.name = "60Hz ExplicitExactOrMultiple"; lr2.vote = LayerVoteType::NoVote; lr2.name = "NoVote"; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers, {.touch = true})); lr1.vote = LayerVoteType::ExplicitExactOrMultiple; lr1.desiredRefreshRate = 60_Hz; lr1.name = "60Hz ExplicitExactOrMultiple"; lr2.vote = LayerVoteType::Max; lr2.name = "Max"; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers, {.touch = true})); lr1.vote = LayerVoteType::ExplicitExactOrMultiple; lr1.desiredRefreshRate = 60_Hz; lr1.name = "60Hz ExplicitExactOrMultiple"; lr2.vote = LayerVoteType::Max; lr2.name = "Max"; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); // The other layer starts to provide buffers lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -1025,20 +948,17 @@ TEST_F(RefreshRateConfigsTest, scrollWhileWatching60fps_60_90) { lr2.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 90_Hz; lr2.name = "90Hz Heuristic"; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); } TEST_F(RefreshRateConfigsTest, touchConsidered) { - RefreshRateConfigs::GlobalSignals consideredSignals; - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90Device, - /*currentConfigId=*/HWC_CONFIG_ID_60); + RefreshRateConfigs configs(kModes_60_90, kModeId60); - refreshRateConfigs->getBestRefreshRate({}, {}, &consideredSignals); - EXPECT_EQ(false, consideredSignals.touch); + auto [_, signals] = configs.getBestRefreshRate({}, {}); + EXPECT_FALSE(signals.touch); - refreshRateConfigs->getBestRefreshRate({}, {.touch = true}, &consideredSignals); - EXPECT_EQ(true, consideredSignals.touch); + std::tie(std::ignore, signals) = configs.getBestRefreshRate({}, {.touch = true}); + EXPECT_TRUE(signals.touch); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; auto& lr1 = layers[0]; @@ -1050,8 +970,8 @@ TEST_F(RefreshRateConfigsTest, touchConsidered) { lr2.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 60_Hz; lr2.name = "60Hz Heuristic"; - refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}, &consideredSignals); - EXPECT_EQ(true, consideredSignals.touch); + std::tie(std::ignore, signals) = configs.getBestRefreshRate(layers, {.touch = true}); + EXPECT_TRUE(signals.touch); lr1.vote = LayerVoteType::ExplicitDefault; lr1.desiredRefreshRate = 60_Hz; @@ -1059,8 +979,8 @@ TEST_F(RefreshRateConfigsTest, touchConsidered) { lr2.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 60_Hz; lr2.name = "60Hz Heuristic"; - refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}, &consideredSignals); - EXPECT_EQ(false, consideredSignals.touch); + std::tie(std::ignore, signals) = configs.getBestRefreshRate(layers, {.touch = true}); + EXPECT_FALSE(signals.touch); lr1.vote = LayerVoteType::ExplicitExactOrMultiple; lr1.desiredRefreshRate = 60_Hz; @@ -1068,8 +988,8 @@ TEST_F(RefreshRateConfigsTest, touchConsidered) { lr2.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 60_Hz; lr2.name = "60Hz Heuristic"; - refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}, &consideredSignals); - EXPECT_EQ(true, consideredSignals.touch); + std::tie(std::ignore, signals) = configs.getBestRefreshRate(layers, {.touch = true}); + EXPECT_TRUE(signals.touch); lr1.vote = LayerVoteType::ExplicitDefault; lr1.desiredRefreshRate = 60_Hz; @@ -1077,14 +997,12 @@ TEST_F(RefreshRateConfigsTest, touchConsidered) { lr2.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 60_Hz; lr2.name = "60Hz Heuristic"; - refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}, &consideredSignals); - EXPECT_EQ(false, consideredSignals.touch); + std::tie(std::ignore, signals) = configs.getBestRefreshRate(layers, {.touch = true}); + EXPECT_FALSE(signals.touch); } TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitDefault) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90_72_120Device, /*currentConfigId=*/ - HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_60_90_72_120, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; @@ -1116,7 +1034,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitDefault) { ss << "ExplicitDefault " << desired; lr.name = ss.str(); - const auto refreshRate = refreshRateConfigs->getBestRefreshRate(layers, {}); + const auto refreshRate = configs.getBestRefreshRate(layers); EXPECT_EQ(refreshRate.getFps(), expected); } } @@ -1128,39 +1046,36 @@ TEST_F(RefreshRateConfigsTest, // Test that 23.976 will choose 24 if 23.976 is not supported { - android::DisplayModes modes = {mConfig24, mConfig25, mConfig30, - mConfig30Frac, mConfig60, mConfig60Frac}; - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs({kMode24, kMode25, kMode30, kMode30Frac, kMode60, + kMode60Frac}, + kModeId60); lr.vote = LayerVoteType::ExplicitExactOrMultiple; lr.desiredRefreshRate = 23.976_Hz; lr.name = "ExplicitExactOrMultiple 23.976 Hz"; - EXPECT_EQ(HWC_CONFIG_ID_24, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId()); + EXPECT_EQ(kModeId24, configs.getBestRefreshRate(layers).getModeId()); } // Test that 24 will choose 23.976 if 24 is not supported { - android::DisplayModes modes = {mConfig24Frac, mConfig25, mConfig30, - mConfig30Frac, mConfig60, mConfig60Frac}; - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs({kMode24Frac, kMode25, kMode30, kMode30Frac, kMode60, + kMode60Frac}, + kModeId60); + lr.desiredRefreshRate = 24_Hz; lr.name = "ExplicitExactOrMultiple 24 Hz"; - EXPECT_EQ(HWC_CONFIG_ID_24_FRAC, - refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId()); + EXPECT_EQ(kModeId24Frac, configs.getBestRefreshRate(layers).getModeId()); } // Test that 29.97 will prefer 59.94 over 60 and 30 { - android::DisplayModes modes = {mConfig24, mConfig24Frac, mConfig25, - mConfig30, mConfig60, mConfig60Frac}; - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs({kMode24, kMode24Frac, kMode25, kMode30, kMode60, + kMode60Frac}, + kModeId60); + lr.desiredRefreshRate = 29.97_Hz; lr.name = "ExplicitExactOrMultiple 29.97 Hz"; - EXPECT_EQ(HWC_CONFIG_ID_60_FRAC, - refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId()); + EXPECT_EQ(kModeId60Frac, configs.getBestRefreshRate(layers).getModeId()); } } @@ -1170,9 +1085,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExact_WithFractionalRe // Test that voting for supported refresh rate will select this refresh rate { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m24_25_30_50_60WithFracDevice, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_24_25_30_50_60_Frac, kModeId60); for (auto desired : {23.976_Hz, 24_Hz, 25_Hz, 29.97_Hz, 30_Hz, 50_Hz, 59.94_Hz, 60_Hz}) { lr.vote = LayerVoteType::ExplicitExact; @@ -1181,69 +1094,61 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExact_WithFractionalRe ss << "ExplicitExact " << desired; lr.name = ss.str(); - auto selectedRefreshRate = refreshRateConfigs->getBestRefreshRate(layers, {}); + auto selectedRefreshRate = configs.getBestRefreshRate(layers); EXPECT_EQ(selectedRefreshRate.getFps(), lr.desiredRefreshRate); } } // Test that 23.976 will choose 24 if 23.976 is not supported { - android::DisplayModes modes = {mConfig24, mConfig25, mConfig30, - mConfig30Frac, mConfig60, mConfig60Frac}; - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs({kMode24, kMode25, kMode30, kMode30Frac, kMode60, + kMode60Frac}, + kModeId60); + lr.vote = LayerVoteType::ExplicitExact; lr.desiredRefreshRate = 23.976_Hz; lr.name = "ExplicitExact 23.976 Hz"; - EXPECT_EQ(HWC_CONFIG_ID_24, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId()); + EXPECT_EQ(kModeId24, configs.getBestRefreshRate(layers).getModeId()); } // Test that 24 will choose 23.976 if 24 is not supported { - android::DisplayModes modes = {mConfig24Frac, mConfig25, mConfig30, - mConfig30Frac, mConfig60, mConfig60Frac}; - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs({kMode24Frac, kMode25, kMode30, kMode30Frac, kMode60, + kMode60Frac}, + kModeId60); + lr.desiredRefreshRate = 24_Hz; lr.name = "ExplicitExact 24 Hz"; - EXPECT_EQ(HWC_CONFIG_ID_24_FRAC, - refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId()); + EXPECT_EQ(kModeId24Frac, configs.getBestRefreshRate(layers).getModeId()); } } TEST_F(RefreshRateConfigsTest, getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresTouchFlag) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90Device, - /*currentConfigId=*/HWC_CONFIG_ID_90); + RefreshRateConfigs configs(kModes_60_90, kModeId90); - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy( - {HWC_CONFIG_ID_90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}), - 0); + EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}), 0); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; - RefreshRateConfigs::GlobalSignals consideredSignals; lr.vote = LayerVoteType::ExplicitDefault; lr.desiredRefreshRate = 60_Hz; lr.name = "60Hz ExplicitDefault"; lr.focused = true; - EXPECT_EQ(mExpected60Config, - refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = true}, - &consideredSignals)); - EXPECT_EQ(false, consideredSignals.touch); + + const auto [refreshRate, signals] = + configs.getBestRefreshRate(layers, {.touch = true, .idle = true}); + + EXPECT_EQ(refreshRate, asRefreshRate(kMode60)); + EXPECT_FALSE(signals.touch); } TEST_F(RefreshRateConfigsTest, getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresIdleFlag) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90Device, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy( - {HWC_CONFIG_ID_60, {60_Hz, 60_Hz}, {60_Hz, 90_Hz}}), - 0); + EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}, {60_Hz, 90_Hz}}), 0); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; @@ -1252,23 +1157,18 @@ TEST_F(RefreshRateConfigsTest, lr.desiredRefreshRate = 90_Hz; lr.name = "90Hz ExplicitDefault"; lr.focused = true; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.idle = true})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers, {.idle = true})); } TEST_F(RefreshRateConfigsTest, getBestRefreshRate_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitFocusedLayers) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90Device, - /*currentConfigId=*/HWC_CONFIG_ID_90); + TestableRefreshRateConfigs configs(kModes_60_90, kModeId90); - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy( - {HWC_CONFIG_ID_90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}), - 0); + EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}), 0); - RefreshRateConfigs::GlobalSignals consideredSignals; - EXPECT_EQ(mExpected90Config, - refreshRateConfigs->getBestRefreshRate({}, {}, &consideredSignals)); - EXPECT_EQ(false, consideredSignals.touch); + const auto [refreshRate, signals] = configs.getBestRefreshRateAndSignals({}, {}); + EXPECT_EQ(refreshRate, asRefreshRate(kMode90)); + EXPECT_FALSE(signals.touch); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; @@ -1277,52 +1177,50 @@ TEST_F(RefreshRateConfigsTest, lr.desiredRefreshRate = 60_Hz; lr.name = "60Hz ExplicitExactOrMultiple"; lr.focused = false; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.focused = true; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.vote = LayerVoteType::ExplicitDefault; lr.desiredRefreshRate = 60_Hz; lr.name = "60Hz ExplicitDefault"; lr.focused = false; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.focused = true; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); lr.vote = LayerVoteType::Heuristic; lr.desiredRefreshRate = 60_Hz; lr.name = "60Hz Heuristic"; lr.focused = false; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.focused = true; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.vote = LayerVoteType::Max; lr.desiredRefreshRate = 60_Hz; lr.name = "60Hz Max"; lr.focused = false; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.focused = true; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.vote = LayerVoteType::Min; lr.desiredRefreshRate = 60_Hz; lr.name = "60Hz Min"; lr.focused = false; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); lr.focused = true; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); } TEST_F(RefreshRateConfigsTest, groupSwitchingNotAllowed) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); // The default policy doesn't allow group switching. Verify that no // group switches are performed. @@ -1334,17 +1232,16 @@ TEST_F(RefreshRateConfigsTest, groupSwitchingNotAllowed) { layer.name = "90Hz ExplicitDefault"; layer.focused = true; - ASSERT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId()); + EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers).getModeId()); } TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayer) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); + RefreshRateConfigs::Policy policy; - policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode; + policy.defaultMode = configs.getCurrentPolicy().defaultMode; policy.allowGroupSwitching = true; - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0); + EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& layer = layers[0]; @@ -1353,17 +1250,16 @@ TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayer) { layer.seamlessness = Seamlessness::SeamedAndSeamless; layer.name = "90Hz ExplicitDefault"; layer.focused = true; - ASSERT_EQ(HWC_CONFIG_ID_90, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId()); + EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers).getModeId()); } TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamless) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); + RefreshRateConfigs::Policy policy; - policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode; + policy.defaultMode = configs.getCurrentPolicy().defaultMode; policy.allowGroupSwitching = true; - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0); + EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0); // Verify that we won't change the group if seamless switch is required. std::vector<LayerRequirement> layers = {{.weight = 1.f}}; @@ -1373,19 +1269,18 @@ TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamless) { layer.seamlessness = Seamlessness::OnlySeamless; layer.name = "90Hz ExplicitDefault"; layer.focused = true; - ASSERT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId()); + EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers).getModeId()); } TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); + RefreshRateConfigs::Policy policy; - policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode; + policy.defaultMode = configs.getCurrentPolicy().defaultMode; policy.allowGroupSwitching = true; - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0); + EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0); - refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90); + configs.setCurrentModeId(kModeId90); // Verify that we won't do a seamless switch if we request the same mode as the default std::vector<LayerRequirement> layers = {{.weight = 1.f}}; @@ -1395,19 +1290,18 @@ TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) layer.seamlessness = Seamlessness::OnlySeamless; layer.name = "60Hz ExplicitDefault"; layer.focused = true; - ASSERT_EQ(HWC_CONFIG_ID_90, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId()); + EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers).getModeId()); } TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerDefaultSeamlessness) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); + RefreshRateConfigs::Policy policy; - policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode; + policy.defaultMode = configs.getCurrentPolicy().defaultMode; policy.allowGroupSwitching = true; - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0); + EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0); - refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90); + configs.setCurrentModeId(kModeId90); // Verify that if the current config is in another group and there are no layers with // seamlessness=SeamedAndSeamless we'll go back to the default group. @@ -1420,19 +1314,18 @@ TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerDefaultSeamlessness) { layer.name = "60Hz ExplicitDefault"; layer.focused = true; - ASSERT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId()); + EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers).getModeId()); } TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); + RefreshRateConfigs::Policy policy; - policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode; + policy.defaultMode = configs.getCurrentPolicy().defaultMode; policy.allowGroupSwitching = true; - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0); + EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0); - refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90); + configs.setCurrentModeId(kModeId90); // If there's a layer with seamlessness=SeamedAndSeamless, another layer with // seamlessness=OnlySeamless can't change the mode group. @@ -1450,19 +1343,18 @@ TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) layers[1].name = "90Hz ExplicitDefault"; layers[1].focused = false; - ASSERT_EQ(HWC_CONFIG_ID_90, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId()); + EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers).getModeId()); } TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); + RefreshRateConfigs::Policy policy; - policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode; + policy.defaultMode = configs.getCurrentPolicy().defaultMode; policy.allowGroupSwitching = true; - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0); + EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0); - refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90); + configs.setCurrentModeId(kModeId90); // If there's a focused layer with seamlessness=SeamedAndSeamless, another layer with // seamlessness=Default can't change the mode group back to the group of the default @@ -1484,19 +1376,18 @@ TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeame layers[1].vote = LayerVoteType::ExplicitDefault; layers[1].name = "90Hz ExplicitDefault"; - ASSERT_EQ(HWC_CONFIG_ID_90, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId()); + EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers).getModeId()); } TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); + RefreshRateConfigs::Policy policy; - policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode; + policy.defaultMode = configs.getCurrentPolicy().defaultMode; policy.allowGroupSwitching = true; - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0); + EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0); - refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90); + configs.setCurrentModeId(kModeId90); // Layer with seamlessness=Default can change the mode group if there's a not // focused layer with seamlessness=SeamedAndSeamless. This happens for example, @@ -1515,19 +1406,17 @@ TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSe layers[1].vote = LayerVoteType::ExplicitDefault; layers[1].name = "90Hz ExplicitDefault"; - ASSERT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId()); + EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers).getModeId()); } TEST_F(RefreshRateConfigsTest, nonSeamlessVotePrefersSeamlessSwitches) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m30_60Device, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_30_60, kModeId60); // Allow group switching. RefreshRateConfigs::Policy policy; - policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode; + policy.defaultMode = configs.getCurrentPolicy().defaultMode; policy.allowGroupSwitching = true; - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0); + EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& layer = layers[0]; @@ -1537,22 +1426,20 @@ TEST_F(RefreshRateConfigsTest, nonSeamlessVotePrefersSeamlessSwitches) { layer.name = "60Hz ExplicitExactOrMultiple"; layer.focused = true; - ASSERT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId()); + EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers).getModeId()); - refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_120); - ASSERT_EQ(HWC_CONFIG_ID_120, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId()); + configs.setCurrentModeId(kModeId120); + EXPECT_EQ(kModeId120, configs.getBestRefreshRate(layers).getModeId()); } TEST_F(RefreshRateConfigsTest, nonSeamlessExactAndSeamlessMultipleLayers) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m25_30_50_60Device, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_25_30_50_60, kModeId60); // Allow group switching. RefreshRateConfigs::Policy policy; - policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode; + policy.defaultMode = configs.getCurrentPolicy().defaultMode; policy.allowGroupSwitching = true; - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0); + EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0); std::vector<LayerRequirement> layers = {{.name = "60Hz ExplicitDefault", .vote = LayerVoteType::ExplicitDefault, @@ -1567,37 +1454,33 @@ TEST_F(RefreshRateConfigsTest, nonSeamlessExactAndSeamlessMultipleLayers) { .weight = 1.f, .focused = true}}; - ASSERT_EQ(HWC_CONFIG_ID_50, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId()); + EXPECT_EQ(kModeId50, configs.getBestRefreshRate(layers).getModeId()); auto& seamedLayer = layers[0]; seamedLayer.desiredRefreshRate = 30_Hz; seamedLayer.name = "30Hz ExplicitDefault"; - refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_30); + configs.setCurrentModeId(kModeId30); - ASSERT_EQ(HWC_CONFIG_ID_25, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId()); + EXPECT_EQ(kModeId25, configs.getBestRefreshRate(layers).getModeId()); } TEST_F(RefreshRateConfigsTest, minLayersDontTrigerSeamedSwitch) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups, - /*currentConfigId=*/HWC_CONFIG_ID_90); + TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId90); // Allow group switching. RefreshRateConfigs::Policy policy; - policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode; + policy.defaultMode = configs.getCurrentPolicy().defaultMode; policy.allowGroupSwitching = true; - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0); + EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0); std::vector<LayerRequirement> layers = { {.name = "Min", .vote = LayerVoteType::Min, .weight = 1.f, .focused = true}}; - ASSERT_EQ(HWC_CONFIG_ID_90, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId()); + EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers).getModeId()); } TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m30_60_90Device, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; layers[0].name = "Test layer"; @@ -1613,50 +1496,45 @@ TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) { layers[0].vote = voteType; layers[0].desiredRefreshRate = fps; layers[0].focused = args.focused; - return refreshRateConfigs->getBestRefreshRate(layers, {.touch = args.touch}).getModeId(); + return configs.getBestRefreshRate(layers, {.touch = args.touch}).getModeId(); }; - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy( - {HWC_CONFIG_ID_60, {30_Hz, 60_Hz}, {30_Hz, 90_Hz}}), - 0); - EXPECT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate({}, {}).getModeId()); - EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::NoVote, 90_Hz)); - EXPECT_EQ(HWC_CONFIG_ID_30, getFrameRate(LayerVoteType::Min, 90_Hz)); - EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, 90_Hz)); - EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Heuristic, 90_Hz)); - EXPECT_EQ(HWC_CONFIG_ID_90, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz)); - EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz)); + EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 60_Hz}, {30_Hz, 90_Hz}}), 0); + + EXPECT_EQ(kModeId60, configs.getBestRefreshRate().getModeId()); + EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz)); + EXPECT_EQ(kModeId30, getFrameRate(LayerVoteType::Min, 90_Hz)); + EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz)); + EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Heuristic, 90_Hz)); + EXPECT_EQ(kModeId90, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz)); + EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz)); // Unfocused layers are not allowed to override primary config. - EXPECT_EQ(HWC_CONFIG_ID_60, - getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz, {.focused = false})); - EXPECT_EQ(HWC_CONFIG_ID_60, + EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz, {.focused = false})); + EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz, {.focused = false})); // Touch boost should be restricted to the primary range. - EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, 90_Hz, {.touch = true})); + EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz, {.touch = true})); + // When we're higher than the primary range max due to a layer frame rate setting, touch boost // shouldn't drag us back down to the primary range max. - EXPECT_EQ(HWC_CONFIG_ID_90, - getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz, {.touch = true})); - EXPECT_EQ(HWC_CONFIG_ID_60, + EXPECT_EQ(kModeId90, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz, {.touch = true})); + EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz, {.touch = true})); - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy( - {HWC_CONFIG_ID_60, {60_Hz, 60_Hz}, {60_Hz, 60_Hz}}), - 0); - EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::NoVote, 90_Hz)); - EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Min, 90_Hz)); - EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, 90_Hz)); - EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Heuristic, 90_Hz)); - EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz)); - EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz)); + EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}, {60_Hz, 60_Hz}}), 0); + + EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz)); + EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Min, 90_Hz)); + EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz)); + EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Heuristic, 90_Hz)); + EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz)); + EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz)); } TEST_F(RefreshRateConfigsTest, idle) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90Device, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; layers[0].name = "Test layer"; @@ -1664,82 +1542,70 @@ TEST_F(RefreshRateConfigsTest, idle) { const auto getIdleFrameRate = [&](LayerVoteType voteType, bool touchActive) -> DisplayModeId { layers[0].vote = voteType; layers[0].desiredRefreshRate = 90_Hz; - RefreshRateConfigs::GlobalSignals consideredSignals; - const auto configId = - refreshRateConfigs - ->getBestRefreshRate(layers, {.touch = touchActive, .idle = true}, - &consideredSignals) - .getModeId(); - // Refresh rate will be chosen by either touch state or idle state - EXPECT_EQ(!touchActive, consideredSignals.idle); - return configId; + + const auto [refreshRate, signals] = + configs.getBestRefreshRateAndSignals(layers, {.touch = touchActive, .idle = true}); + + // Refresh rate will be chosen by either touch state or idle state. + EXPECT_EQ(!touchActive, signals.idle); + return refreshRate.getModeId(); }; - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy( - {HWC_CONFIG_ID_60, {60_Hz, 90_Hz}, {60_Hz, 90_Hz}}), - 0); + EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}, {60_Hz, 90_Hz}}), 0); // Idle should be lower priority than touch boost. - EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::NoVote, /*touchActive=*/true)); - EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Min, /*touchActive=*/true)); - EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Max, /*touchActive=*/true)); - EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Heuristic, /*touchActive=*/true)); - EXPECT_EQ(HWC_CONFIG_ID_90, - getIdleFrameRate(LayerVoteType::ExplicitDefault, /*touchActive=*/true)); - EXPECT_EQ(HWC_CONFIG_ID_90, - getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, /*touchActive=*/true)); + { + constexpr bool kTouchActive = true; + EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::NoVote, kTouchActive)); + EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::Min, kTouchActive)); + EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::Max, kTouchActive)); + EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::Heuristic, kTouchActive)); + EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::ExplicitDefault, kTouchActive)); + EXPECT_EQ(kModeId90, + getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, kTouchActive)); + } // With no layers, idle should still be lower priority than touch boost. - EXPECT_EQ(HWC_CONFIG_ID_90, - refreshRateConfigs->getBestRefreshRate({}, {.touch = true, .idle = true}) - .getModeId()); + EXPECT_EQ(kModeId90, configs.getBestRefreshRate({}, {.touch = true, .idle = true}).getModeId()); // Idle should be higher precedence than other layer frame rate considerations. - refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90); - EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::NoVote, /*touchActive=*/false)); - EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Min, /*touchActive=*/false)); - EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Max, /*touchActive=*/false)); - EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Heuristic, /*touchActive=*/false)); - EXPECT_EQ(HWC_CONFIG_ID_60, - getIdleFrameRate(LayerVoteType::ExplicitDefault, /*touchActive=*/false)); - EXPECT_EQ(HWC_CONFIG_ID_60, - getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, /*touchActive=*/false)); + configs.setCurrentModeId(kModeId90); + + { + constexpr bool kTouchActive = false; + EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::NoVote, kTouchActive)); + EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::Min, kTouchActive)); + EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::Max, kTouchActive)); + EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::Heuristic, kTouchActive)); + EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::ExplicitDefault, kTouchActive)); + EXPECT_EQ(kModeId60, + getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, kTouchActive)); + } // Idle should be applied rather than the current config when there are no layers. - EXPECT_EQ(HWC_CONFIG_ID_60, - refreshRateConfigs->getBestRefreshRate({}, {.idle = true}).getModeId()); + EXPECT_EQ(kModeId60, configs.getBestRefreshRate({}, {.idle = true}).getModeId()); } TEST_F(RefreshRateConfigsTest, findClosestKnownFrameRate) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90Device, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); for (float fps = 1.0f; fps <= 120.0f; fps += 0.1f) { - const auto knownFrameRate = - findClosestKnownFrameRate(*refreshRateConfigs, Fps::fromValue(fps)); - Fps expectedFrameRate; - if (fps < 26.91f) { - expectedFrameRate = 24_Hz; - } else if (fps < 37.51f) { - expectedFrameRate = 30_Hz; - } else if (fps < 52.51f) { - expectedFrameRate = 45_Hz; - } else if (fps < 66.01f) { - expectedFrameRate = 60_Hz; - } else if (fps < 81.01f) { - expectedFrameRate = 72_Hz; - } else { - expectedFrameRate = 90_Hz; - } + const auto knownFrameRate = configs.findClosestKnownFrameRate(Fps::fromValue(fps)); + const Fps expectedFrameRate = [fps] { + if (fps < 26.91f) return 24_Hz; + if (fps < 37.51f) return 30_Hz; + if (fps < 52.51f) return 45_Hz; + if (fps < 66.01f) return 60_Hz; + if (fps < 81.01f) return 72_Hz; + return 90_Hz; + }(); + EXPECT_EQ(expectedFrameRate, knownFrameRate); } } TEST_F(RefreshRateConfigsTest, getBestRefreshRate_KnownFrameRate) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90Device, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); struct Expectation { Fps fps; @@ -1747,13 +1613,14 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_KnownFrameRate) { }; const std::initializer_list<Expectation> knownFrameRatesExpectations = { - {24_Hz, mExpected60Config}, {30_Hz, mExpected60Config}, {45_Hz, mExpected90Config}, - {60_Hz, mExpected60Config}, {72_Hz, mExpected90Config}, {90_Hz, mExpected90Config}, + {24_Hz, asRefreshRate(kMode60)}, {30_Hz, asRefreshRate(kMode60)}, + {45_Hz, asRefreshRate(kMode90)}, {60_Hz, asRefreshRate(kMode60)}, + {72_Hz, asRefreshRate(kMode90)}, {90_Hz, asRefreshRate(kMode90)}, }; // Make sure the test tests all the known frame rate - const auto knownFrameRateList = getKnownFrameRate(*refreshRateConfigs); - const bool equal = std::equal(knownFrameRateList.begin(), knownFrameRateList.end(), + const auto& knownFrameRates = configs.knownFrameRates(); + const bool equal = std::equal(knownFrameRates.begin(), knownFrameRates.end(), knownFrameRatesExpectations.begin(), [](Fps fps, const Expectation& expected) { return isApproxEqual(fps, expected.fps); @@ -1766,14 +1633,12 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_KnownFrameRate) { for (const auto& [fps, refreshRate] : knownFrameRatesExpectations) { layer.desiredRefreshRate = fps; - EXPECT_EQ(refreshRate, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(refreshRate, configs.getBestRefreshRate(layers)); } } TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExact) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, - /*currentConfigId=*/HWC_CONFIG_ID_60); + TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}}; auto& explicitExactLayer = layers[0]; @@ -1787,28 +1652,26 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExact) { explicitExactLayer.name = "ExplicitExact"; explicitExactLayer.desiredRefreshRate = 30_Hz; - EXPECT_EQ(mExpected30Config, refreshRateConfigs->getBestRefreshRate(layers, {})); - EXPECT_EQ(mExpected30Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(asRefreshRate(kMode30), configs.getBestRefreshRate(layers)); + EXPECT_EQ(asRefreshRate(kMode30), configs.getBestRefreshRate(layers, {.touch = true})); explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz; explicitExactLayer.desiredRefreshRate = 60_Hz; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); explicitExactLayer.desiredRefreshRate = 72_Hz; - EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers)); explicitExactLayer.desiredRefreshRate = 90_Hz; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); explicitExactLayer.desiredRefreshRate = 120_Hz; - EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers)); } TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactEnableFrameRateOverride) { - RefreshRateConfigs::Config config = {.enableFrameRateOverride = true}; - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, - /*currentConfigId=*/HWC_CONFIG_ID_60, config); + TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60, + {.enableFrameRateOverride = true}); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}}; auto& explicitExactLayer = layers[0]; @@ -1822,92 +1685,55 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactEnableFrameRateOv explicitExactLayer.name = "ExplicitExact"; explicitExactLayer.desiredRefreshRate = 30_Hz; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); - EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); + EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers, {.touch = true})); explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz; explicitExactLayer.desiredRefreshRate = 60_Hz; - EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers)); explicitExactLayer.desiredRefreshRate = 72_Hz; - EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers)); explicitExactLayer.desiredRefreshRate = 90_Hz; - EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers)); explicitExactLayer.desiredRefreshRate = 120_Hz; - EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers)); } -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ReadsCached) { - using GlobalSignals = RefreshRateConfigs::GlobalSignals; - - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, - /*currentConfigId=*/HWC_CONFIG_ID_60); - - setLastBestRefreshRateInvocation(*refreshRateConfigs, - GetBestRefreshRateInvocation{.globalSignals = {.touch = true, - .idle = true}, - .outSignalsConsidered = - {.touch = true}, - .resultingBestRefreshRate = - createRefreshRate( - mConfig90)}); - - EXPECT_EQ(createRefreshRate(mConfig90), - refreshRateConfigs->getBestRefreshRate({}, {.touch = true, .idle = true})); +TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ReadsCache) { + TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60); - const GlobalSignals cachedSignalsConsidered{.touch = true}; - setLastBestRefreshRateInvocation(*refreshRateConfigs, - GetBestRefreshRateInvocation{.globalSignals = {.touch = true, - .idle = true}, - .outSignalsConsidered = - cachedSignalsConsidered, - .resultingBestRefreshRate = - createRefreshRate( - mConfig30)}); + using GlobalSignals = RefreshRateConfigs::GlobalSignals; + const auto args = std::make_pair(std::vector<LayerRequirement>{}, + GlobalSignals{.touch = true, .idle = true}); + const auto result = std::make_pair(asRefreshRate(kMode90), GlobalSignals{.touch = true}); - GlobalSignals signalsConsidered; - EXPECT_EQ(createRefreshRate(mConfig30), - refreshRateConfigs->getBestRefreshRate({}, {.touch = true, .idle = true}, - &signalsConsidered)); + configs.mutableGetBestRefreshRateCache() = {args, result}; - EXPECT_EQ(cachedSignalsConsidered, signalsConsidered); + EXPECT_EQ(result, configs.getBestRefreshRateAndSignals(args.first, args.second)); } TEST_F(RefreshRateConfigsTest, getBestRefreshRate_WritesCache) { - using GlobalSignals = RefreshRateConfigs::GlobalSignals; + TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60); - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, - /*currentConfigId=*/HWC_CONFIG_ID_60); - ASSERT_FALSE(getLastBestRefreshRateInvocation(*refreshRateConfigs).has_value()); + EXPECT_FALSE(configs.mutableGetBestRefreshRateCache()); - GlobalSignals globalSignals{.touch = true, .idle = true}; std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}}; - const auto lastResult = - refreshRateConfigs->getBestRefreshRate(layers, globalSignals, - /* outSignalsConsidered */ nullptr); + RefreshRateConfigs::GlobalSignals globalSignals{.touch = true, .idle = true}; - const auto lastInvocation = getLastBestRefreshRateInvocation(*refreshRateConfigs); + const auto result = configs.getBestRefreshRateAndSignals(layers, globalSignals); - ASSERT_TRUE(lastInvocation.has_value()); - ASSERT_EQ(layers, lastInvocation->layerRequirements); - ASSERT_EQ(globalSignals, lastInvocation->globalSignals); - ASSERT_EQ(lastResult, lastInvocation->resultingBestRefreshRate); + const auto& cache = configs.mutableGetBestRefreshRateCache(); + ASSERT_TRUE(cache); - // outSignalsConsidered needs to be populated even tho earlier we gave nullptr - // to getBestRefreshRate() - GlobalSignals detaultSignals; - ASSERT_FALSE(detaultSignals == lastInvocation->outSignalsConsidered); + EXPECT_EQ(cache->arguments, std::make_pair(layers, globalSignals)); + EXPECT_EQ(cache->result, result); } TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactTouchBoost) { - RefreshRateConfigs::Config config = {.enableFrameRateOverride = true}; - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_120Device, - /*currentConfigId=*/HWC_CONFIG_ID_60, config); + TestableRefreshRateConfigs configs(kModes_60_120, kModeId60, {.enableFrameRateOverride = true}); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}}; auto& explicitExactLayer = layers[0]; @@ -1921,20 +1747,18 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactTouchBoost) { explicitExactLayer.name = "ExplicitExact"; explicitExactLayer.desiredRefreshRate = 30_Hz; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); - EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); + EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers, {.touch = true})); explicitExactOrMultipleLayer.vote = LayerVoteType::NoVote; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers, {.touch = true})); } TEST_F(RefreshRateConfigsTest, getBestRefreshRate_FractionalRefreshRates_ExactAndDefault) { - RefreshRateConfigs::Config config = {.enableFrameRateOverride = true}; - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m24_25_30_50_60WithFracDevice, - /*currentConfigId=*/HWC_CONFIG_ID_60, config); + TestableRefreshRateConfigs configs(kModes_24_25_30_50_60_Frac, kModeId60, + {.enableFrameRateOverride = true}); std::vector<LayerRequirement> layers = {{.weight = 0.5f}, {.weight = 0.5f}}; auto& explicitDefaultLayer = layers[0]; @@ -1948,32 +1772,27 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_FractionalRefreshRates_ExactAn explicitDefaultLayer.name = "ExplicitDefault"; explicitDefaultLayer.desiredRefreshRate = 59.94_Hz; - EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {})); + EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers)); } // b/190578904 -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_deviceWithCloseRefreshRates) { +TEST_F(RefreshRateConfigsTest, getBestRefreshRate_withCloseRefreshRates) { constexpr int kMinRefreshRate = 10; constexpr int kMaxRefreshRate = 240; DisplayModes displayModes; for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) { - constexpr int32_t kGroup = 0; - const auto refreshRate = Fps::fromValue(static_cast<float>(fps)); displayModes.push_back( - createDisplayMode(DisplayModeId(fps), kGroup, refreshRate.getPeriodNsecs())); + createDisplayMode(DisplayModeId(fps), Fps::fromValue(static_cast<float>(fps)))); } - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(displayModes, - /*currentConfigId=*/displayModes[0]->getId()); + const TestableRefreshRateConfigs configs(displayModes, displayModes[0]->getId()); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; const auto testRefreshRate = [&](Fps fps, LayerVoteType vote) { layers[0].desiredRefreshRate = fps; layers[0].vote = vote; - EXPECT_EQ(fps.getIntValue(), - refreshRateConfigs->getBestRefreshRate(layers, {}).getFps().getIntValue()) + EXPECT_EQ(fps.getIntValue(), configs.getBestRefreshRate(layers).getFps().getIntValue()) << "Failed for " << ftl::enum_string(vote); }; @@ -1989,25 +1808,23 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_deviceWithCloseRefreshRates) { // b/190578904 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_conflictingVotes) { const DisplayModes displayModes = { - createDisplayMode(DisplayModeId(0), 0, (43_Hz).getPeriodNsecs()), - createDisplayMode(DisplayModeId(1), 0, (53_Hz).getPeriodNsecs()), - createDisplayMode(DisplayModeId(2), 0, (55_Hz).getPeriodNsecs()), - createDisplayMode(DisplayModeId(3), 0, (60_Hz).getPeriodNsecs()), + createDisplayMode(DisplayModeId(0), 43_Hz), + createDisplayMode(DisplayModeId(1), 53_Hz), + createDisplayMode(DisplayModeId(2), 55_Hz), + createDisplayMode(DisplayModeId(3), 60_Hz), }; const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false}; - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(displayModes, - /*currentConfigId=*/displayModes[0]->getId()); + const TestableRefreshRateConfigs configs(displayModes, displayModes[0]->getId()); - const auto layers = std::vector<LayerRequirement>{ - LayerRequirement{ + const std::vector<LayerRequirement> layers = { + { .vote = LayerVoteType::ExplicitDefault, .desiredRefreshRate = 43_Hz, .seamlessness = Seamlessness::SeamedAndSeamless, .weight = 0.41f, }, - LayerRequirement{ + { .vote = LayerVoteType::ExplicitExactOrMultiple, .desiredRefreshRate = 53_Hz, .seamlessness = Seamlessness::SeamedAndSeamless, @@ -2015,89 +1832,83 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_conflictingVotes) { }, }; - EXPECT_EQ(53_Hz, refreshRateConfigs->getBestRefreshRate(layers, globalSignals).getFps()); + EXPECT_EQ(53_Hz, configs.getBestRefreshRate(layers, globalSignals).getFps()); } TEST_F(RefreshRateConfigsTest, testComparisonOperator) { - EXPECT_TRUE(mExpected60Config < mExpected90Config); - EXPECT_FALSE(mExpected60Config < mExpected60Config); - EXPECT_FALSE(mExpected90Config < mExpected90Config); + EXPECT_TRUE(asRefreshRate(kMode60) < asRefreshRate(kMode90)); + EXPECT_FALSE(asRefreshRate(kMode60) < asRefreshRate(kMode60)); + EXPECT_FALSE(asRefreshRate(kMode90) < asRefreshRate(kMode90)); } TEST_F(RefreshRateConfigsTest, testKernelIdleTimerAction) { - using KernelIdleTimerAction = scheduler::RefreshRateConfigs::KernelIdleTimerAction; + using KernelIdleTimerAction = RefreshRateConfigs::KernelIdleTimerAction; + + RefreshRateConfigs configs(kModes_60_90, kModeId90); - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_90Device, - /*currentConfigId=*/HWC_CONFIG_ID_90); // SetPolicy(60, 90), current 90Hz => TurnOn. - EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction()); + EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction()); // SetPolicy(60, 90), current 60Hz => TurnOn. - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 90_Hz}}), 0); - EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction()); + EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}}), 0); + EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction()); // SetPolicy(60, 60), current 60Hz => TurnOff - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 60_Hz}}), 0); - EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction()); + EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0); + EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction()); // SetPolicy(90, 90), current 90Hz => TurnOff. - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90_Hz, 90_Hz}}), 0); - EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction()); + EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}), 0); + EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction()); } TEST_F(RefreshRateConfigsTest, testKernelIdleTimerActionFor120Hz) { - using KernelIdleTimerAction = scheduler::RefreshRateConfigs::KernelIdleTimerAction; + using KernelIdleTimerAction = RefreshRateConfigs::KernelIdleTimerAction; + + RefreshRateConfigs configs(kModes_60_120, kModeId120); - // Tests with 120Hz - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m60_120Device, - /*currentConfigId=*/HWC_CONFIG_ID_120); // SetPolicy(0, 60), current 60Hz => TurnOn. - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {0_Hz, 60_Hz}}), 0); - EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction()); + EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {0_Hz, 60_Hz}}), 0); + EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction()); // SetPolicy(60, 60), current 60Hz => TurnOff. - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 60_Hz}}), 0); - EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction()); + EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0); + EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction()); // SetPolicy(60, 120), current 60Hz => TurnOn. - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 120_Hz}}), 0); - EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction()); + EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 120_Hz}}), 0); + EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction()); // SetPolicy(120, 120), current 120Hz => TurnOff. - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_120, {120_Hz, 120_Hz}}), - 0); - EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction()); + EXPECT_GE(configs.setDisplayManagerPolicy({kModeId120, {120_Hz, 120_Hz}}), 0); + EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction()); } TEST_F(RefreshRateConfigsTest, getFrameRateDivider) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, - /*currentConfigId=*/HWC_CONFIG_ID_30); + RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId30); const auto frameRate = 30_Hz; - Fps displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps(); + Fps displayRefreshRate = configs.getCurrentRefreshRate().getFps(); EXPECT_EQ(1, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate)); - refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_60); - displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps(); + configs.setCurrentModeId(kModeId60); + displayRefreshRate = configs.getCurrentRefreshRate().getFps(); EXPECT_EQ(2, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate)); - refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_72); - displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps(); + configs.setCurrentModeId(kModeId72); + displayRefreshRate = configs.getCurrentRefreshRate().getFps(); EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate)); - refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90); - displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps(); + configs.setCurrentModeId(kModeId90); + displayRefreshRate = configs.getCurrentRefreshRate().getFps(); EXPECT_EQ(3, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate)); - refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_120); - displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps(); + configs.setCurrentModeId(kModeId120); + displayRefreshRate = configs.getCurrentRefreshRate().getFps(); EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate)); - refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90); - displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps(); + configs.setCurrentModeId(kModeId90); + displayRefreshRate = configs.getCurrentRefreshRate().getFps(); EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, 22.5_Hz)); EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(24_Hz, 25_Hz)); @@ -2133,57 +1944,52 @@ TEST_F(RefreshRateConfigsTest, isFractionalPairOrMultiple) { } TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_noLayers) { - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/ - HWC_CONFIG_ID_120); + RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120); - ASSERT_TRUE(refreshRateConfigs->getFrameRateOverrides({}, 120_Hz, {}).empty()); + EXPECT_TRUE(configs.getFrameRateOverrides({}, 120_Hz, {}).empty()); } TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_60on120) { - RefreshRateConfigs::Config config = {.enableFrameRateOverride = true}; - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/ - HWC_CONFIG_ID_120, config); + RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120, + {.enableFrameRateOverride = true}); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; layers[0].name = "Test layer"; layers[0].ownerUid = 1234; layers[0].desiredRefreshRate = 60_Hz; layers[0].vote = LayerVoteType::ExplicitDefault; - auto frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {}); - ASSERT_EQ(1, frameRateOverrides.size()); - ASSERT_EQ(1, frameRateOverrides.count(1234)); - ASSERT_EQ(60_Hz, frameRateOverrides.at(1234)); + + auto frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); layers[0].vote = LayerVoteType::ExplicitExactOrMultiple; - frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {}); - ASSERT_EQ(1, frameRateOverrides.size()); - ASSERT_EQ(1, frameRateOverrides.count(1234)); - ASSERT_EQ(60_Hz, frameRateOverrides.at(1234)); + frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); layers[0].vote = LayerVoteType::NoVote; - frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {}); - ASSERT_TRUE(frameRateOverrides.empty()); + frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_TRUE(frameRateOverrides.empty()); layers[0].vote = LayerVoteType::Min; - frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {}); - ASSERT_TRUE(frameRateOverrides.empty()); + frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_TRUE(frameRateOverrides.empty()); layers[0].vote = LayerVoteType::Max; - frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {}); - ASSERT_TRUE(frameRateOverrides.empty()); + frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_TRUE(frameRateOverrides.empty()); layers[0].vote = LayerVoteType::Heuristic; - frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {}); - ASSERT_TRUE(frameRateOverrides.empty()); + frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_TRUE(frameRateOverrides.empty()); } TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_twoUids) { - RefreshRateConfigs::Config config = {.enableFrameRateOverride = true}; - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/ - HWC_CONFIG_ID_120, config); + RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120, + {.enableFrameRateOverride = true}); std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}, {.ownerUid = 5678, .weight = 1.f}}; @@ -2195,69 +2001,64 @@ TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_twoUids) { layers[1].name = "Test layer 5678"; layers[1].desiredRefreshRate = 30_Hz; layers[1].vote = LayerVoteType::ExplicitDefault; - auto frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {}); + auto frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); - ASSERT_EQ(2, frameRateOverrides.size()); - ASSERT_EQ(1, frameRateOverrides.count(1234)); - ASSERT_EQ(60_Hz, frameRateOverrides.at(1234)); - ASSERT_EQ(1, frameRateOverrides.count(5678)); - ASSERT_EQ(30_Hz, frameRateOverrides.at(5678)); + EXPECT_EQ(2u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); + ASSERT_EQ(1u, frameRateOverrides.count(5678)); + EXPECT_EQ(30_Hz, frameRateOverrides.at(5678)); layers[1].vote = LayerVoteType::Heuristic; - frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {}); - ASSERT_EQ(1, frameRateOverrides.size()); - ASSERT_EQ(1, frameRateOverrides.count(1234)); - ASSERT_EQ(60_Hz, frameRateOverrides.at(1234)); + frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); layers[1].ownerUid = 1234; - frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {}); - ASSERT_TRUE(frameRateOverrides.empty()); + frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_TRUE(frameRateOverrides.empty()); } TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_touch) { - RefreshRateConfigs::Config config = {.enableFrameRateOverride = true}; - auto refreshRateConfigs = - std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/ - HWC_CONFIG_ID_120, config); + RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120, + {.enableFrameRateOverride = true}); std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}}; layers[0].name = "Test layer"; layers[0].desiredRefreshRate = 60_Hz; layers[0].vote = LayerVoteType::ExplicitDefault; - auto frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {}); - ASSERT_EQ(1, frameRateOverrides.size()); - ASSERT_EQ(1, frameRateOverrides.count(1234)); - ASSERT_EQ(60_Hz, frameRateOverrides.at(1234)); + auto frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); - frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {.touch = true}); - ASSERT_EQ(1, frameRateOverrides.size()); - ASSERT_EQ(1, frameRateOverrides.count(1234)); - ASSERT_EQ(60_Hz, frameRateOverrides.at(1234)); + frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); layers[0].vote = LayerVoteType::ExplicitExact; - frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {}); - ASSERT_EQ(1, frameRateOverrides.size()); - ASSERT_EQ(1, frameRateOverrides.count(1234)); - ASSERT_EQ(60_Hz, frameRateOverrides.at(1234)); + frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); - frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {.touch = true}); - ASSERT_EQ(1, frameRateOverrides.size()); - ASSERT_EQ(1, frameRateOverrides.count(1234)); - ASSERT_EQ(60_Hz, frameRateOverrides.at(1234)); + frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); layers[0].vote = LayerVoteType::ExplicitExactOrMultiple; - frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {}); - ASSERT_EQ(1, frameRateOverrides.size()); - ASSERT_EQ(1, frameRateOverrides.count(1234)); - ASSERT_EQ(60_Hz, frameRateOverrides.at(1234)); + frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); - frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {.touch = true}); - ASSERT_TRUE(frameRateOverrides.empty()); + frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); + EXPECT_TRUE(frameRateOverrides.empty()); } } // namespace } // namespace android::scheduler - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wextra" diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp index 5ac581288e..39dbb07653 100644 --- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp @@ -91,10 +91,9 @@ TEST_F(TransactionTracingTest, addTransactions) { flush(secondTransactionSetVsyncId); proto::TransactionTraceFile proto = writeToProto(); - EXPECT_EQ(proto.entry().size(), 3); - // skip starting entry - verifyEntry(proto.entry(1), firstTransactionSet, firstTransactionSetVsyncId); - verifyEntry(proto.entry(2), secondTransactionSet, secondTransactionSetVsyncId); + EXPECT_EQ(proto.entry().size(), 2); + verifyEntry(proto.entry(0), firstTransactionSet, firstTransactionSetVsyncId); + verifyEntry(proto.entry(1), secondTransactionSet, secondTransactionSetVsyncId); } class TransactionTracingLayerHandlingTest : public TransactionTracingTest { @@ -274,15 +273,13 @@ protected: TEST_F(TransactionTracingMirrorLayerTest, canAddMirrorLayers) { proto::TransactionTraceFile proto = writeToProto(); // We don't have any starting states since no layer was removed from. - EXPECT_EQ(proto.entry().size(), 2); - EXPECT_EQ(proto.entry(0).transactions().size(), 0); - EXPECT_EQ(proto.entry(0).added_layers().size(), 0); + EXPECT_EQ(proto.entry().size(), 1); // Verify the mirror layer was added - EXPECT_EQ(proto.entry(1).transactions().size(), 1); - EXPECT_EQ(proto.entry(1).added_layers().size(), 2); - EXPECT_EQ(proto.entry(1).added_layers(1).layer_id(), mMirrorLayerId); - EXPECT_EQ(proto.entry(1).transactions(0).layer_changes().size(), 2); - EXPECT_EQ(proto.entry(1).transactions(0).layer_changes(1).z(), 43); + EXPECT_EQ(proto.entry(0).transactions().size(), 1); + EXPECT_EQ(proto.entry(0).added_layers().size(), 2); + EXPECT_EQ(proto.entry(0).added_layers(1).layer_id(), mMirrorLayerId); + EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 2); + EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(1).z(), 43); } } // namespace android diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h index 4273401bc7..0765d5b59c 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h @@ -19,6 +19,7 @@ #include <gmock/gmock.h> #include "DisplayHardware/ComposerHal.h" +#include "DisplayHardware/HWC2.h" namespace android { @@ -51,7 +52,7 @@ public: MOCK_METHOD(bool, isSupported, (OptionalFeature), (const, override)); MOCK_METHOD0(getCapabilities, std::vector<IComposer::Capability>()); MOCK_METHOD0(dumpDebugInfo, std::string()); - MOCK_METHOD1(registerCallback, void(const sp<IComposerCallback>&)); + MOCK_METHOD1(registerCallback, void(HWC2::ComposerCallback&)); MOCK_METHOD0(resetCommands, void()); MOCK_METHOD0(executeCommands, Error()); MOCK_METHOD0(getMaxVirtualDisplayCount, uint32_t()); @@ -135,7 +136,6 @@ public: V2_4::Error(Display, Config, const IComposerClient::VsyncPeriodChangeConstraints&, VsyncPeriodChangeTimeline*)); MOCK_METHOD2(setAutoLowLatencyMode, V2_4::Error(Display, bool)); - MOCK_METHOD2(getBootDisplayConfigSupport, Error(Display, bool*)); MOCK_METHOD2(setBootDisplayConfig, Error(Display, Config)); MOCK_METHOD1(clearBootDisplayConfig, Error(Display)); MOCK_METHOD2(getPreferredBootDisplayConfig, Error(Display, Config*)); diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp index fa3b2601a4..c335e2a952 100644 --- a/vulkan/libvulkan/api.cpp +++ b/vulkan/libvulkan/api.cpp @@ -1473,7 +1473,7 @@ VkResult EnumerateInstanceVersion(uint32_t* pApiVersion) { if (!EnsureInitialized()) return VK_ERROR_OUT_OF_HOST_MEMORY; - *pApiVersion = VK_API_VERSION_1_1; + *pApiVersion = VK_API_VERSION_1_3; return VK_SUCCESS; } diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp index 33401d24d6..df70bf4419 100644 --- a/vulkan/libvulkan/api_gen.cpp +++ b/vulkan/libvulkan/api_gen.cpp @@ -179,6 +179,7 @@ bool InitDispatchTable( INIT_PROC(false, instance, GetPhysicalDeviceExternalFenceProperties); INIT_PROC(false, instance, EnumeratePhysicalDeviceGroups); INIT_PROC_EXT(KHR_swapchain, false, instance, GetPhysicalDevicePresentRectanglesKHR); + INIT_PROC(false, instance, GetPhysicalDeviceToolProperties); // clang-format on return success; @@ -334,6 +335,9 @@ bool InitDispatchTable( INIT_PROC(false, dev, GetBufferMemoryRequirements2); INIT_PROC(false, dev, GetImageMemoryRequirements2); INIT_PROC(false, dev, GetImageSparseMemoryRequirements2); + INIT_PROC(false, dev, GetDeviceBufferMemoryRequirements); + INIT_PROC(false, dev, GetDeviceImageMemoryRequirements); + INIT_PROC(false, dev, GetDeviceImageSparseMemoryRequirements); INIT_PROC(false, dev, CreateSamplerYcbcrConversion); INIT_PROC(false, dev, DestroySamplerYcbcrConversion); INIT_PROC(false, dev, GetDeviceQueue2); @@ -352,6 +356,39 @@ bool InitDispatchTable( INIT_PROC(false, dev, GetBufferOpaqueCaptureAddress); INIT_PROC(false, dev, GetBufferDeviceAddress); INIT_PROC(false, dev, GetDeviceMemoryOpaqueCaptureAddress); + INIT_PROC(false, dev, CmdSetCullMode); + INIT_PROC(false, dev, CmdSetFrontFace); + INIT_PROC(false, dev, CmdSetPrimitiveTopology); + INIT_PROC(false, dev, CmdSetViewportWithCount); + INIT_PROC(false, dev, CmdSetScissorWithCount); + INIT_PROC(false, dev, CmdBindVertexBuffers2); + INIT_PROC(false, dev, CmdSetDepthTestEnable); + INIT_PROC(false, dev, CmdSetDepthWriteEnable); + INIT_PROC(false, dev, CmdSetDepthCompareOp); + INIT_PROC(false, dev, CmdSetDepthBoundsTestEnable); + INIT_PROC(false, dev, CmdSetStencilTestEnable); + INIT_PROC(false, dev, CmdSetStencilOp); + INIT_PROC(false, dev, CmdSetRasterizerDiscardEnable); + INIT_PROC(false, dev, CmdSetDepthBiasEnable); + INIT_PROC(false, dev, CmdSetPrimitiveRestartEnable); + INIT_PROC(false, dev, CreatePrivateDataSlot); + INIT_PROC(false, dev, DestroyPrivateDataSlot); + INIT_PROC(false, dev, SetPrivateData); + INIT_PROC(false, dev, GetPrivateData); + INIT_PROC(false, dev, CmdCopyBuffer2); + INIT_PROC(false, dev, CmdCopyImage2); + INIT_PROC(false, dev, CmdBlitImage2); + INIT_PROC(false, dev, CmdCopyBufferToImage2); + INIT_PROC(false, dev, CmdCopyImageToBuffer2); + INIT_PROC(false, dev, CmdResolveImage2); + INIT_PROC(false, dev, CmdSetEvent2); + INIT_PROC(false, dev, CmdResetEvent2); + INIT_PROC(false, dev, CmdWaitEvents2); + INIT_PROC(false, dev, CmdPipelineBarrier2); + INIT_PROC(false, dev, QueueSubmit2); + INIT_PROC(false, dev, CmdWriteTimestamp2); + INIT_PROC(false, dev, CmdBeginRendering); + INIT_PROC(false, dev, CmdEndRendering); // clang-format on return success; @@ -530,6 +567,9 @@ VKAPI_ATTR void UpdateDescriptorSetWithTemplate(VkDevice device, VkDescriptorSet VKAPI_ATTR void GetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements); VKAPI_ATTR void GetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements); VKAPI_ATTR void GetImageSparseMemoryRequirements2(VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements); +VKAPI_ATTR void GetDeviceBufferMemoryRequirements(VkDevice device, const VkDeviceBufferMemoryRequirements* pInfo, VkMemoryRequirements2* pMemoryRequirements); +VKAPI_ATTR void GetDeviceImageMemoryRequirements(VkDevice device, const VkDeviceImageMemoryRequirements* pInfo, VkMemoryRequirements2* pMemoryRequirements); +VKAPI_ATTR void GetDeviceImageSparseMemoryRequirements(VkDevice device, const VkDeviceImageMemoryRequirements* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements); VKAPI_ATTR VkResult CreateSamplerYcbcrConversion(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion); VKAPI_ATTR void DestroySamplerYcbcrConversion(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue); @@ -548,6 +588,40 @@ VKAPI_ATTR void CmdDrawIndexedIndirectCount(VkCommandBuffer commandBuffer, VkBuf VKAPI_ATTR uint64_t GetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo); VKAPI_ATTR VkDeviceAddress GetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo); VKAPI_ATTR uint64_t GetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo); +VKAPI_ATTR VkResult GetPhysicalDeviceToolProperties(VkPhysicalDevice physicalDevice, uint32_t* pToolCount, VkPhysicalDeviceToolProperties* pToolProperties); +VKAPI_ATTR void CmdSetCullMode(VkCommandBuffer commandBuffer, VkCullModeFlags cullMode); +VKAPI_ATTR void CmdSetFrontFace(VkCommandBuffer commandBuffer, VkFrontFace frontFace); +VKAPI_ATTR void CmdSetPrimitiveTopology(VkCommandBuffer commandBuffer, VkPrimitiveTopology primitiveTopology); +VKAPI_ATTR void CmdSetViewportWithCount(VkCommandBuffer commandBuffer, uint32_t viewportCount, const VkViewport* pViewports); +VKAPI_ATTR void CmdSetScissorWithCount(VkCommandBuffer commandBuffer, uint32_t scissorCount, const VkRect2D* pScissors); +VKAPI_ATTR void CmdBindVertexBuffers2(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets, const VkDeviceSize* pSizes, const VkDeviceSize* pStrides); +VKAPI_ATTR void CmdSetDepthTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthTestEnable); +VKAPI_ATTR void CmdSetDepthWriteEnable(VkCommandBuffer commandBuffer, VkBool32 depthWriteEnable); +VKAPI_ATTR void CmdSetDepthCompareOp(VkCommandBuffer commandBuffer, VkCompareOp depthCompareOp); +VKAPI_ATTR void CmdSetDepthBoundsTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthBoundsTestEnable); +VKAPI_ATTR void CmdSetStencilTestEnable(VkCommandBuffer commandBuffer, VkBool32 stencilTestEnable); +VKAPI_ATTR void CmdSetStencilOp(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, VkStencilOp failOp, VkStencilOp passOp, VkStencilOp depthFailOp, VkCompareOp compareOp); +VKAPI_ATTR void CmdSetRasterizerDiscardEnable(VkCommandBuffer commandBuffer, VkBool32 rasterizerDiscardEnable); +VKAPI_ATTR void CmdSetDepthBiasEnable(VkCommandBuffer commandBuffer, VkBool32 depthBiasEnable); +VKAPI_ATTR void CmdSetPrimitiveRestartEnable(VkCommandBuffer commandBuffer, VkBool32 primitiveRestartEnable); +VKAPI_ATTR VkResult CreatePrivateDataSlot(VkDevice device, const VkPrivateDataSlotCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPrivateDataSlot* pPrivateDataSlot); +VKAPI_ATTR void DestroyPrivateDataSlot(VkDevice device, VkPrivateDataSlot privateDataSlot, const VkAllocationCallbacks* pAllocator); +VKAPI_ATTR VkResult SetPrivateData(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t data); +VKAPI_ATTR void GetPrivateData(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t* pData); +VKAPI_ATTR void CmdCopyBuffer2(VkCommandBuffer commandBuffer, const VkCopyBufferInfo2* pCopyBufferInfo); +VKAPI_ATTR void CmdCopyImage2(VkCommandBuffer commandBuffer, const VkCopyImageInfo2* pCopyImageInfo); +VKAPI_ATTR void CmdBlitImage2(VkCommandBuffer commandBuffer, const VkBlitImageInfo2* pBlitImageInfo); +VKAPI_ATTR void CmdCopyBufferToImage2(VkCommandBuffer commandBuffer, const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo); +VKAPI_ATTR void CmdCopyImageToBuffer2(VkCommandBuffer commandBuffer, const VkCopyImageToBufferInfo2* pCopyImageToBufferInfo); +VKAPI_ATTR void CmdResolveImage2(VkCommandBuffer commandBuffer, const VkResolveImageInfo2* pResolveImageInfo); +VKAPI_ATTR void CmdSetEvent2(VkCommandBuffer commandBuffer, VkEvent event, const VkDependencyInfo* pDependencyInfo); +VKAPI_ATTR void CmdResetEvent2(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags2 stageMask); +VKAPI_ATTR void CmdWaitEvents2(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent* pEvents, const VkDependencyInfo* pDependencyInfos); +VKAPI_ATTR void CmdPipelineBarrier2(VkCommandBuffer commandBuffer, const VkDependencyInfo* pDependencyInfo); +VKAPI_ATTR VkResult QueueSubmit2(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2* pSubmits, VkFence fence); +VKAPI_ATTR void CmdWriteTimestamp2(VkCommandBuffer commandBuffer, VkPipelineStageFlags2 stage, VkQueryPool queryPool, uint32_t query); +VKAPI_ATTR void CmdBeginRendering(VkCommandBuffer commandBuffer, const VkRenderingInfo* pRenderingInfo); +VKAPI_ATTR void CmdEndRendering(VkCommandBuffer commandBuffer); VKAPI_ATTR VkResult EnumeratePhysicalDevices(VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices) { return GetData(instance).dispatch.EnumeratePhysicalDevices(instance, pPhysicalDeviceCount, pPhysicalDevices); @@ -625,6 +699,7 @@ VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, const char* pNa "vkGetPhysicalDeviceSurfaceFormatsKHR", "vkGetPhysicalDeviceSurfacePresentModesKHR", "vkGetPhysicalDeviceSurfaceSupportKHR", + "vkGetPhysicalDeviceToolProperties", "vkGetPhysicalDeviceToolPropertiesEXT", "vkGetPhysicalDeviceVideoCapabilitiesKHR", "vkGetPhysicalDeviceVideoFormatPropertiesKHR", @@ -680,18 +755,25 @@ VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const cha { "vkCmdBeginQuery", reinterpret_cast<PFN_vkVoidFunction>(CmdBeginQuery) }, { "vkCmdBeginRenderPass", reinterpret_cast<PFN_vkVoidFunction>(CmdBeginRenderPass) }, { "vkCmdBeginRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(CmdBeginRenderPass2) }, + { "vkCmdBeginRendering", reinterpret_cast<PFN_vkVoidFunction>(CmdBeginRendering) }, { "vkCmdBindDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(CmdBindDescriptorSets) }, { "vkCmdBindIndexBuffer", reinterpret_cast<PFN_vkVoidFunction>(CmdBindIndexBuffer) }, { "vkCmdBindPipeline", reinterpret_cast<PFN_vkVoidFunction>(CmdBindPipeline) }, { "vkCmdBindVertexBuffers", reinterpret_cast<PFN_vkVoidFunction>(CmdBindVertexBuffers) }, + { "vkCmdBindVertexBuffers2", reinterpret_cast<PFN_vkVoidFunction>(CmdBindVertexBuffers2) }, { "vkCmdBlitImage", reinterpret_cast<PFN_vkVoidFunction>(CmdBlitImage) }, + { "vkCmdBlitImage2", reinterpret_cast<PFN_vkVoidFunction>(CmdBlitImage2) }, { "vkCmdClearAttachments", reinterpret_cast<PFN_vkVoidFunction>(CmdClearAttachments) }, { "vkCmdClearColorImage", reinterpret_cast<PFN_vkVoidFunction>(CmdClearColorImage) }, { "vkCmdClearDepthStencilImage", reinterpret_cast<PFN_vkVoidFunction>(CmdClearDepthStencilImage) }, { "vkCmdCopyBuffer", reinterpret_cast<PFN_vkVoidFunction>(CmdCopyBuffer) }, + { "vkCmdCopyBuffer2", reinterpret_cast<PFN_vkVoidFunction>(CmdCopyBuffer2) }, { "vkCmdCopyBufferToImage", reinterpret_cast<PFN_vkVoidFunction>(CmdCopyBufferToImage) }, + { "vkCmdCopyBufferToImage2", reinterpret_cast<PFN_vkVoidFunction>(CmdCopyBufferToImage2) }, { "vkCmdCopyImage", reinterpret_cast<PFN_vkVoidFunction>(CmdCopyImage) }, + { "vkCmdCopyImage2", reinterpret_cast<PFN_vkVoidFunction>(CmdCopyImage2) }, { "vkCmdCopyImageToBuffer", reinterpret_cast<PFN_vkVoidFunction>(CmdCopyImageToBuffer) }, + { "vkCmdCopyImageToBuffer2", reinterpret_cast<PFN_vkVoidFunction>(CmdCopyImageToBuffer2) }, { "vkCmdCopyQueryPoolResults", reinterpret_cast<PFN_vkVoidFunction>(CmdCopyQueryPoolResults) }, { "vkCmdDispatch", reinterpret_cast<PFN_vkVoidFunction>(CmdDispatch) }, { "vkCmdDispatchBase", reinterpret_cast<PFN_vkVoidFunction>(CmdDispatchBase) }, @@ -705,29 +787,50 @@ VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const cha { "vkCmdEndQuery", reinterpret_cast<PFN_vkVoidFunction>(CmdEndQuery) }, { "vkCmdEndRenderPass", reinterpret_cast<PFN_vkVoidFunction>(CmdEndRenderPass) }, { "vkCmdEndRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(CmdEndRenderPass2) }, + { "vkCmdEndRendering", reinterpret_cast<PFN_vkVoidFunction>(CmdEndRendering) }, { "vkCmdExecuteCommands", reinterpret_cast<PFN_vkVoidFunction>(CmdExecuteCommands) }, { "vkCmdFillBuffer", reinterpret_cast<PFN_vkVoidFunction>(CmdFillBuffer) }, { "vkCmdNextSubpass", reinterpret_cast<PFN_vkVoidFunction>(CmdNextSubpass) }, { "vkCmdNextSubpass2", reinterpret_cast<PFN_vkVoidFunction>(CmdNextSubpass2) }, { "vkCmdPipelineBarrier", reinterpret_cast<PFN_vkVoidFunction>(CmdPipelineBarrier) }, + { "vkCmdPipelineBarrier2", reinterpret_cast<PFN_vkVoidFunction>(CmdPipelineBarrier2) }, { "vkCmdPushConstants", reinterpret_cast<PFN_vkVoidFunction>(CmdPushConstants) }, { "vkCmdResetEvent", reinterpret_cast<PFN_vkVoidFunction>(CmdResetEvent) }, + { "vkCmdResetEvent2", reinterpret_cast<PFN_vkVoidFunction>(CmdResetEvent2) }, { "vkCmdResetQueryPool", reinterpret_cast<PFN_vkVoidFunction>(CmdResetQueryPool) }, { "vkCmdResolveImage", reinterpret_cast<PFN_vkVoidFunction>(CmdResolveImage) }, + { "vkCmdResolveImage2", reinterpret_cast<PFN_vkVoidFunction>(CmdResolveImage2) }, { "vkCmdSetBlendConstants", reinterpret_cast<PFN_vkVoidFunction>(CmdSetBlendConstants) }, + { "vkCmdSetCullMode", reinterpret_cast<PFN_vkVoidFunction>(CmdSetCullMode) }, { "vkCmdSetDepthBias", reinterpret_cast<PFN_vkVoidFunction>(CmdSetDepthBias) }, + { "vkCmdSetDepthBiasEnable", reinterpret_cast<PFN_vkVoidFunction>(CmdSetDepthBiasEnable) }, { "vkCmdSetDepthBounds", reinterpret_cast<PFN_vkVoidFunction>(CmdSetDepthBounds) }, + { "vkCmdSetDepthBoundsTestEnable", reinterpret_cast<PFN_vkVoidFunction>(CmdSetDepthBoundsTestEnable) }, + { "vkCmdSetDepthCompareOp", reinterpret_cast<PFN_vkVoidFunction>(CmdSetDepthCompareOp) }, + { "vkCmdSetDepthTestEnable", reinterpret_cast<PFN_vkVoidFunction>(CmdSetDepthTestEnable) }, + { "vkCmdSetDepthWriteEnable", reinterpret_cast<PFN_vkVoidFunction>(CmdSetDepthWriteEnable) }, { "vkCmdSetDeviceMask", reinterpret_cast<PFN_vkVoidFunction>(CmdSetDeviceMask) }, { "vkCmdSetEvent", reinterpret_cast<PFN_vkVoidFunction>(CmdSetEvent) }, + { "vkCmdSetEvent2", reinterpret_cast<PFN_vkVoidFunction>(CmdSetEvent2) }, + { "vkCmdSetFrontFace", reinterpret_cast<PFN_vkVoidFunction>(CmdSetFrontFace) }, { "vkCmdSetLineWidth", reinterpret_cast<PFN_vkVoidFunction>(CmdSetLineWidth) }, + { "vkCmdSetPrimitiveRestartEnable", reinterpret_cast<PFN_vkVoidFunction>(CmdSetPrimitiveRestartEnable) }, + { "vkCmdSetPrimitiveTopology", reinterpret_cast<PFN_vkVoidFunction>(CmdSetPrimitiveTopology) }, + { "vkCmdSetRasterizerDiscardEnable", reinterpret_cast<PFN_vkVoidFunction>(CmdSetRasterizerDiscardEnable) }, { "vkCmdSetScissor", reinterpret_cast<PFN_vkVoidFunction>(CmdSetScissor) }, + { "vkCmdSetScissorWithCount", reinterpret_cast<PFN_vkVoidFunction>(CmdSetScissorWithCount) }, { "vkCmdSetStencilCompareMask", reinterpret_cast<PFN_vkVoidFunction>(CmdSetStencilCompareMask) }, + { "vkCmdSetStencilOp", reinterpret_cast<PFN_vkVoidFunction>(CmdSetStencilOp) }, { "vkCmdSetStencilReference", reinterpret_cast<PFN_vkVoidFunction>(CmdSetStencilReference) }, + { "vkCmdSetStencilTestEnable", reinterpret_cast<PFN_vkVoidFunction>(CmdSetStencilTestEnable) }, { "vkCmdSetStencilWriteMask", reinterpret_cast<PFN_vkVoidFunction>(CmdSetStencilWriteMask) }, { "vkCmdSetViewport", reinterpret_cast<PFN_vkVoidFunction>(CmdSetViewport) }, + { "vkCmdSetViewportWithCount", reinterpret_cast<PFN_vkVoidFunction>(CmdSetViewportWithCount) }, { "vkCmdUpdateBuffer", reinterpret_cast<PFN_vkVoidFunction>(CmdUpdateBuffer) }, { "vkCmdWaitEvents", reinterpret_cast<PFN_vkVoidFunction>(CmdWaitEvents) }, + { "vkCmdWaitEvents2", reinterpret_cast<PFN_vkVoidFunction>(CmdWaitEvents2) }, { "vkCmdWriteTimestamp", reinterpret_cast<PFN_vkVoidFunction>(CmdWriteTimestamp) }, + { "vkCmdWriteTimestamp2", reinterpret_cast<PFN_vkVoidFunction>(CmdWriteTimestamp2) }, { "vkCreateBuffer", reinterpret_cast<PFN_vkVoidFunction>(CreateBuffer) }, { "vkCreateBufferView", reinterpret_cast<PFN_vkVoidFunction>(CreateBufferView) }, { "vkCreateCommandPool", reinterpret_cast<PFN_vkVoidFunction>(CreateCommandPool) }, @@ -745,6 +848,7 @@ VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const cha { "vkCreateInstance", nullptr }, { "vkCreatePipelineCache", reinterpret_cast<PFN_vkVoidFunction>(CreatePipelineCache) }, { "vkCreatePipelineLayout", reinterpret_cast<PFN_vkVoidFunction>(CreatePipelineLayout) }, + { "vkCreatePrivateDataSlot", reinterpret_cast<PFN_vkVoidFunction>(CreatePrivateDataSlot) }, { "vkCreateQueryPool", reinterpret_cast<PFN_vkVoidFunction>(CreateQueryPool) }, { "vkCreateRenderPass", reinterpret_cast<PFN_vkVoidFunction>(CreateRenderPass) }, { "vkCreateRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(CreateRenderPass2) }, @@ -769,6 +873,7 @@ VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const cha { "vkDestroyPipeline", reinterpret_cast<PFN_vkVoidFunction>(DestroyPipeline) }, { "vkDestroyPipelineCache", reinterpret_cast<PFN_vkVoidFunction>(DestroyPipelineCache) }, { "vkDestroyPipelineLayout", reinterpret_cast<PFN_vkVoidFunction>(DestroyPipelineLayout) }, + { "vkDestroyPrivateDataSlot", reinterpret_cast<PFN_vkVoidFunction>(DestroyPrivateDataSlot) }, { "vkDestroyQueryPool", reinterpret_cast<PFN_vkVoidFunction>(DestroyQueryPool) }, { "vkDestroyRenderPass", reinterpret_cast<PFN_vkVoidFunction>(DestroyRenderPass) }, { "vkDestroySampler", reinterpret_cast<PFN_vkVoidFunction>(DestroySampler) }, @@ -793,9 +898,12 @@ VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const cha { "vkGetBufferMemoryRequirements2", reinterpret_cast<PFN_vkVoidFunction>(GetBufferMemoryRequirements2) }, { "vkGetBufferOpaqueCaptureAddress", reinterpret_cast<PFN_vkVoidFunction>(GetBufferOpaqueCaptureAddress) }, { "vkGetDescriptorSetLayoutSupport", reinterpret_cast<PFN_vkVoidFunction>(GetDescriptorSetLayoutSupport) }, + { "vkGetDeviceBufferMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceBufferMemoryRequirements) }, { "vkGetDeviceGroupPeerMemoryFeatures", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceGroupPeerMemoryFeatures) }, { "vkGetDeviceGroupPresentCapabilitiesKHR", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceGroupPresentCapabilitiesKHR) }, { "vkGetDeviceGroupSurfacePresentModesKHR", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceGroupSurfacePresentModesKHR) }, + { "vkGetDeviceImageMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceImageMemoryRequirements) }, + { "vkGetDeviceImageSparseMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceImageSparseMemoryRequirements) }, { "vkGetDeviceMemoryCommitment", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceMemoryCommitment) }, { "vkGetDeviceMemoryOpaqueCaptureAddress", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceMemoryOpaqueCaptureAddress) }, { "vkGetDeviceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceProcAddr) }, @@ -811,6 +919,7 @@ VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const cha { "vkGetInstanceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(GetInstanceProcAddr) }, { "vkGetMemoryAndroidHardwareBufferANDROID", reinterpret_cast<PFN_vkVoidFunction>(GetMemoryAndroidHardwareBufferANDROID) }, { "vkGetPipelineCacheData", reinterpret_cast<PFN_vkVoidFunction>(GetPipelineCacheData) }, + { "vkGetPrivateData", reinterpret_cast<PFN_vkVoidFunction>(GetPrivateData) }, { "vkGetQueryPoolResults", reinterpret_cast<PFN_vkVoidFunction>(GetQueryPoolResults) }, { "vkGetRenderAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(GetRenderAreaGranularity) }, { "vkGetSemaphoreCounterValue", reinterpret_cast<PFN_vkVoidFunction>(GetSemaphoreCounterValue) }, @@ -821,6 +930,7 @@ VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const cha { "vkQueueBindSparse", reinterpret_cast<PFN_vkVoidFunction>(QueueBindSparse) }, { "vkQueuePresentKHR", reinterpret_cast<PFN_vkVoidFunction>(QueuePresentKHR) }, { "vkQueueSubmit", reinterpret_cast<PFN_vkVoidFunction>(QueueSubmit) }, + { "vkQueueSubmit2", reinterpret_cast<PFN_vkVoidFunction>(QueueSubmit2) }, { "vkQueueWaitIdle", reinterpret_cast<PFN_vkVoidFunction>(QueueWaitIdle) }, { "vkResetCommandBuffer", reinterpret_cast<PFN_vkVoidFunction>(ResetCommandBuffer) }, { "vkResetCommandPool", reinterpret_cast<PFN_vkVoidFunction>(ResetCommandPool) }, @@ -829,6 +939,7 @@ VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const cha { "vkResetFences", reinterpret_cast<PFN_vkVoidFunction>(ResetFences) }, { "vkResetQueryPool", reinterpret_cast<PFN_vkVoidFunction>(ResetQueryPool) }, { "vkSetEvent", reinterpret_cast<PFN_vkVoidFunction>(SetEvent) }, + { "vkSetPrivateData", reinterpret_cast<PFN_vkVoidFunction>(SetPrivateData) }, { "vkSignalSemaphore", reinterpret_cast<PFN_vkVoidFunction>(SignalSemaphore) }, { "vkTrimCommandPool", reinterpret_cast<PFN_vkVoidFunction>(TrimCommandPool) }, { "vkUnmapMemory", reinterpret_cast<PFN_vkVoidFunction>(UnmapMemory) }, @@ -1515,6 +1626,18 @@ VKAPI_ATTR void GetImageSparseMemoryRequirements2(VkDevice device, const VkImage GetData(device).dispatch.GetImageSparseMemoryRequirements2(device, pInfo, pSparseMemoryRequirementCount, pSparseMemoryRequirements); } +VKAPI_ATTR void GetDeviceBufferMemoryRequirements(VkDevice device, const VkDeviceBufferMemoryRequirements* pInfo, VkMemoryRequirements2* pMemoryRequirements) { + GetData(device).dispatch.GetDeviceBufferMemoryRequirements(device, pInfo, pMemoryRequirements); +} + +VKAPI_ATTR void GetDeviceImageMemoryRequirements(VkDevice device, const VkDeviceImageMemoryRequirements* pInfo, VkMemoryRequirements2* pMemoryRequirements) { + GetData(device).dispatch.GetDeviceImageMemoryRequirements(device, pInfo, pMemoryRequirements); +} + +VKAPI_ATTR void GetDeviceImageSparseMemoryRequirements(VkDevice device, const VkDeviceImageMemoryRequirements* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements) { + GetData(device).dispatch.GetDeviceImageSparseMemoryRequirements(device, pInfo, pSparseMemoryRequirementCount, pSparseMemoryRequirements); +} + VKAPI_ATTR VkResult CreateSamplerYcbcrConversion(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion) { return GetData(device).dispatch.CreateSamplerYcbcrConversion(device, pCreateInfo, pAllocator, pYcbcrConversion); } @@ -1587,6 +1710,142 @@ VKAPI_ATTR uint64_t GetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const V return GetData(device).dispatch.GetDeviceMemoryOpaqueCaptureAddress(device, pInfo); } +VKAPI_ATTR VkResult GetPhysicalDeviceToolProperties(VkPhysicalDevice physicalDevice, uint32_t* pToolCount, VkPhysicalDeviceToolProperties* pToolProperties) { + return GetData(physicalDevice).dispatch.GetPhysicalDeviceToolProperties(physicalDevice, pToolCount, pToolProperties); +} + +VKAPI_ATTR void CmdSetCullMode(VkCommandBuffer commandBuffer, VkCullModeFlags cullMode) { + GetData(commandBuffer).dispatch.CmdSetCullMode(commandBuffer, cullMode); +} + +VKAPI_ATTR void CmdSetFrontFace(VkCommandBuffer commandBuffer, VkFrontFace frontFace) { + GetData(commandBuffer).dispatch.CmdSetFrontFace(commandBuffer, frontFace); +} + +VKAPI_ATTR void CmdSetPrimitiveTopology(VkCommandBuffer commandBuffer, VkPrimitiveTopology primitiveTopology) { + GetData(commandBuffer).dispatch.CmdSetPrimitiveTopology(commandBuffer, primitiveTopology); +} + +VKAPI_ATTR void CmdSetViewportWithCount(VkCommandBuffer commandBuffer, uint32_t viewportCount, const VkViewport* pViewports) { + GetData(commandBuffer).dispatch.CmdSetViewportWithCount(commandBuffer, viewportCount, pViewports); +} + +VKAPI_ATTR void CmdSetScissorWithCount(VkCommandBuffer commandBuffer, uint32_t scissorCount, const VkRect2D* pScissors) { + GetData(commandBuffer).dispatch.CmdSetScissorWithCount(commandBuffer, scissorCount, pScissors); +} + +VKAPI_ATTR void CmdBindVertexBuffers2(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets, const VkDeviceSize* pSizes, const VkDeviceSize* pStrides) { + GetData(commandBuffer).dispatch.CmdBindVertexBuffers2(commandBuffer, firstBinding, bindingCount, pBuffers, pOffsets, pSizes, pStrides); +} + +VKAPI_ATTR void CmdSetDepthTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthTestEnable) { + GetData(commandBuffer).dispatch.CmdSetDepthTestEnable(commandBuffer, depthTestEnable); +} + +VKAPI_ATTR void CmdSetDepthWriteEnable(VkCommandBuffer commandBuffer, VkBool32 depthWriteEnable) { + GetData(commandBuffer).dispatch.CmdSetDepthWriteEnable(commandBuffer, depthWriteEnable); +} + +VKAPI_ATTR void CmdSetDepthCompareOp(VkCommandBuffer commandBuffer, VkCompareOp depthCompareOp) { + GetData(commandBuffer).dispatch.CmdSetDepthCompareOp(commandBuffer, depthCompareOp); +} + +VKAPI_ATTR void CmdSetDepthBoundsTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthBoundsTestEnable) { + GetData(commandBuffer).dispatch.CmdSetDepthBoundsTestEnable(commandBuffer, depthBoundsTestEnable); +} + +VKAPI_ATTR void CmdSetStencilTestEnable(VkCommandBuffer commandBuffer, VkBool32 stencilTestEnable) { + GetData(commandBuffer).dispatch.CmdSetStencilTestEnable(commandBuffer, stencilTestEnable); +} + +VKAPI_ATTR void CmdSetStencilOp(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, VkStencilOp failOp, VkStencilOp passOp, VkStencilOp depthFailOp, VkCompareOp compareOp) { + GetData(commandBuffer).dispatch.CmdSetStencilOp(commandBuffer, faceMask, failOp, passOp, depthFailOp, compareOp); +} + +VKAPI_ATTR void CmdSetRasterizerDiscardEnable(VkCommandBuffer commandBuffer, VkBool32 rasterizerDiscardEnable) { + GetData(commandBuffer).dispatch.CmdSetRasterizerDiscardEnable(commandBuffer, rasterizerDiscardEnable); +} + +VKAPI_ATTR void CmdSetDepthBiasEnable(VkCommandBuffer commandBuffer, VkBool32 depthBiasEnable) { + GetData(commandBuffer).dispatch.CmdSetDepthBiasEnable(commandBuffer, depthBiasEnable); +} + +VKAPI_ATTR void CmdSetPrimitiveRestartEnable(VkCommandBuffer commandBuffer, VkBool32 primitiveRestartEnable) { + GetData(commandBuffer).dispatch.CmdSetPrimitiveRestartEnable(commandBuffer, primitiveRestartEnable); +} + +VKAPI_ATTR VkResult CreatePrivateDataSlot(VkDevice device, const VkPrivateDataSlotCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPrivateDataSlot* pPrivateDataSlot) { + return GetData(device).dispatch.CreatePrivateDataSlot(device, pCreateInfo, pAllocator, pPrivateDataSlot); +} + +VKAPI_ATTR void DestroyPrivateDataSlot(VkDevice device, VkPrivateDataSlot privateDataSlot, const VkAllocationCallbacks* pAllocator) { + GetData(device).dispatch.DestroyPrivateDataSlot(device, privateDataSlot, pAllocator); +} + +VKAPI_ATTR VkResult SetPrivateData(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t data) { + return GetData(device).dispatch.SetPrivateData(device, objectType, objectHandle, privateDataSlot, data); +} + +VKAPI_ATTR void GetPrivateData(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t* pData) { + GetData(device).dispatch.GetPrivateData(device, objectType, objectHandle, privateDataSlot, pData); +} + +VKAPI_ATTR void CmdCopyBuffer2(VkCommandBuffer commandBuffer, const VkCopyBufferInfo2* pCopyBufferInfo) { + GetData(commandBuffer).dispatch.CmdCopyBuffer2(commandBuffer, pCopyBufferInfo); +} + +VKAPI_ATTR void CmdCopyImage2(VkCommandBuffer commandBuffer, const VkCopyImageInfo2* pCopyImageInfo) { + GetData(commandBuffer).dispatch.CmdCopyImage2(commandBuffer, pCopyImageInfo); +} + +VKAPI_ATTR void CmdBlitImage2(VkCommandBuffer commandBuffer, const VkBlitImageInfo2* pBlitImageInfo) { + GetData(commandBuffer).dispatch.CmdBlitImage2(commandBuffer, pBlitImageInfo); +} + +VKAPI_ATTR void CmdCopyBufferToImage2(VkCommandBuffer commandBuffer, const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo) { + GetData(commandBuffer).dispatch.CmdCopyBufferToImage2(commandBuffer, pCopyBufferToImageInfo); +} + +VKAPI_ATTR void CmdCopyImageToBuffer2(VkCommandBuffer commandBuffer, const VkCopyImageToBufferInfo2* pCopyImageToBufferInfo) { + GetData(commandBuffer).dispatch.CmdCopyImageToBuffer2(commandBuffer, pCopyImageToBufferInfo); +} + +VKAPI_ATTR void CmdResolveImage2(VkCommandBuffer commandBuffer, const VkResolveImageInfo2* pResolveImageInfo) { + GetData(commandBuffer).dispatch.CmdResolveImage2(commandBuffer, pResolveImageInfo); +} + +VKAPI_ATTR void CmdSetEvent2(VkCommandBuffer commandBuffer, VkEvent event, const VkDependencyInfo* pDependencyInfo) { + GetData(commandBuffer).dispatch.CmdSetEvent2(commandBuffer, event, pDependencyInfo); +} + +VKAPI_ATTR void CmdResetEvent2(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags2 stageMask) { + GetData(commandBuffer).dispatch.CmdResetEvent2(commandBuffer, event, stageMask); +} + +VKAPI_ATTR void CmdWaitEvents2(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent* pEvents, const VkDependencyInfo* pDependencyInfos) { + GetData(commandBuffer).dispatch.CmdWaitEvents2(commandBuffer, eventCount, pEvents, pDependencyInfos); +} + +VKAPI_ATTR void CmdPipelineBarrier2(VkCommandBuffer commandBuffer, const VkDependencyInfo* pDependencyInfo) { + GetData(commandBuffer).dispatch.CmdPipelineBarrier2(commandBuffer, pDependencyInfo); +} + +VKAPI_ATTR VkResult QueueSubmit2(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2* pSubmits, VkFence fence) { + return GetData(queue).dispatch.QueueSubmit2(queue, submitCount, pSubmits, fence); +} + +VKAPI_ATTR void CmdWriteTimestamp2(VkCommandBuffer commandBuffer, VkPipelineStageFlags2 stage, VkQueryPool queryPool, uint32_t query) { + GetData(commandBuffer).dispatch.CmdWriteTimestamp2(commandBuffer, stage, queryPool, query); +} + +VKAPI_ATTR void CmdBeginRendering(VkCommandBuffer commandBuffer, const VkRenderingInfo* pRenderingInfo) { + GetData(commandBuffer).dispatch.CmdBeginRendering(commandBuffer, pRenderingInfo); +} + +VKAPI_ATTR void CmdEndRendering(VkCommandBuffer commandBuffer) { + GetData(commandBuffer).dispatch.CmdEndRendering(commandBuffer); +} + } // anonymous namespace @@ -2483,6 +2742,21 @@ VKAPI_ATTR void vkGetImageSparseMemoryRequirements2(VkDevice device, const VkIma } __attribute__((visibility("default"))) +VKAPI_ATTR void vkGetDeviceBufferMemoryRequirements(VkDevice device, const VkDeviceBufferMemoryRequirements* pInfo, VkMemoryRequirements2* pMemoryRequirements) { + vulkan::api::GetDeviceBufferMemoryRequirements(device, pInfo, pMemoryRequirements); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkGetDeviceImageMemoryRequirements(VkDevice device, const VkDeviceImageMemoryRequirements* pInfo, VkMemoryRequirements2* pMemoryRequirements) { + vulkan::api::GetDeviceImageMemoryRequirements(device, pInfo, pMemoryRequirements); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkGetDeviceImageSparseMemoryRequirements(VkDevice device, const VkDeviceImageMemoryRequirements* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements) { + vulkan::api::GetDeviceImageSparseMemoryRequirements(device, pInfo, pSparseMemoryRequirementCount, pSparseMemoryRequirements); +} + +__attribute__((visibility("default"))) VKAPI_ATTR VkResult vkCreateSamplerYcbcrConversion(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion) { return vulkan::api::CreateSamplerYcbcrConversion(device, pCreateInfo, pAllocator, pYcbcrConversion); } @@ -2572,4 +2846,174 @@ VKAPI_ATTR uint64_t vkGetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const return vulkan::api::GetDeviceMemoryOpaqueCaptureAddress(device, pInfo); } +__attribute__((visibility("default"))) +VKAPI_ATTR VkResult vkGetPhysicalDeviceToolProperties(VkPhysicalDevice physicalDevice, uint32_t* pToolCount, VkPhysicalDeviceToolProperties* pToolProperties) { + return vulkan::api::GetPhysicalDeviceToolProperties(physicalDevice, pToolCount, pToolProperties); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdSetCullMode(VkCommandBuffer commandBuffer, VkCullModeFlags cullMode) { + vulkan::api::CmdSetCullMode(commandBuffer, cullMode); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdSetFrontFace(VkCommandBuffer commandBuffer, VkFrontFace frontFace) { + vulkan::api::CmdSetFrontFace(commandBuffer, frontFace); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdSetPrimitiveTopology(VkCommandBuffer commandBuffer, VkPrimitiveTopology primitiveTopology) { + vulkan::api::CmdSetPrimitiveTopology(commandBuffer, primitiveTopology); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdSetViewportWithCount(VkCommandBuffer commandBuffer, uint32_t viewportCount, const VkViewport* pViewports) { + vulkan::api::CmdSetViewportWithCount(commandBuffer, viewportCount, pViewports); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdSetScissorWithCount(VkCommandBuffer commandBuffer, uint32_t scissorCount, const VkRect2D* pScissors) { + vulkan::api::CmdSetScissorWithCount(commandBuffer, scissorCount, pScissors); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdBindVertexBuffers2(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets, const VkDeviceSize* pSizes, const VkDeviceSize* pStrides) { + vulkan::api::CmdBindVertexBuffers2(commandBuffer, firstBinding, bindingCount, pBuffers, pOffsets, pSizes, pStrides); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdSetDepthTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthTestEnable) { + vulkan::api::CmdSetDepthTestEnable(commandBuffer, depthTestEnable); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdSetDepthWriteEnable(VkCommandBuffer commandBuffer, VkBool32 depthWriteEnable) { + vulkan::api::CmdSetDepthWriteEnable(commandBuffer, depthWriteEnable); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdSetDepthCompareOp(VkCommandBuffer commandBuffer, VkCompareOp depthCompareOp) { + vulkan::api::CmdSetDepthCompareOp(commandBuffer, depthCompareOp); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdSetDepthBoundsTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthBoundsTestEnable) { + vulkan::api::CmdSetDepthBoundsTestEnable(commandBuffer, depthBoundsTestEnable); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdSetStencilTestEnable(VkCommandBuffer commandBuffer, VkBool32 stencilTestEnable) { + vulkan::api::CmdSetStencilTestEnable(commandBuffer, stencilTestEnable); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdSetStencilOp(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, VkStencilOp failOp, VkStencilOp passOp, VkStencilOp depthFailOp, VkCompareOp compareOp) { + vulkan::api::CmdSetStencilOp(commandBuffer, faceMask, failOp, passOp, depthFailOp, compareOp); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdSetRasterizerDiscardEnable(VkCommandBuffer commandBuffer, VkBool32 rasterizerDiscardEnable) { + vulkan::api::CmdSetRasterizerDiscardEnable(commandBuffer, rasterizerDiscardEnable); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdSetDepthBiasEnable(VkCommandBuffer commandBuffer, VkBool32 depthBiasEnable) { + vulkan::api::CmdSetDepthBiasEnable(commandBuffer, depthBiasEnable); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdSetPrimitiveRestartEnable(VkCommandBuffer commandBuffer, VkBool32 primitiveRestartEnable) { + vulkan::api::CmdSetPrimitiveRestartEnable(commandBuffer, primitiveRestartEnable); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR VkResult vkCreatePrivateDataSlot(VkDevice device, const VkPrivateDataSlotCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPrivateDataSlot* pPrivateDataSlot) { + return vulkan::api::CreatePrivateDataSlot(device, pCreateInfo, pAllocator, pPrivateDataSlot); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkDestroyPrivateDataSlot(VkDevice device, VkPrivateDataSlot privateDataSlot, const VkAllocationCallbacks* pAllocator) { + vulkan::api::DestroyPrivateDataSlot(device, privateDataSlot, pAllocator); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR VkResult vkSetPrivateData(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t data) { + return vulkan::api::SetPrivateData(device, objectType, objectHandle, privateDataSlot, data); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkGetPrivateData(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t* pData) { + vulkan::api::GetPrivateData(device, objectType, objectHandle, privateDataSlot, pData); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdCopyBuffer2(VkCommandBuffer commandBuffer, const VkCopyBufferInfo2* pCopyBufferInfo) { + vulkan::api::CmdCopyBuffer2(commandBuffer, pCopyBufferInfo); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdCopyImage2(VkCommandBuffer commandBuffer, const VkCopyImageInfo2* pCopyImageInfo) { + vulkan::api::CmdCopyImage2(commandBuffer, pCopyImageInfo); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdBlitImage2(VkCommandBuffer commandBuffer, const VkBlitImageInfo2* pBlitImageInfo) { + vulkan::api::CmdBlitImage2(commandBuffer, pBlitImageInfo); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdCopyBufferToImage2(VkCommandBuffer commandBuffer, const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo) { + vulkan::api::CmdCopyBufferToImage2(commandBuffer, pCopyBufferToImageInfo); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdCopyImageToBuffer2(VkCommandBuffer commandBuffer, const VkCopyImageToBufferInfo2* pCopyImageToBufferInfo) { + vulkan::api::CmdCopyImageToBuffer2(commandBuffer, pCopyImageToBufferInfo); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdResolveImage2(VkCommandBuffer commandBuffer, const VkResolveImageInfo2* pResolveImageInfo) { + vulkan::api::CmdResolveImage2(commandBuffer, pResolveImageInfo); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdSetEvent2(VkCommandBuffer commandBuffer, VkEvent event, const VkDependencyInfo* pDependencyInfo) { + vulkan::api::CmdSetEvent2(commandBuffer, event, pDependencyInfo); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdResetEvent2(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags2 stageMask) { + vulkan::api::CmdResetEvent2(commandBuffer, event, stageMask); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdWaitEvents2(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent* pEvents, const VkDependencyInfo* pDependencyInfos) { + vulkan::api::CmdWaitEvents2(commandBuffer, eventCount, pEvents, pDependencyInfos); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdPipelineBarrier2(VkCommandBuffer commandBuffer, const VkDependencyInfo* pDependencyInfo) { + vulkan::api::CmdPipelineBarrier2(commandBuffer, pDependencyInfo); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR VkResult vkQueueSubmit2(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2* pSubmits, VkFence fence) { + return vulkan::api::QueueSubmit2(queue, submitCount, pSubmits, fence); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdWriteTimestamp2(VkCommandBuffer commandBuffer, VkPipelineStageFlags2 stage, VkQueryPool queryPool, uint32_t query) { + vulkan::api::CmdWriteTimestamp2(commandBuffer, stage, queryPool, query); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdBeginRendering(VkCommandBuffer commandBuffer, const VkRenderingInfo* pRenderingInfo) { + vulkan::api::CmdBeginRendering(commandBuffer, pRenderingInfo); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR void vkCmdEndRendering(VkCommandBuffer commandBuffer) { + vulkan::api::CmdEndRendering(commandBuffer); +} + // clang-format on diff --git a/vulkan/libvulkan/api_gen.h b/vulkan/libvulkan/api_gen.h index ad5cc34799..4998018882 100644 --- a/vulkan/libvulkan/api_gen.h +++ b/vulkan/libvulkan/api_gen.h @@ -60,6 +60,7 @@ struct InstanceDispatchTable { PFN_vkGetPhysicalDeviceExternalFenceProperties GetPhysicalDeviceExternalFenceProperties; PFN_vkEnumeratePhysicalDeviceGroups EnumeratePhysicalDeviceGroups; PFN_vkGetPhysicalDevicePresentRectanglesKHR GetPhysicalDevicePresentRectanglesKHR; + PFN_vkGetPhysicalDeviceToolProperties GetPhysicalDeviceToolProperties; // clang-format on }; @@ -207,6 +208,9 @@ struct DeviceDispatchTable { PFN_vkGetBufferMemoryRequirements2 GetBufferMemoryRequirements2; PFN_vkGetImageMemoryRequirements2 GetImageMemoryRequirements2; PFN_vkGetImageSparseMemoryRequirements2 GetImageSparseMemoryRequirements2; + PFN_vkGetDeviceBufferMemoryRequirements GetDeviceBufferMemoryRequirements; + PFN_vkGetDeviceImageMemoryRequirements GetDeviceImageMemoryRequirements; + PFN_vkGetDeviceImageSparseMemoryRequirements GetDeviceImageSparseMemoryRequirements; PFN_vkCreateSamplerYcbcrConversion CreateSamplerYcbcrConversion; PFN_vkDestroySamplerYcbcrConversion DestroySamplerYcbcrConversion; PFN_vkGetDeviceQueue2 GetDeviceQueue2; @@ -225,6 +229,39 @@ struct DeviceDispatchTable { PFN_vkGetBufferOpaqueCaptureAddress GetBufferOpaqueCaptureAddress; PFN_vkGetBufferDeviceAddress GetBufferDeviceAddress; PFN_vkGetDeviceMemoryOpaqueCaptureAddress GetDeviceMemoryOpaqueCaptureAddress; + PFN_vkCmdSetCullMode CmdSetCullMode; + PFN_vkCmdSetFrontFace CmdSetFrontFace; + PFN_vkCmdSetPrimitiveTopology CmdSetPrimitiveTopology; + PFN_vkCmdSetViewportWithCount CmdSetViewportWithCount; + PFN_vkCmdSetScissorWithCount CmdSetScissorWithCount; + PFN_vkCmdBindVertexBuffers2 CmdBindVertexBuffers2; + PFN_vkCmdSetDepthTestEnable CmdSetDepthTestEnable; + PFN_vkCmdSetDepthWriteEnable CmdSetDepthWriteEnable; + PFN_vkCmdSetDepthCompareOp CmdSetDepthCompareOp; + PFN_vkCmdSetDepthBoundsTestEnable CmdSetDepthBoundsTestEnable; + PFN_vkCmdSetStencilTestEnable CmdSetStencilTestEnable; + PFN_vkCmdSetStencilOp CmdSetStencilOp; + PFN_vkCmdSetRasterizerDiscardEnable CmdSetRasterizerDiscardEnable; + PFN_vkCmdSetDepthBiasEnable CmdSetDepthBiasEnable; + PFN_vkCmdSetPrimitiveRestartEnable CmdSetPrimitiveRestartEnable; + PFN_vkCreatePrivateDataSlot CreatePrivateDataSlot; + PFN_vkDestroyPrivateDataSlot DestroyPrivateDataSlot; + PFN_vkSetPrivateData SetPrivateData; + PFN_vkGetPrivateData GetPrivateData; + PFN_vkCmdCopyBuffer2 CmdCopyBuffer2; + PFN_vkCmdCopyImage2 CmdCopyImage2; + PFN_vkCmdBlitImage2 CmdBlitImage2; + PFN_vkCmdCopyBufferToImage2 CmdCopyBufferToImage2; + PFN_vkCmdCopyImageToBuffer2 CmdCopyImageToBuffer2; + PFN_vkCmdResolveImage2 CmdResolveImage2; + PFN_vkCmdSetEvent2 CmdSetEvent2; + PFN_vkCmdResetEvent2 CmdResetEvent2; + PFN_vkCmdWaitEvents2 CmdWaitEvents2; + PFN_vkCmdPipelineBarrier2 CmdPipelineBarrier2; + PFN_vkQueueSubmit2 QueueSubmit2; + PFN_vkCmdWriteTimestamp2 CmdWriteTimestamp2; + PFN_vkCmdBeginRendering CmdBeginRendering; + PFN_vkCmdEndRendering CmdEndRendering; // clang-format on }; diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp index cf774fd9b8..92250621ed 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -365,7 +365,7 @@ CreateInfoWrapper::CreateInfoWrapper(const VkInstanceCreateInfo& create_info, const VkAllocationCallbacks& allocator) : is_instance_(true), allocator_(allocator), - loader_api_version_(VK_API_VERSION_1_1), + loader_api_version_(VK_API_VERSION_1_3), icd_api_version_(icd_api_version), physical_dev_(VK_NULL_HANDLE), instance_info_(create_info), @@ -377,7 +377,7 @@ CreateInfoWrapper::CreateInfoWrapper(VkPhysicalDevice physical_dev, const VkAllocationCallbacks& allocator) : is_instance_(false), allocator_(allocator), - loader_api_version_(VK_API_VERSION_1_1), + loader_api_version_(VK_API_VERSION_1_3), icd_api_version_(icd_api_version), physical_dev_(physical_dev), dev_info_(create_info), @@ -519,6 +519,14 @@ VkResult CreateInfoWrapper::SanitizeExtensions() { is_instance_ ? loader_api_version_ : std::min(icd_api_version_, loader_api_version_); switch (api_version) { + case VK_API_VERSION_1_3: + hook_extensions_.set(ProcHook::EXTENSION_CORE_1_3); + hal_extensions_.set(ProcHook::EXTENSION_CORE_1_3); + [[clang::fallthrough]]; + case VK_API_VERSION_1_2: + hook_extensions_.set(ProcHook::EXTENSION_CORE_1_2); + hal_extensions_.set(ProcHook::EXTENSION_CORE_1_2); + [[clang::fallthrough]]; case VK_API_VERSION_1_1: hook_extensions_.set(ProcHook::EXTENSION_CORE_1_1); hal_extensions_.set(ProcHook::EXTENSION_CORE_1_1); @@ -653,6 +661,7 @@ void CreateInfoWrapper::FilterExtension(const char* name) { case ProcHook::EXTENSION_CORE_1_0: case ProcHook::EXTENSION_CORE_1_1: case ProcHook::EXTENSION_CORE_1_2: + case ProcHook::EXTENSION_CORE_1_3: case ProcHook::EXTENSION_COUNT: // Device and meta extensions. If we ever get here it's a bug in // our code. But enumerating them lets us avoid having a default @@ -707,6 +716,7 @@ void CreateInfoWrapper::FilterExtension(const char* name) { case ProcHook::EXTENSION_CORE_1_0: case ProcHook::EXTENSION_CORE_1_1: case ProcHook::EXTENSION_CORE_1_2: + case ProcHook::EXTENSION_CORE_1_3: case ProcHook::EXTENSION_COUNT: // Instance and meta extensions. If we ever get here it's a bug // in our code. But enumerating them lets us avoid having a @@ -1111,7 +1121,7 @@ VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo, if (result != VK_SUCCESS) return result; - icd_api_version ^= VK_VERSION_PATCH(icd_api_version); + icd_api_version ^= VK_API_VERSION_PATCH(icd_api_version); } CreateInfoWrapper wrapper(*pCreateInfo, icd_api_version, data_allocator); @@ -1195,7 +1205,7 @@ VkResult CreateDevice(VkPhysicalDevice physicalDevice, CreateInfoWrapper wrapper( physicalDevice, *pCreateInfo, - properties.apiVersion ^ VK_VERSION_PATCH(properties.apiVersion), + properties.apiVersion ^ VK_API_VERSION_PATCH(properties.apiVersion), data_allocator); VkResult result = wrapper.Validate(); if (result != VK_SUCCESS) diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h index 047e774004..819f6b211c 100644 --- a/vulkan/libvulkan/driver_gen.h +++ b/vulkan/libvulkan/driver_gen.h @@ -58,6 +58,7 @@ struct ProcHook { EXTENSION_CORE_1_0, EXTENSION_CORE_1_1, EXTENSION_CORE_1_2, + EXTENSION_CORE_1_3, EXTENSION_COUNT, EXTENSION_UNKNOWN, }; diff --git a/vulkan/libvulkan/libvulkan.map.txt b/vulkan/libvulkan/libvulkan.map.txt index df97d7fa78..f49e8f3e8d 100644 --- a/vulkan/libvulkan/libvulkan.map.txt +++ b/vulkan/libvulkan/libvulkan.map.txt @@ -11,20 +11,27 @@ LIBVULKAN { vkBindImageMemory; vkBindImageMemory2; # introduced=28 vkCmdBeginQuery; + vkCmdBeginRendering; # introduced=33 vkCmdBeginRenderPass; vkCmdBeginRenderPass2; # introduced=31 vkCmdBindDescriptorSets; vkCmdBindIndexBuffer; vkCmdBindPipeline; vkCmdBindVertexBuffers; + vkCmdBindVertexBuffers2; #introduced=33 vkCmdBlitImage; + vkCmdBlitImage2; #introduced=33 vkCmdClearAttachments; vkCmdClearColorImage; vkCmdClearDepthStencilImage; vkCmdCopyBuffer; + vkCmdCopyBuffer2; #introduced=33 vkCmdCopyBufferToImage; + vkCmdCopyBufferToImage2; #introduced=33 vkCmdCopyImage; + vkCmdCopyImage2; #introduced=33 vkCmdCopyImageToBuffer; + vkCmdCopyImageToBuffer2; #introduced=33 vkCmdCopyQueryPoolResults; vkCmdDispatch; vkCmdDispatchBase; # introduced=28 @@ -36,6 +43,7 @@ LIBVULKAN { vkCmdDrawIndirect; vkCmdDrawIndirectCount; # introduced=31 vkCmdEndQuery; + vkCmdEndRendering; #introduced=33 vkCmdEndRenderPass; vkCmdEndRenderPass2; # introduced=31 vkCmdExecuteCommands; @@ -43,24 +51,44 @@ LIBVULKAN { vkCmdNextSubpass; vkCmdNextSubpass2; # introduced=31 vkCmdPipelineBarrier; + vkCmdPipelineBarrier2; #introduced=33 vkCmdPushConstants; vkCmdResetEvent; + vkCmdResetEvent2; #introduced=33 vkCmdResetQueryPool; vkCmdResolveImage; + vkCmdResolveImage2; #introduced=33 vkCmdSetBlendConstants; + vkCmdSetCullMode; #introduced=33 vkCmdSetDepthBias; + vkCmdSetDepthBiasEnable; #introduced=33 vkCmdSetDepthBounds; + vkCmdSetDepthBoundsTestEnable; #introduced=33 + vkCmdSetDepthCompareOp; #introduced=33 + vkCmdSetDepthTestEnable; #introduced=33 + vkCmdSetDepthWriteEnable; #introduced=33 vkCmdSetDeviceMask; # introduced=28 vkCmdSetEvent; + vkCmdSetEvent2; #introduced=33 + vkCmdSetFrontFace; #introduced=33 vkCmdSetLineWidth; + vkCmdSetPrimitiveRestartEnable; #introduced=33 + vkCmdSetPrimitiveTopology; #introduced=33 + vkCmdSetRasterizerDiscardEnable; #introduced=33 vkCmdSetScissor; + vkCmdSetScissorWithCount; #introduced=33 vkCmdSetStencilCompareMask; + vkCmdSetStencilOp; #introduced=33 vkCmdSetStencilReference; + vkCmdSetStencilTestEnable; #introduced=33 vkCmdSetStencilWriteMask; vkCmdSetViewport; + vkCmdSetViewportWithCount; #introduced=33 vkCmdUpdateBuffer; vkCmdWaitEvents; + vkCmdWaitEvents2; #introduced=33 vkCmdWriteTimestamp; + vkCmdWriteTimestamp2; #introduced=33 vkCreateAndroidSurfaceKHR; vkCreateBuffer; vkCreateBufferView; @@ -79,6 +107,7 @@ LIBVULKAN { vkCreateInstance; vkCreatePipelineCache; vkCreatePipelineLayout; + vkCreatePrivateDataSlot; #introduced=33 vkCreateQueryPool; vkCreateRenderPass; vkCreateRenderPass2; # introduced=31 @@ -103,6 +132,7 @@ LIBVULKAN { vkDestroyPipeline; vkDestroyPipelineCache; vkDestroyPipelineLayout; + vkDestroyPrivateDataSlot; #introduced=33 vkDestroyQueryPool; vkDestroyRenderPass; vkDestroySampler; @@ -130,9 +160,12 @@ LIBVULKAN { vkGetBufferMemoryRequirements2; # introduced=28 vkGetBufferOpaqueCaptureAddress; # introduced=31 vkGetDescriptorSetLayoutSupport; # introduced=28 + vkGetDeviceBufferMemoryRequirements; #introduced=33 vkGetDeviceGroupPeerMemoryFeatures; # introduced=28 vkGetDeviceGroupPresentCapabilitiesKHR; # introduced=28 vkGetDeviceGroupSurfacePresentModesKHR; # introduced=28 + vkGetDeviceImageMemoryRequirements; #introduced=33 + vkGetDeviceImageSparseMemoryRequirements; #introduced=33 vkGetDeviceMemoryCommitment; vkGetDeviceMemoryOpaqueCaptureAddress; # introduced=31 vkGetDeviceProcAddr; @@ -169,7 +202,9 @@ LIBVULKAN { vkGetPhysicalDeviceSurfaceFormatsKHR; vkGetPhysicalDeviceSurfacePresentModesKHR; vkGetPhysicalDeviceSurfaceSupportKHR; + vkGetPhysicalDeviceToolProperties; #introduced=33 vkGetPipelineCacheData; + vkGetPrivateData; #introduced=33 vkGetQueryPoolResults; vkGetRenderAreaGranularity; vkGetSemaphoreCounterValue; # introduced=31 @@ -180,6 +215,7 @@ LIBVULKAN { vkQueueBindSparse; vkQueuePresentKHR; vkQueueSubmit; + vkQueueSubmit2; #introduced=33 vkQueueWaitIdle; vkResetCommandBuffer; vkResetCommandPool; @@ -188,6 +224,7 @@ LIBVULKAN { vkResetFences; vkResetQueryPool; # introduced=31 vkSetEvent; + vkSetPrivateData; # introduced=33 vkSignalSemaphore; # introduced=31 vkTrimCommandPool; # introduced=28 vkUnmapMemory; diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp index b94233b24a..3c91150d45 100644 --- a/vulkan/nulldrv/null_driver.cpp +++ b/vulkan/nulldrv/null_driver.cpp @@ -260,7 +260,7 @@ namespace null_driver { VKAPI_ATTR VkResult EnumerateInstanceVersion(uint32_t* pApiVersion) { - *pApiVersion = VK_API_VERSION_1_1; + *pApiVersion = VK_API_VERSION_1_3; return VK_SUCCESS; } @@ -397,8 +397,8 @@ VkResult EnumerateDeviceExtensionProperties(VkPhysicalDevice /*gpu*/, void GetPhysicalDeviceProperties(VkPhysicalDevice, VkPhysicalDeviceProperties* properties) { - properties->apiVersion = VK_MAKE_VERSION(1, 0, VK_HEADER_VERSION); - properties->driverVersion = VK_MAKE_VERSION(0, 0, 1); + properties->apiVersion = VK_MAKE_API_VERSION(0, 1, 2, VK_HEADER_VERSION); + properties->driverVersion = VK_MAKE_API_VERSION(0, 0, 0, 1); properties->vendorID = 0; properties->deviceID = 0; properties->deviceType = VK_PHYSICAL_DEVICE_TYPE_OTHER; @@ -1625,6 +1625,125 @@ uint64_t GetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemo return 0; } +void CmdBeginRendering(VkCommandBuffer commandBuffer, const VkRenderingInfo* pRenderingInfo) { +} + +void CmdEndRendering(VkCommandBuffer commandBuffer) { +} + +void CmdBindVertexBuffers2(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets, const VkDeviceSize* pSizes, const VkDeviceSize* pStrides) { +} + +void CmdBlitImage2(VkCommandBuffer commandBuffer, const VkBlitImageInfo2* pBlitImageInfo) { +} + +void CmdCopyBuffer2(VkCommandBuffer commandBuffer, const VkCopyBufferInfo2* pCopyBufferInfo) { +} + +void CmdCopyImage2(VkCommandBuffer commandBuffer, const VkCopyImageInfo2* pCopyImageInfo) { +} + +void CmdCopyBufferToImage2(VkCommandBuffer commandBuffer, const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo) { +} + +void CmdCopyImageToBuffer2(VkCommandBuffer commandBuffer, const VkCopyImageToBufferInfo2* pCopyImageToBufferInfo) { +} + +void CmdPipelineBarrier2(VkCommandBuffer commandBuffer, const VkDependencyInfo* pDependencyInfo) { +} + +void CmdResetEvent2(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags2 stageMask) { +} + +void CmdResolveImage2(VkCommandBuffer commandBuffer, const VkResolveImageInfo2* pResolveImageInfo) { +} + +void CmdSetCullMode(VkCommandBuffer commandBuffer, VkCullModeFlags cullMode) { +} + +void CmdSetDepthBiasEnable(VkCommandBuffer commandBuffer, VkBool32 depthBiasEnable) { +} + +void CmdSetDepthBoundsTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthBoundsTestEnable) { +} + +void CmdSetDepthCompareOp(VkCommandBuffer commandBuffer, VkCompareOp depthCompareOp) { +} + +void CmdSetDepthTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthTestEnable) { +} + +void CmdSetDepthWriteEnable(VkCommandBuffer commandBuffer, VkBool32 depthWriteEnable) { +} + +void CmdSetEvent2(VkCommandBuffer commandBuffer, VkEvent event, const VkDependencyInfo* pDependencyInfo) { +} + +void CmdSetFrontFace(VkCommandBuffer commandBuffer, VkFrontFace frontFace) { +} + +void CmdSetPrimitiveRestartEnable(VkCommandBuffer commandBuffer, VkBool32 primitiveRestartEnable) { +} + +void CmdSetPrimitiveTopology(VkCommandBuffer commandBuffer, VkPrimitiveTopology primitiveTopology) { +} + +void CmdSetRasterizerDiscardEnable(VkCommandBuffer commandBuffer, VkBool32 rasterizerDiscardEnable) { +} + +void CmdSetScissorWithCount(VkCommandBuffer commandBuffer, uint32_t scissorCount, const VkRect2D* pScissors) { +} + +void CmdSetStencilOp(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, VkStencilOp failOp, VkStencilOp passOp, VkStencilOp depthFailOp, VkCompareOp compareOp) { +} + +void CmdSetStencilTestEnable(VkCommandBuffer commandBuffer, VkBool32 stencilTestEnable) { +} + +void CmdSetViewportWithCount(VkCommandBuffer commandBuffer, uint32_t viewportCount, const VkViewport* pViewports) { +} + +void CmdWaitEvents2(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent* pEvents, const VkDependencyInfo* pDependencyInfos) { +} + +void CmdWriteTimestamp2(VkCommandBuffer commandBuffer, VkPipelineStageFlags2 stage, VkQueryPool queryPool, uint32_t query) { +} + +VkResult CreatePrivateDataSlot(VkDevice device, const VkPrivateDataSlotCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPrivateDataSlot* pPrivateDataSlot) { + ALOGV("TODO: vk%s", __FUNCTION__); + return VK_SUCCESS; +} + +void DestroyPrivateDataSlot(VkDevice device, VkPrivateDataSlot privateDataSlot, const VkAllocationCallbacks* pAllocator) { +} + +void GetDeviceBufferMemoryRequirements(VkDevice device, const VkDeviceBufferMemoryRequirements* pInfo, VkMemoryRequirements2* pMemoryRequirements) { +} + +void GetDeviceImageMemoryRequirements(VkDevice device, const VkDeviceImageMemoryRequirements* pInfo, VkMemoryRequirements2* pMemoryRequirements) { +} + +void GetDeviceImageSparseMemoryRequirements(VkDevice device, const VkDeviceImageMemoryRequirements* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements) { +} + +VkResult GetPhysicalDeviceToolProperties(VkPhysicalDevice physicalDevice, uint32_t* pToolCount, VkPhysicalDeviceToolProperties* pToolProperties) { + ALOGV("TODO: vk%s", __FUNCTION__); + return VK_SUCCESS; +} + +void GetPrivateData(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t* pData) { +} + +VkResult QueueSubmit2(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2* pSubmits, VkFence fence) { + ALOGV("TODO: vk%s", __FUNCTION__); + return VK_SUCCESS; +} + +VkResult SetPrivateData(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t data) { + ALOGV("TODO: vk%s", __FUNCTION__); + return VK_SUCCESS; +} + #pragma clang diagnostic pop // clang-format on diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp index edda12c6f9..f6dcf0900c 100644 --- a/vulkan/nulldrv/null_driver_gen.cpp +++ b/vulkan/nulldrv/null_driver_gen.cpp @@ -68,18 +68,25 @@ const NameProc kInstanceProcs[] = { {"vkCmdBeginQuery", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBeginQuery>(CmdBeginQuery))}, {"vkCmdBeginRenderPass", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBeginRenderPass>(CmdBeginRenderPass))}, {"vkCmdBeginRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBeginRenderPass2>(CmdBeginRenderPass2))}, + {"vkCmdBeginRendering", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBeginRendering>(CmdBeginRendering))}, {"vkCmdBindDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindDescriptorSets>(CmdBindDescriptorSets))}, {"vkCmdBindIndexBuffer", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindIndexBuffer>(CmdBindIndexBuffer))}, {"vkCmdBindPipeline", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindPipeline>(CmdBindPipeline))}, {"vkCmdBindVertexBuffers", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindVertexBuffers>(CmdBindVertexBuffers))}, + {"vkCmdBindVertexBuffers2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindVertexBuffers2>(CmdBindVertexBuffers2))}, {"vkCmdBlitImage", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBlitImage>(CmdBlitImage))}, + {"vkCmdBlitImage2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBlitImage2>(CmdBlitImage2))}, {"vkCmdClearAttachments", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdClearAttachments>(CmdClearAttachments))}, {"vkCmdClearColorImage", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdClearColorImage>(CmdClearColorImage))}, {"vkCmdClearDepthStencilImage", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdClearDepthStencilImage>(CmdClearDepthStencilImage))}, {"vkCmdCopyBuffer", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdCopyBuffer>(CmdCopyBuffer))}, + {"vkCmdCopyBuffer2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdCopyBuffer2>(CmdCopyBuffer2))}, {"vkCmdCopyBufferToImage", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdCopyBufferToImage>(CmdCopyBufferToImage))}, + {"vkCmdCopyBufferToImage2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdCopyBufferToImage2>(CmdCopyBufferToImage2))}, {"vkCmdCopyImage", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdCopyImage>(CmdCopyImage))}, + {"vkCmdCopyImage2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdCopyImage2>(CmdCopyImage2))}, {"vkCmdCopyImageToBuffer", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdCopyImageToBuffer>(CmdCopyImageToBuffer))}, + {"vkCmdCopyImageToBuffer2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdCopyImageToBuffer2>(CmdCopyImageToBuffer2))}, {"vkCmdCopyQueryPoolResults", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdCopyQueryPoolResults>(CmdCopyQueryPoolResults))}, {"vkCmdDispatch", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDispatch>(CmdDispatch))}, {"vkCmdDispatchBase", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDispatchBase>(CmdDispatchBase))}, @@ -93,29 +100,50 @@ const NameProc kInstanceProcs[] = { {"vkCmdEndQuery", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdEndQuery>(CmdEndQuery))}, {"vkCmdEndRenderPass", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdEndRenderPass>(CmdEndRenderPass))}, {"vkCmdEndRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdEndRenderPass2>(CmdEndRenderPass2))}, + {"vkCmdEndRendering", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdEndRendering>(CmdEndRendering))}, {"vkCmdExecuteCommands", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdExecuteCommands>(CmdExecuteCommands))}, {"vkCmdFillBuffer", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdFillBuffer>(CmdFillBuffer))}, {"vkCmdNextSubpass", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdNextSubpass>(CmdNextSubpass))}, {"vkCmdNextSubpass2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdNextSubpass2>(CmdNextSubpass2))}, {"vkCmdPipelineBarrier", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPipelineBarrier>(CmdPipelineBarrier))}, + {"vkCmdPipelineBarrier2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPipelineBarrier2>(CmdPipelineBarrier2))}, {"vkCmdPushConstants", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPushConstants>(CmdPushConstants))}, {"vkCmdResetEvent", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdResetEvent>(CmdResetEvent))}, + {"vkCmdResetEvent2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdResetEvent2>(CmdResetEvent2))}, {"vkCmdResetQueryPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdResetQueryPool>(CmdResetQueryPool))}, {"vkCmdResolveImage", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdResolveImage>(CmdResolveImage))}, + {"vkCmdResolveImage2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdResolveImage2>(CmdResolveImage2))}, {"vkCmdSetBlendConstants", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetBlendConstants>(CmdSetBlendConstants))}, + {"vkCmdSetCullMode", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetCullMode>(CmdSetCullMode))}, {"vkCmdSetDepthBias", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetDepthBias>(CmdSetDepthBias))}, + {"vkCmdSetDepthBiasEnable", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetDepthBiasEnable>(CmdSetDepthBiasEnable))}, {"vkCmdSetDepthBounds", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetDepthBounds>(CmdSetDepthBounds))}, + {"vkCmdSetDepthBoundsTestEnable", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetDepthBoundsTestEnable>(CmdSetDepthBoundsTestEnable))}, + {"vkCmdSetDepthCompareOp", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetDepthCompareOp>(CmdSetDepthCompareOp))}, + {"vkCmdSetDepthTestEnable", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetDepthTestEnable>(CmdSetDepthTestEnable))}, + {"vkCmdSetDepthWriteEnable", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetDepthWriteEnable>(CmdSetDepthWriteEnable))}, {"vkCmdSetDeviceMask", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetDeviceMask>(CmdSetDeviceMask))}, {"vkCmdSetEvent", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetEvent>(CmdSetEvent))}, + {"vkCmdSetEvent2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetEvent2>(CmdSetEvent2))}, + {"vkCmdSetFrontFace", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetFrontFace>(CmdSetFrontFace))}, {"vkCmdSetLineWidth", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetLineWidth>(CmdSetLineWidth))}, + {"vkCmdSetPrimitiveRestartEnable", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetPrimitiveRestartEnable>(CmdSetPrimitiveRestartEnable))}, + {"vkCmdSetPrimitiveTopology", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetPrimitiveTopology>(CmdSetPrimitiveTopology))}, + {"vkCmdSetRasterizerDiscardEnable", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetRasterizerDiscardEnable>(CmdSetRasterizerDiscardEnable))}, {"vkCmdSetScissor", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetScissor>(CmdSetScissor))}, + {"vkCmdSetScissorWithCount", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetScissorWithCount>(CmdSetScissorWithCount))}, {"vkCmdSetStencilCompareMask", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetStencilCompareMask>(CmdSetStencilCompareMask))}, + {"vkCmdSetStencilOp", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetStencilOp>(CmdSetStencilOp))}, {"vkCmdSetStencilReference", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetStencilReference>(CmdSetStencilReference))}, + {"vkCmdSetStencilTestEnable", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetStencilTestEnable>(CmdSetStencilTestEnable))}, {"vkCmdSetStencilWriteMask", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetStencilWriteMask>(CmdSetStencilWriteMask))}, {"vkCmdSetViewport", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetViewport>(CmdSetViewport))}, + {"vkCmdSetViewportWithCount", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetViewportWithCount>(CmdSetViewportWithCount))}, {"vkCmdUpdateBuffer", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdUpdateBuffer>(CmdUpdateBuffer))}, {"vkCmdWaitEvents", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdWaitEvents>(CmdWaitEvents))}, + {"vkCmdWaitEvents2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdWaitEvents2>(CmdWaitEvents2))}, {"vkCmdWriteTimestamp", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdWriteTimestamp>(CmdWriteTimestamp))}, + {"vkCmdWriteTimestamp2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdWriteTimestamp2>(CmdWriteTimestamp2))}, {"vkCreateBuffer", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateBuffer>(CreateBuffer))}, {"vkCreateBufferView", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateBufferView>(CreateBufferView))}, {"vkCreateCommandPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateCommandPool>(CreateCommandPool))}, @@ -134,6 +162,7 @@ const NameProc kInstanceProcs[] = { {"vkCreateInstance", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateInstance>(CreateInstance))}, {"vkCreatePipelineCache", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreatePipelineCache>(CreatePipelineCache))}, {"vkCreatePipelineLayout", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreatePipelineLayout>(CreatePipelineLayout))}, + {"vkCreatePrivateDataSlot", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreatePrivateDataSlot>(CreatePrivateDataSlot))}, {"vkCreateQueryPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateQueryPool>(CreateQueryPool))}, {"vkCreateRenderPass", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateRenderPass>(CreateRenderPass))}, {"vkCreateRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateRenderPass2>(CreateRenderPass2))}, @@ -159,6 +188,7 @@ const NameProc kInstanceProcs[] = { {"vkDestroyPipeline", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkDestroyPipeline>(DestroyPipeline))}, {"vkDestroyPipelineCache", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkDestroyPipelineCache>(DestroyPipelineCache))}, {"vkDestroyPipelineLayout", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkDestroyPipelineLayout>(DestroyPipelineLayout))}, + {"vkDestroyPrivateDataSlot", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkDestroyPrivateDataSlot>(DestroyPrivateDataSlot))}, {"vkDestroyQueryPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkDestroyQueryPool>(DestroyQueryPool))}, {"vkDestroyRenderPass", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkDestroyRenderPass>(DestroyRenderPass))}, {"vkDestroySampler", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkDestroySampler>(DestroySampler))}, @@ -183,7 +213,10 @@ const NameProc kInstanceProcs[] = { {"vkGetBufferMemoryRequirements2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetBufferMemoryRequirements2>(GetBufferMemoryRequirements2))}, {"vkGetBufferOpaqueCaptureAddress", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetBufferOpaqueCaptureAddress>(GetBufferOpaqueCaptureAddress))}, {"vkGetDescriptorSetLayoutSupport", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDescriptorSetLayoutSupport>(GetDescriptorSetLayoutSupport))}, + {"vkGetDeviceBufferMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceBufferMemoryRequirements>(GetDeviceBufferMemoryRequirements))}, {"vkGetDeviceGroupPeerMemoryFeatures", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceGroupPeerMemoryFeatures>(GetDeviceGroupPeerMemoryFeatures))}, + {"vkGetDeviceImageMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceImageMemoryRequirements>(GetDeviceImageMemoryRequirements))}, + {"vkGetDeviceImageSparseMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceImageSparseMemoryRequirements>(GetDeviceImageSparseMemoryRequirements))}, {"vkGetDeviceMemoryCommitment", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceMemoryCommitment>(GetDeviceMemoryCommitment))}, {"vkGetDeviceMemoryOpaqueCaptureAddress", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceMemoryOpaqueCaptureAddress>(GetDeviceMemoryOpaqueCaptureAddress))}, {"vkGetDeviceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceProcAddr>(GetDeviceProcAddr))}, @@ -221,7 +254,9 @@ const NameProc kInstanceProcs[] = { {"vkGetPhysicalDeviceSparseImageFormatProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceSparseImageFormatProperties>(GetPhysicalDeviceSparseImageFormatProperties))}, {"vkGetPhysicalDeviceSparseImageFormatProperties2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceSparseImageFormatProperties2>(GetPhysicalDeviceSparseImageFormatProperties2))}, {"vkGetPhysicalDeviceSparseImageFormatProperties2KHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR>(GetPhysicalDeviceSparseImageFormatProperties2KHR))}, + {"vkGetPhysicalDeviceToolProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceToolProperties>(GetPhysicalDeviceToolProperties))}, {"vkGetPipelineCacheData", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPipelineCacheData>(GetPipelineCacheData))}, + {"vkGetPrivateData", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPrivateData>(GetPrivateData))}, {"vkGetQueryPoolResults", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetQueryPoolResults>(GetQueryPoolResults))}, {"vkGetRenderAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetRenderAreaGranularity>(GetRenderAreaGranularity))}, {"vkGetSemaphoreCounterValue", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSemaphoreCounterValue>(GetSemaphoreCounterValue))}, @@ -233,6 +268,7 @@ const NameProc kInstanceProcs[] = { {"vkQueueBindSparse", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueBindSparse>(QueueBindSparse))}, {"vkQueueSignalReleaseImageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueSignalReleaseImageANDROID>(QueueSignalReleaseImageANDROID))}, {"vkQueueSubmit", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueSubmit>(QueueSubmit))}, + {"vkQueueSubmit2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueSubmit2>(QueueSubmit2))}, {"vkQueueWaitIdle", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueWaitIdle>(QueueWaitIdle))}, {"vkResetCommandBuffer", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkResetCommandBuffer>(ResetCommandBuffer))}, {"vkResetCommandPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkResetCommandPool>(ResetCommandPool))}, @@ -241,6 +277,7 @@ const NameProc kInstanceProcs[] = { {"vkResetFences", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkResetFences>(ResetFences))}, {"vkResetQueryPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkResetQueryPool>(ResetQueryPool))}, {"vkSetEvent", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkSetEvent>(SetEvent))}, + {"vkSetPrivateData", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkSetPrivateData>(SetPrivateData))}, {"vkSignalSemaphore", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkSignalSemaphore>(SignalSemaphore))}, {"vkTrimCommandPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkTrimCommandPool>(TrimCommandPool))}, {"vkUnmapMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkUnmapMemory>(UnmapMemory))}, diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h index e59cae925f..3e003e3189 100644 --- a/vulkan/nulldrv/null_driver_gen.h +++ b/vulkan/nulldrv/null_driver_gen.h @@ -200,6 +200,9 @@ VKAPI_ATTR void UpdateDescriptorSetWithTemplate(VkDevice device, VkDescriptorSet VKAPI_ATTR void GetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements); VKAPI_ATTR void GetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements); VKAPI_ATTR void GetImageSparseMemoryRequirements2(VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements); +VKAPI_ATTR void GetDeviceBufferMemoryRequirements(VkDevice device, const VkDeviceBufferMemoryRequirements* pInfo, VkMemoryRequirements2* pMemoryRequirements); +VKAPI_ATTR void GetDeviceImageMemoryRequirements(VkDevice device, const VkDeviceImageMemoryRequirements* pInfo, VkMemoryRequirements2* pMemoryRequirements); +VKAPI_ATTR void GetDeviceImageSparseMemoryRequirements(VkDevice device, const VkDeviceImageMemoryRequirements* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements); VKAPI_ATTR VkResult CreateSamplerYcbcrConversion(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion); VKAPI_ATTR void DestroySamplerYcbcrConversion(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue); @@ -220,6 +223,40 @@ VKAPI_ATTR void CmdDrawIndexedIndirectCount(VkCommandBuffer commandBuffer, VkBuf VKAPI_ATTR uint64_t GetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo); VKAPI_ATTR VkDeviceAddress GetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo); VKAPI_ATTR uint64_t GetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo); +VKAPI_ATTR VkResult GetPhysicalDeviceToolProperties(VkPhysicalDevice physicalDevice, uint32_t* pToolCount, VkPhysicalDeviceToolProperties* pToolProperties); +VKAPI_ATTR void CmdSetCullMode(VkCommandBuffer commandBuffer, VkCullModeFlags cullMode); +VKAPI_ATTR void CmdSetFrontFace(VkCommandBuffer commandBuffer, VkFrontFace frontFace); +VKAPI_ATTR void CmdSetPrimitiveTopology(VkCommandBuffer commandBuffer, VkPrimitiveTopology primitiveTopology); +VKAPI_ATTR void CmdSetViewportWithCount(VkCommandBuffer commandBuffer, uint32_t viewportCount, const VkViewport* pViewports); +VKAPI_ATTR void CmdSetScissorWithCount(VkCommandBuffer commandBuffer, uint32_t scissorCount, const VkRect2D* pScissors); +VKAPI_ATTR void CmdBindVertexBuffers2(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets, const VkDeviceSize* pSizes, const VkDeviceSize* pStrides); +VKAPI_ATTR void CmdSetDepthTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthTestEnable); +VKAPI_ATTR void CmdSetDepthWriteEnable(VkCommandBuffer commandBuffer, VkBool32 depthWriteEnable); +VKAPI_ATTR void CmdSetDepthCompareOp(VkCommandBuffer commandBuffer, VkCompareOp depthCompareOp); +VKAPI_ATTR void CmdSetDepthBoundsTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthBoundsTestEnable); +VKAPI_ATTR void CmdSetStencilTestEnable(VkCommandBuffer commandBuffer, VkBool32 stencilTestEnable); +VKAPI_ATTR void CmdSetStencilOp(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, VkStencilOp failOp, VkStencilOp passOp, VkStencilOp depthFailOp, VkCompareOp compareOp); +VKAPI_ATTR void CmdSetRasterizerDiscardEnable(VkCommandBuffer commandBuffer, VkBool32 rasterizerDiscardEnable); +VKAPI_ATTR void CmdSetDepthBiasEnable(VkCommandBuffer commandBuffer, VkBool32 depthBiasEnable); +VKAPI_ATTR void CmdSetPrimitiveRestartEnable(VkCommandBuffer commandBuffer, VkBool32 primitiveRestartEnable); +VKAPI_ATTR VkResult CreatePrivateDataSlot(VkDevice device, const VkPrivateDataSlotCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPrivateDataSlot* pPrivateDataSlot); +VKAPI_ATTR void DestroyPrivateDataSlot(VkDevice device, VkPrivateDataSlot privateDataSlot, const VkAllocationCallbacks* pAllocator); +VKAPI_ATTR VkResult SetPrivateData(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t data); +VKAPI_ATTR void GetPrivateData(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t* pData); +VKAPI_ATTR void CmdCopyBuffer2(VkCommandBuffer commandBuffer, const VkCopyBufferInfo2* pCopyBufferInfo); +VKAPI_ATTR void CmdCopyImage2(VkCommandBuffer commandBuffer, const VkCopyImageInfo2* pCopyImageInfo); +VKAPI_ATTR void CmdBlitImage2(VkCommandBuffer commandBuffer, const VkBlitImageInfo2* pBlitImageInfo); +VKAPI_ATTR void CmdCopyBufferToImage2(VkCommandBuffer commandBuffer, const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo); +VKAPI_ATTR void CmdCopyImageToBuffer2(VkCommandBuffer commandBuffer, const VkCopyImageToBufferInfo2* pCopyImageToBufferInfo); +VKAPI_ATTR void CmdResolveImage2(VkCommandBuffer commandBuffer, const VkResolveImageInfo2* pResolveImageInfo); +VKAPI_ATTR void CmdSetEvent2(VkCommandBuffer commandBuffer, VkEvent event, const VkDependencyInfo* pDependencyInfo); +VKAPI_ATTR void CmdResetEvent2(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags2 stageMask); +VKAPI_ATTR void CmdWaitEvents2(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent* pEvents, const VkDependencyInfo* pDependencyInfos); +VKAPI_ATTR void CmdPipelineBarrier2(VkCommandBuffer commandBuffer, const VkDependencyInfo* pDependencyInfo); +VKAPI_ATTR VkResult QueueSubmit2(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2* pSubmits, VkFence fence); +VKAPI_ATTR void CmdWriteTimestamp2(VkCommandBuffer commandBuffer, VkPipelineStageFlags2 stage, VkQueryPool queryPool, uint32_t query); +VKAPI_ATTR void CmdBeginRendering(VkCommandBuffer commandBuffer, const VkRenderingInfo* pRenderingInfo); +VKAPI_ATTR void CmdEndRendering(VkCommandBuffer commandBuffer); // clang-format on } // namespace null_driver diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc index 438e5dd10d..da6b00a0c6 100644 --- a/vulkan/vkjson/vkjson.cc +++ b/vulkan/vkjson/vkjson.cc @@ -387,6 +387,19 @@ struct EnumTraits<VkDriverIdKHR> { } }; +template <> +struct EnumTraits<VkShaderFloatControlsIndependence> { + static bool exist(uint32_t e) { + switch (e) { + case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY: + case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL: + case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE: + return true; + } + return false; + } +}; + // VkSparseImageFormatProperties template <typename Visitor> @@ -407,6 +420,7 @@ inline bool Iterate(Visitor* visitor, VkImageFormatProperties* properties) { visitor->Visit("maxResourceSize", &properties->maxResourceSize); } +// clang-format off template <typename Visitor> inline bool Iterate(Visitor* visitor, VkPhysicalDeviceLimits* limits) { return @@ -605,6 +619,200 @@ inline bool Iterate(Visitor* visitor, VkPhysicalDeviceFeatures* features) { } template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkJsonCore12* core) { + return + visitor->Visit("features", &core->features) && + visitor->Visit("properties", &core->properties); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkPhysicalDeviceVulkan12Properties* properties) { + return + visitor->Visit("driverID", &properties->driverID) && + visitor->Visit("driverName", &properties->driverName) && + visitor->Visit("driverInfo", &properties->driverInfo) && + visitor->Visit("conformanceVersion", &properties->conformanceVersion) && + visitor->Visit("denormBehaviorIndependence", &properties->denormBehaviorIndependence) && + visitor->Visit("roundingModeIndependence", &properties->roundingModeIndependence) && + visitor->Visit("shaderSignedZeroInfNanPreserveFloat16", &properties->shaderSignedZeroInfNanPreserveFloat16) && + visitor->Visit("shaderSignedZeroInfNanPreserveFloat32", &properties->shaderSignedZeroInfNanPreserveFloat32) && + visitor->Visit("shaderSignedZeroInfNanPreserveFloat64", &properties->shaderSignedZeroInfNanPreserveFloat64) && + visitor->Visit("shaderDenormPreserveFloat16", &properties->shaderDenormPreserveFloat16) && + visitor->Visit("shaderDenormPreserveFloat32", &properties->shaderDenormPreserveFloat32) && + visitor->Visit("shaderDenormPreserveFloat64", &properties->shaderDenormPreserveFloat64) && + visitor->Visit("shaderDenormFlushToZeroFloat16", &properties->shaderDenormFlushToZeroFloat16) && + visitor->Visit("shaderDenormFlushToZeroFloat32", &properties->shaderDenormFlushToZeroFloat32) && + visitor->Visit("shaderDenormFlushToZeroFloat64", &properties->shaderDenormFlushToZeroFloat64) && + visitor->Visit("shaderRoundingModeRTEFloat16", &properties->shaderRoundingModeRTEFloat16) && + visitor->Visit("shaderRoundingModeRTEFloat32", &properties->shaderRoundingModeRTEFloat32) && + visitor->Visit("shaderRoundingModeRTEFloat64", &properties->shaderRoundingModeRTEFloat64) && + visitor->Visit("shaderRoundingModeRTZFloat16", &properties->shaderRoundingModeRTZFloat16) && + visitor->Visit("shaderRoundingModeRTZFloat32", &properties->shaderRoundingModeRTZFloat32) && + visitor->Visit("shaderRoundingModeRTZFloat64", &properties->shaderRoundingModeRTZFloat64) && + visitor->Visit("maxUpdateAfterBindDescriptorsInAllPools", &properties->maxUpdateAfterBindDescriptorsInAllPools) && + visitor->Visit("shaderUniformBufferArrayNonUniformIndexingNative", &properties->shaderUniformBufferArrayNonUniformIndexingNative) && + visitor->Visit("shaderSampledImageArrayNonUniformIndexingNative", &properties->shaderSampledImageArrayNonUniformIndexingNative) && + visitor->Visit("shaderStorageBufferArrayNonUniformIndexingNative", &properties->shaderStorageBufferArrayNonUniformIndexingNative) && + visitor->Visit("shaderStorageImageArrayNonUniformIndexingNative", &properties->shaderStorageImageArrayNonUniformIndexingNative) && + visitor->Visit("shaderInputAttachmentArrayNonUniformIndexingNative", &properties->shaderInputAttachmentArrayNonUniformIndexingNative) && + visitor->Visit("robustBufferAccessUpdateAfterBind", &properties->robustBufferAccessUpdateAfterBind) && + visitor->Visit("quadDivergentImplicitLod", &properties->quadDivergentImplicitLod) && + visitor->Visit("maxPerStageDescriptorUpdateAfterBindSamplers", &properties->maxPerStageDescriptorUpdateAfterBindSamplers) && + visitor->Visit("maxPerStageDescriptorUpdateAfterBindUniformBuffers", &properties->maxPerStageDescriptorUpdateAfterBindUniformBuffers) && + visitor->Visit("maxPerStageDescriptorUpdateAfterBindStorageBuffers", &properties->maxPerStageDescriptorUpdateAfterBindStorageBuffers) && + visitor->Visit("maxPerStageDescriptorUpdateAfterBindSampledImages", &properties->maxPerStageDescriptorUpdateAfterBindSampledImages) && + visitor->Visit("maxPerStageDescriptorUpdateAfterBindStorageImages", &properties->maxPerStageDescriptorUpdateAfterBindStorageImages) && + visitor->Visit("maxPerStageDescriptorUpdateAfterBindInputAttachments", &properties->maxPerStageDescriptorUpdateAfterBindInputAttachments) && + visitor->Visit("maxPerStageUpdateAfterBindResources", &properties->maxPerStageUpdateAfterBindResources) && + visitor->Visit("maxDescriptorSetUpdateAfterBindSamplers", &properties->maxDescriptorSetUpdateAfterBindSamplers) && + visitor->Visit("maxDescriptorSetUpdateAfterBindUniformBuffers", &properties->maxDescriptorSetUpdateAfterBindUniformBuffers) && + visitor->Visit("maxDescriptorSetUpdateAfterBindUniformBuffersDynamic", &properties->maxDescriptorSetUpdateAfterBindUniformBuffersDynamic) && + visitor->Visit("maxDescriptorSetUpdateAfterBindStorageBuffers", &properties->maxDescriptorSetUpdateAfterBindStorageBuffers) && + visitor->Visit("maxDescriptorSetUpdateAfterBindStorageBuffersDynamic", &properties->maxDescriptorSetUpdateAfterBindStorageBuffersDynamic) && + visitor->Visit("maxDescriptorSetUpdateAfterBindSampledImages", &properties->maxDescriptorSetUpdateAfterBindSampledImages) && + visitor->Visit("maxDescriptorSetUpdateAfterBindStorageImages", &properties->maxDescriptorSetUpdateAfterBindStorageImages) && + visitor->Visit("maxDescriptorSetUpdateAfterBindInputAttachments", &properties->maxDescriptorSetUpdateAfterBindInputAttachments) && + visitor->Visit("supportedDepthResolveModes", &properties->supportedDepthResolveModes) && + visitor->Visit("supportedStencilResolveModes", &properties->supportedStencilResolveModes) && + visitor->Visit("independentResolveNone", &properties->independentResolveNone) && + visitor->Visit("independentResolve", &properties->independentResolve) && + visitor->Visit("filterMinmaxSingleComponentFormats", &properties->filterMinmaxSingleComponentFormats) && + visitor->Visit("filterMinmaxImageComponentMapping", &properties->filterMinmaxImageComponentMapping) && + visitor->Visit("maxTimelineSemaphoreValueDifference", &properties->maxTimelineSemaphoreValueDifference) && + visitor->Visit("framebufferIntegerColorSampleCounts", &properties->framebufferIntegerColorSampleCounts); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkPhysicalDeviceVulkan12Features* features) { + return + visitor->Visit("samplerMirrorClampToEdge", &features->samplerMirrorClampToEdge) && + visitor->Visit("drawIndirectCount", &features->drawIndirectCount) && + visitor->Visit("storageBuffer8BitAccess", &features->storageBuffer8BitAccess) && + visitor->Visit("uniformAndStorageBuffer8BitAccess", &features->uniformAndStorageBuffer8BitAccess) && + visitor->Visit("storagePushConstant8", &features->storagePushConstant8) && + visitor->Visit("shaderBufferInt64Atomics", &features->shaderBufferInt64Atomics) && + visitor->Visit("shaderSharedInt64Atomics", &features->shaderSharedInt64Atomics) && + visitor->Visit("shaderFloat16", &features->shaderFloat16) && + visitor->Visit("shaderInt8", &features->shaderInt8) && + visitor->Visit("descriptorIndexing", &features->descriptorIndexing) && + visitor->Visit("shaderInputAttachmentArrayDynamicIndexing", &features->shaderInputAttachmentArrayDynamicIndexing) && + visitor->Visit("shaderUniformTexelBufferArrayDynamicIndexing", &features->shaderUniformTexelBufferArrayDynamicIndexing) && + visitor->Visit("shaderStorageTexelBufferArrayDynamicIndexing", &features->shaderStorageTexelBufferArrayDynamicIndexing) && + visitor->Visit("shaderUniformBufferArrayNonUniformIndexing", &features->shaderUniformBufferArrayNonUniformIndexing) && + visitor->Visit("shaderSampledImageArrayNonUniformIndexing", &features->shaderSampledImageArrayNonUniformIndexing) && + visitor->Visit("shaderStorageBufferArrayNonUniformIndexing", &features->shaderStorageBufferArrayNonUniformIndexing) && + visitor->Visit("shaderStorageImageArrayNonUniformIndexing", &features->shaderStorageImageArrayNonUniformIndexing) && + visitor->Visit("shaderInputAttachmentArrayNonUniformIndexing", &features->shaderInputAttachmentArrayNonUniformIndexing) && + visitor->Visit("shaderUniformTexelBufferArrayNonUniformIndexing", &features->shaderUniformTexelBufferArrayNonUniformIndexing) && + visitor->Visit("shaderStorageTexelBufferArrayNonUniformIndexing", &features->shaderStorageTexelBufferArrayNonUniformIndexing) && + visitor->Visit("descriptorBindingUniformBufferUpdateAfterBind", &features->descriptorBindingUniformBufferUpdateAfterBind) && + visitor->Visit("descriptorBindingSampledImageUpdateAfterBind", &features->descriptorBindingSampledImageUpdateAfterBind) && + visitor->Visit("descriptorBindingStorageImageUpdateAfterBind", &features->descriptorBindingStorageImageUpdateAfterBind) && + visitor->Visit("descriptorBindingStorageBufferUpdateAfterBind", &features->descriptorBindingStorageBufferUpdateAfterBind) && + visitor->Visit("descriptorBindingUniformTexelBufferUpdateAfterBind", &features->descriptorBindingUniformTexelBufferUpdateAfterBind) && + visitor->Visit("descriptorBindingStorageTexelBufferUpdateAfterBind", &features->descriptorBindingStorageTexelBufferUpdateAfterBind) && + visitor->Visit("descriptorBindingUpdateUnusedWhilePending", &features->descriptorBindingUpdateUnusedWhilePending) && + visitor->Visit("descriptorBindingPartiallyBound", &features->descriptorBindingPartiallyBound) && + visitor->Visit("descriptorBindingVariableDescriptorCount", &features->descriptorBindingVariableDescriptorCount) && + visitor->Visit("runtimeDescriptorArray", &features->runtimeDescriptorArray) && + visitor->Visit("samplerFilterMinmax", &features->samplerFilterMinmax) && + visitor->Visit("scalarBlockLayout", &features->scalarBlockLayout) && + visitor->Visit("imagelessFramebuffer", &features->imagelessFramebuffer) && + visitor->Visit("uniformBufferStandardLayout", &features->uniformBufferStandardLayout) && + visitor->Visit("shaderSubgroupExtendedTypes", &features->shaderSubgroupExtendedTypes) && + visitor->Visit("separateDepthStencilLayouts", &features->separateDepthStencilLayouts) && + visitor->Visit("hostQueryReset", &features->hostQueryReset) && + visitor->Visit("timelineSemaphore", &features->timelineSemaphore) && + visitor->Visit("bufferDeviceAddress", &features->bufferDeviceAddress) && + visitor->Visit("bufferDeviceAddressCaptureReplay", &features->bufferDeviceAddressCaptureReplay) && + visitor->Visit("bufferDeviceAddressMultiDevice", &features->bufferDeviceAddressMultiDevice) && + visitor->Visit("vulkanMemoryModel", &features->vulkanMemoryModel) && + visitor->Visit("vulkanMemoryModelDeviceScope", &features->vulkanMemoryModelDeviceScope) && + visitor->Visit("vulkanMemoryModelAvailabilityVisibilityChains", &features->vulkanMemoryModelAvailabilityVisibilityChains) && + visitor->Visit("shaderOutputViewportIndex", &features->shaderOutputViewportIndex) && + visitor->Visit("shaderOutputLayer", &features->shaderOutputLayer) && + visitor->Visit("shaderOutputLayer", &features->shaderOutputLayer); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkJsonCore13* core) { + return + visitor->Visit("features", &core->features) && + visitor->Visit("properties", &core->properties); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkPhysicalDeviceVulkan13Properties* properties) { + return + visitor->Visit("minSubgroupSize", &properties->minSubgroupSize) && + visitor->Visit("maxSubgroupSize", &properties->maxSubgroupSize) && + visitor->Visit("maxComputeWorkgroupSubgroups", &properties->maxComputeWorkgroupSubgroups) && + visitor->Visit("requiredSubgroupSizeStages", &properties->requiredSubgroupSizeStages) && + visitor->Visit("maxInlineUniformBlockSize", &properties->maxInlineUniformBlockSize) && + visitor->Visit("maxPerStageDescriptorInlineUniformBlocks", &properties->maxPerStageDescriptorInlineUniformBlocks) && + visitor->Visit("maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks", &properties->maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks) && + visitor->Visit("maxDescriptorSetInlineUniformBlocks", &properties->maxDescriptorSetInlineUniformBlocks) && + visitor->Visit("maxDescriptorSetUpdateAfterBindInlineUniformBlocks", &properties->maxDescriptorSetUpdateAfterBindInlineUniformBlocks) && + visitor->Visit("maxInlineUniformTotalSize", &properties->maxInlineUniformTotalSize) && + visitor->Visit("integerDotProduct8BitUnsignedAccelerated", &properties->integerDotProduct8BitUnsignedAccelerated) && + visitor->Visit("integerDotProduct8BitSignedAccelerated", &properties->integerDotProduct8BitSignedAccelerated) && + visitor->Visit("integerDotProduct8BitMixedSignednessAccelerated", &properties->integerDotProduct8BitMixedSignednessAccelerated) && + visitor->Visit("integerDotProduct4x8BitPackedUnsignedAccelerated", &properties->integerDotProduct4x8BitPackedUnsignedAccelerated) && + visitor->Visit("integerDotProduct4x8BitPackedSignedAccelerated", &properties->integerDotProduct4x8BitPackedSignedAccelerated) && + visitor->Visit("integerDotProduct4x8BitPackedMixedSignednessAccelerated", &properties->integerDotProduct4x8BitPackedMixedSignednessAccelerated) && + visitor->Visit("integerDotProduct16BitUnsignedAccelerated", &properties->integerDotProduct16BitUnsignedAccelerated) && + visitor->Visit("integerDotProduct16BitSignedAccelerated", &properties->integerDotProduct16BitSignedAccelerated) && + visitor->Visit("integerDotProduct16BitMixedSignednessAccelerated", &properties->integerDotProduct16BitMixedSignednessAccelerated) && + visitor->Visit("integerDotProduct32BitUnsignedAccelerated", &properties->integerDotProduct32BitUnsignedAccelerated) && + visitor->Visit("integerDotProduct32BitSignedAccelerated", &properties->integerDotProduct32BitSignedAccelerated) && + visitor->Visit("integerDotProduct32BitMixedSignednessAccelerated", &properties->integerDotProduct32BitMixedSignednessAccelerated) && + visitor->Visit("integerDotProduct64BitUnsignedAccelerated", &properties->integerDotProduct64BitUnsignedAccelerated) && + visitor->Visit("integerDotProduct64BitSignedAccelerated", &properties->integerDotProduct64BitSignedAccelerated) && + visitor->Visit("integerDotProduct64BitMixedSignednessAccelerated", &properties->integerDotProduct64BitMixedSignednessAccelerated) && + visitor->Visit("integerDotProductAccumulatingSaturating8BitUnsignedAccelerated", &properties->integerDotProductAccumulatingSaturating8BitUnsignedAccelerated) && + visitor->Visit("integerDotProductAccumulatingSaturating8BitSignedAccelerated", &properties->integerDotProductAccumulatingSaturating8BitSignedAccelerated) && + visitor->Visit("integerDotProductAccumulatingSaturating8BitMixedSignednessAccelerated", &properties->integerDotProductAccumulatingSaturating8BitMixedSignednessAccelerated) && + visitor->Visit("integerDotProductAccumulatingSaturating4x8BitPackedUnsignedAccelerated", &properties->integerDotProductAccumulatingSaturating4x8BitPackedUnsignedAccelerated) && + visitor->Visit("integerDotProductAccumulatingSaturating4x8BitPackedSignedAccelerated", &properties->integerDotProductAccumulatingSaturating4x8BitPackedSignedAccelerated) && + visitor->Visit("integerDotProductAccumulatingSaturating4x8BitPackedMixedSignednessAccelerated", &properties->integerDotProductAccumulatingSaturating4x8BitPackedMixedSignednessAccelerated) && + visitor->Visit("integerDotProductAccumulatingSaturating16BitUnsignedAccelerated", &properties->integerDotProductAccumulatingSaturating16BitUnsignedAccelerated) && + visitor->Visit("integerDotProductAccumulatingSaturating16BitSignedAccelerated", &properties->integerDotProductAccumulatingSaturating16BitSignedAccelerated) && + visitor->Visit("integerDotProductAccumulatingSaturating16BitMixedSignednessAccelerated", &properties->integerDotProductAccumulatingSaturating16BitMixedSignednessAccelerated) && + visitor->Visit("integerDotProductAccumulatingSaturating32BitUnsignedAccelerated", &properties->integerDotProductAccumulatingSaturating32BitUnsignedAccelerated) && + visitor->Visit("integerDotProductAccumulatingSaturating32BitSignedAccelerated", &properties->integerDotProductAccumulatingSaturating32BitSignedAccelerated) && + visitor->Visit("integerDotProductAccumulatingSaturating32BitMixedSignednessAccelerated", &properties->integerDotProductAccumulatingSaturating32BitMixedSignednessAccelerated) && + visitor->Visit("integerDotProductAccumulatingSaturating64BitUnsignedAccelerated", &properties->integerDotProductAccumulatingSaturating64BitUnsignedAccelerated) && + visitor->Visit("integerDotProductAccumulatingSaturating64BitSignedAccelerated", &properties->integerDotProductAccumulatingSaturating64BitSignedAccelerated) && + visitor->Visit("integerDotProductAccumulatingSaturating64BitMixedSignednessAccelerated", &properties->integerDotProductAccumulatingSaturating64BitMixedSignednessAccelerated) && + visitor->Visit("storageTexelBufferOffsetAlignmentBytes", &properties->storageTexelBufferOffsetAlignmentBytes) && + visitor->Visit("storageTexelBufferOffsetSingleTexelAlignment", &properties->storageTexelBufferOffsetSingleTexelAlignment) && + visitor->Visit("uniformTexelBufferOffsetAlignmentBytes", &properties->uniformTexelBufferOffsetAlignmentBytes) && + visitor->Visit("uniformTexelBufferOffsetSingleTexelAlignment", &properties->uniformTexelBufferOffsetSingleTexelAlignment) && + visitor->Visit("maxBufferSize", &properties->maxBufferSize); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkPhysicalDeviceVulkan13Features* features) { + return + visitor->Visit("robustImageAccess", &features->robustImageAccess) && + visitor->Visit("inlineUniformBlock", &features->inlineUniformBlock) && + visitor->Visit("descriptorBindingInlineUniformBlockUpdateAfterBind", &features->descriptorBindingInlineUniformBlockUpdateAfterBind) && + visitor->Visit("pipelineCreationCacheControl", &features->pipelineCreationCacheControl) && + visitor->Visit("privateData", &features->privateData) && + visitor->Visit("shaderDemoteToHelperInvocation", &features->shaderDemoteToHelperInvocation) && + visitor->Visit("shaderTerminateInvocation", &features->shaderTerminateInvocation) && + visitor->Visit("subgroupSizeControl", &features->subgroupSizeControl) && + visitor->Visit("computeFullSubgroups", &features->computeFullSubgroups) && + visitor->Visit("synchronization2", &features->synchronization2) && + visitor->Visit("textureCompressionASTC_HDR", &features->textureCompressionASTC_HDR) && + visitor->Visit("shaderZeroInitializeWorkgroupMemory", &features->shaderZeroInitializeWorkgroupMemory) && + visitor->Visit("dynamicRendering", &features->dynamicRendering) && + visitor->Visit("shaderIntegerDotProduct", &features->shaderIntegerDotProduct) && + visitor->Visit("maintenance4", &features->maintenance4); +} +// clang-format on + +template <typename Visitor> inline bool Iterate(Visitor* visitor, VkJsonExtDriverProperties* properties) { return visitor->Visit("driverPropertiesKHR", @@ -841,8 +1049,12 @@ template <typename Visitor> inline bool Iterate(Visitor* visitor, VkJsonDevice* device) { bool ret = true; switch (device->properties.apiVersion ^ - VK_VERSION_PATCH(device->properties.apiVersion)) { + VK_API_VERSION_PATCH(device->properties.apiVersion)) { + case VK_API_VERSION_1_3: + ret &= visitor->Visit("core13", &device->core13); + FALLTHROUGH_INTENDED; case VK_API_VERSION_1_2: + ret &= visitor->Visit("core12", &device->core12); FALLTHROUGH_INTENDED; case VK_API_VERSION_1_1: ret &= @@ -897,16 +1109,22 @@ inline bool Iterate(Visitor* visitor, VkJsonDevice* device) { template <typename Visitor> inline bool Iterate(Visitor* visitor, VkJsonInstance* instance) { bool ret = true; - switch (instance->api_version ^ VK_VERSION_PATCH(instance->api_version)) { + switch (instance->api_version ^ VK_API_VERSION_PATCH(instance->api_version)) { + case VK_API_VERSION_1_3: + FALLTHROUGH_INTENDED; case VK_API_VERSION_1_2: + ret &= visitor->Visit("apiVersion", &instance->api_version); FALLTHROUGH_INTENDED; case VK_API_VERSION_1_1: ret &= visitor->Visit("deviceGroups", &instance->device_groups); FALLTHROUGH_INTENDED; case VK_API_VERSION_1_0: + char depString[] = + "vkjson is deprecated, and will be replaced in a future release"; ret &= visitor->Visit("layers", &instance->layers) && visitor->Visit("extensions", &instance->extensions) && - visitor->Visit("devices", &instance->devices); + visitor->Visit("devices", &instance->devices) && + visitor->Visit("_comment", &depString); } return ret; } diff --git a/vulkan/vkjson/vkjson.h b/vulkan/vkjson/vkjson.h index 52e7bee288..88f6e7de53 100644 --- a/vulkan/vkjson/vkjson.h +++ b/vulkan/vkjson/vkjson.h @@ -33,14 +33,6 @@ #undef max #endif -#ifndef VK_API_VERSION_1_0 -#define VK_API_VERSION_1_0 VK_MAKE_VERSION(1, 0, 0) -#endif - -#ifndef VK_API_VERSION_1_1 -#define VK_API_VERSION_1_1 VK_MAKE_VERSION(1, 1, 0) -#endif - /* * Annotation to tell clang that we intend to fall through from one case to * another in a switch. Sourced from android-base/macros.h. @@ -82,6 +74,16 @@ struct VkJsonExtShaderFloat16Int8Features { VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shader_float16_int8_features_khr; }; +struct VkJsonCore12 { + VkPhysicalDeviceVulkan12Properties properties; + VkPhysicalDeviceVulkan12Features features; +}; + +struct VkJsonCore13 { + VkPhysicalDeviceVulkan13Properties properties; + VkPhysicalDeviceVulkan13Features features; +}; + struct VkJsonDevice { VkJsonDevice() { memset(&properties, 0, sizeof(VkPhysicalDeviceProperties)); @@ -106,6 +108,8 @@ struct VkJsonDevice { sizeof(VkPhysicalDeviceSamplerYcbcrConversionFeatures)); memset(&shader_draw_parameter_features, 0, sizeof(VkPhysicalDeviceShaderDrawParameterFeatures)); + memset(&core12, 0, sizeof(VkJsonCore12)); + memset(&core13, 0, sizeof(VkJsonCore13)); } VkPhysicalDeviceProperties properties; VkPhysicalDeviceFeatures features; @@ -133,6 +137,8 @@ struct VkJsonDevice { external_fence_properties; std::map<VkExternalSemaphoreHandleTypeFlagBits, VkExternalSemaphoreProperties> external_semaphore_properties; + VkJsonCore12 core12; + VkJsonCore13 core13; }; struct VkJsonDeviceGroup { diff --git a/vulkan/vkjson/vkjson_instance.cc b/vulkan/vkjson/vkjson_instance.cc index 587249539f..0ffe7e0fdf 100644 --- a/vulkan/vkjson/vkjson_instance.cc +++ b/vulkan/vkjson/vkjson_instance.cc @@ -157,76 +157,64 @@ VkJsonDevice VkJsonGetDevice(VkPhysicalDevice physical_device) { } } - VkPhysicalDeviceProperties2 properties2 = { - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, - nullptr, - {}, - }; - device.subgroup_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES; - device.subgroup_properties.pNext = properties2.pNext; - properties2.pNext = &device.subgroup_properties; + device.subgroup_properties.pNext = properties.pNext; + properties.pNext = &device.subgroup_properties; device.point_clipping_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES; - device.point_clipping_properties.pNext = properties2.pNext; - properties2.pNext = &device.point_clipping_properties; + device.point_clipping_properties.pNext = properties.pNext; + properties.pNext = &device.point_clipping_properties; device.multiview_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES; - device.multiview_properties.pNext = properties2.pNext; - properties2.pNext = &device.multiview_properties; + device.multiview_properties.pNext = properties.pNext; + properties.pNext = &device.multiview_properties; device.id_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES; - device.id_properties.pNext = properties2.pNext; - properties2.pNext = &device.id_properties; + device.id_properties.pNext = properties.pNext; + properties.pNext = &device.id_properties; device.maintenance3_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES; - device.maintenance3_properties.pNext = properties2.pNext; - properties2.pNext = &device.maintenance3_properties; - - vkGetPhysicalDeviceProperties2(physical_device, &properties2); + device.maintenance3_properties.pNext = properties.pNext; + properties.pNext = &device.maintenance3_properties; - VkPhysicalDeviceFeatures2 features2 = { - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, - nullptr, - {}, - }; + vkGetPhysicalDeviceProperties2(physical_device, &properties); device.bit16_storage_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES; - device.bit16_storage_features.pNext = features2.pNext; - features2.pNext = &device.bit16_storage_features; + device.bit16_storage_features.pNext = features.pNext; + features.pNext = &device.bit16_storage_features; device.multiview_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES; - device.multiview_features.pNext = features2.pNext; - features2.pNext = &device.multiview_features; + device.multiview_features.pNext = features.pNext; + features.pNext = &device.multiview_features; device.variable_pointer_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES; - device.variable_pointer_features.pNext = features2.pNext; - features2.pNext = &device.variable_pointer_features; + device.variable_pointer_features.pNext = features.pNext; + features.pNext = &device.variable_pointer_features; device.protected_memory_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES; - device.protected_memory_features.pNext = features2.pNext; - features2.pNext = &device.protected_memory_features; + device.protected_memory_features.pNext = features.pNext; + features.pNext = &device.protected_memory_features; device.sampler_ycbcr_conversion_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES; - device.sampler_ycbcr_conversion_features.pNext = features2.pNext; - features2.pNext = &device.sampler_ycbcr_conversion_features; + device.sampler_ycbcr_conversion_features.pNext = features.pNext; + features.pNext = &device.sampler_ycbcr_conversion_features; device.shader_draw_parameter_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES; - device.shader_draw_parameter_features.pNext = features2.pNext; - features2.pNext = &device.shader_draw_parameter_features; + device.shader_draw_parameter_features.pNext = features.pNext; + features.pNext = &device.shader_draw_parameter_features; - vkGetPhysicalDeviceFeatures2(physical_device, &features2); + vkGetPhysicalDeviceFeatures2(physical_device, &features); VkPhysicalDeviceExternalFenceInfo external_fence_info = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO, nullptr, @@ -272,6 +260,38 @@ VkJsonDevice VkJsonGetDevice(VkPhysicalDevice physical_device) { } } + if (device.properties.apiVersion >= VK_API_VERSION_1_2) { + device.core12.properties.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES; + device.core12.properties.pNext = properties.pNext; + properties.pNext = &device.core12.properties; + + vkGetPhysicalDeviceProperties2(physical_device, &properties); + + device.core12.features.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; + device.core12.features.pNext = features.pNext; + features.pNext = &device.core12.features; + + vkGetPhysicalDeviceFeatures2(physical_device, &features); + } + + if (device.properties.apiVersion >= VK_API_VERSION_1_3) { + device.core13.properties.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_PROPERTIES; + device.core13.properties.pNext = properties.pNext; + properties.pNext = &device.core13.properties; + + vkGetPhysicalDeviceProperties2(physical_device, &properties); + + device.core13.features.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES; + device.core13.features.pNext = features.pNext; + features.pNext = &device.core13.features; + + vkGetPhysicalDeviceFeatures2(physical_device, &features); + } + return device; } |