Merge "CompositionEngine: fix memory leak in unit tests"
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index 0712c0a..28e5ee2 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -240,11 +240,11 @@
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 677d6c7..49c1318 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -61,6 +61,10 @@
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 b6f42ad..c796da6 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -244,7 +244,7 @@
// 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 @@
*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 c4ecd07..0f8a732 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -829,7 +829,7 @@
* 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 0000000..c746f7f
--- /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 396ae9d..0000000
--- 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 fe417a3..d5ca725 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 80c0548..32922ca 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -47,6 +47,14 @@
}
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 0000000..e01f132
--- /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 1be3a69..5003151 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -119,6 +119,12 @@
}
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 {
+ 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 0000000..6a43d5d
--- /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 0000000..4c6755c
--- /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 0000000..ece58c9
--- /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 0000000..45c6547
--- /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 0000000..2ca8364
--- /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 0000000..4ecea7b
--- /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 7481a23..cb18837 100644
--- a/headers/Android.bp
+++ b/headers/Android.bp
@@ -28,6 +28,11 @@
"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 6704a1d..35f87f9 100644
--- a/include/android/bitmap.h
+++ b/include/android/bitmap.h
@@ -68,6 +68,8 @@
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 6f579ca..98f0eec 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -39,6 +39,12 @@
*/
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 @@
/**
* 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 fbd61b5..e6ad943f 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -877,6 +877,7 @@
* 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 0000000..5fa47f6
--- /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 45e8afc..7e86d3f 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -263,6 +263,44 @@
* 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 @@
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 @@
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 3a13104..9a36ecb 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 @@
__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 ae70831..932af2d 100644
--- a/include/ftl/Flags.h
+++ b/include/ftl/Flags.h
@@ -209,12 +209,12 @@
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
similarity index 96%
rename from include/ftl/array_traits.h
rename to include/ftl/details/array_traits.h
index 1265fa1..16e63ec 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 @@
}
};
-} // namespace android::ftl
+} // namespace android::ftl::details
diff --git a/include/ftl/enum.h b/include/ftl/enum.h
index dfe3a09..5234c05 100644
--- a/include/ftl/enum.h
+++ b/include/ftl/enum.h
@@ -87,11 +87,13 @@
// 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 @@
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 @@
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 @@
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 @@
//
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 @@
//
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 @@
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 @@
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 65a9536..03587e3 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 @@
// 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 @@
// 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 cd7b92a..b7f8c29 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 @@
// 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 f4147a0..e421dee 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -164,7 +164,7 @@
* (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 @@
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 22aae19..c4f03c9 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -235,7 +235,7 @@
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 edcb615..5f9a37d 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -500,24 +500,6 @@
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 5832bf4..f27f5f1 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 bb40f51..41b3460 100644
--- a/libs/arect/Android.bp
+++ b/libs/arect/Android.bp
@@ -57,4 +57,11 @@
},
},
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 81e61da..ea2f8d2 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 @@
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 @@
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::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 6286c9c..4ddbce7 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -310,9 +310,9 @@
}
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 @@
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::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 5cad394..f4a0894 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -180,11 +180,10 @@
[[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 2182e18..636e5d0 100644
--- a/libs/binder/RpcTransportRaw.cpp
+++ b/libs/binder/RpcTransportRaw.cpp
@@ -44,11 +44,15 @@
}
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 @@
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 @@
}
}
- 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 c05ea15..3936204 100644
--- a/libs/binder/RpcTransportTls.cpp
+++ b/libs/binder/RpcTransportTls.cpp
@@ -275,9 +275,9 @@
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 @@
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 @@
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 240e3c2..ea40db8 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -115,6 +115,17 @@
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 348bfeb..ade2d94 100644
--- a/libs/binder/include/binder/RpcTransport.h
+++ b/libs/binder/include/binder/RpcTransport.h
@@ -58,10 +58,10 @@
* 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 90cbf9d..355b3b4 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -44,6 +44,7 @@
"libtokio",
],
host_supported: true,
+ vendor_available: true,
target: {
darwin: {
enabled: false,
@@ -52,8 +53,10 @@
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 7895a72..467e51e 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -192,9 +192,6 @@
/// 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 @@
/// 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 bc70ea6..d58e839 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 @@
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 @@
/// `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_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 760d862..12bfde7 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -312,17 +312,6 @@
}
}
- 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 @@
)
})
}
+
+ 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 63a4b2c..700940a 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -1220,6 +1220,19 @@
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
new file mode 100644
index 0000000..ae081e6
--- /dev/null
+++ b/libs/binder/tests/rpc_fuzzer/corpus/transact_on_binder
Binary files differ
diff --git a/libs/binderthreadstate/Android.bp b/libs/binderthreadstate/Android.bp
index 0a82463..4860613 100644
--- a/libs/binderthreadstate/Android.bp
+++ b/libs/binderthreadstate/Android.bp
@@ -31,7 +31,7 @@
target: {
darwin: {
enabled: false,
- }
+ },
},
shared_libs: [
@@ -46,6 +46,11 @@
"-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 1fd2c62..4f63194 100644
--- a/libs/cputimeinstate/Android.bp
+++ b/libs/cputimeinstate/Android.bp
@@ -14,6 +14,7 @@
"libbase",
"libbpf_bcc",
"libbpf_android",
+ "libbpf_minimal",
"liblog",
"libnetdutils"
],
@@ -33,6 +34,7 @@
"libbase",
"libbpf_bcc",
"libbpf_android",
+ "libbpf_minimal",
"libtimeinstate",
"libnetdutils",
],
@@ -43,4 +45,5 @@
"-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 0000000..4781520
--- /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 2112b10..1513eca 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 @@
using std::vector;
-TEST(TimeInStateTest, IsTrackingSupported) {
- isTrackingUidTimesSupported();
- SUCCEED();
-}
+class TimeInStateTest : public testing::Test {
+ protected:
+ TimeInStateTest() {};
-TEST(TimeInStateTest, TotalTimeInState) {
+ void SetUp() {
+ if (!isTrackingUidTimesSupported() ||
+ !android::base::GetBoolProperty("sys.init.perf_lsm_hooks", false)) {
+ GTEST_SKIP();
+ }
+ }
+};
+
+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 @@
EXPECT_EQ(activeSum, policySum);
}
-TEST(TimeInStateTest, SingleUidTimesConsistent) {
+TEST_F(TimeInStateTest, SingleUidTimesConsistent) {
auto times = getUidCpuFreqTimes(0);
ASSERT_TRUE(times.has_value());
@@ -127,7 +135,7 @@
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 @@
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, TotalAndAllUidTimeInStateConsistent) {
+TEST_F(TimeInStateTest, TotalAndAllUidTimeInStateConsistent) {
auto allUid = getUidsCpuFreqTimes();
auto total = getTotalCpuFreqTimes();
@@ -222,7 +230,7 @@
}
}
-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, 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, AllUidUpdatedConcurrentTimes) {
+TEST_F(TimeInStateTest, AllUidUpdatedConcurrentTimes) {
uint64_t lastUpdate = 0;
auto map1 = getUidsUpdatedConcurrentTimes(&lastUpdate);
ASSERT_TRUE(map1.has_value());
@@ -299,7 +307,7 @@
}
}
-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 @@
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, AllUidTimeInStateMonotonic) {
+TEST_F(TimeInStateTest, AllUidTimeInStateMonotonic) {
auto map1 = getUidsCpuFreqTimes();
ASSERT_TRUE(map1.has_value());
sleep(1);
@@ -365,7 +373,7 @@
}
}
-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, 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, 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, AllUidConcurrentTimesFailsOnInvalidBucket) {
+TEST_F(TimeInStateTest, AllUidConcurrentTimesFailsOnInvalidBucket) {
uint32_t uid = 0;
{
// Find an unused UID
@@ -463,7 +471,7 @@
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, RemoveUid) {
+TEST_F(TimeInStateTest, RemoveUid) {
uint32_t uid = 0;
{
// Find an unused UID
@@ -547,7 +555,7 @@
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 @@
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 9f0754b..61e4a98 100644
--- a/libs/fakeservicemanager/ServiceManager.cpp
+++ b/libs/fakeservicemanager/ServiceManager.cpp
@@ -84,4 +84,14 @@
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 b1496ba..6d6e008 100644
--- a/libs/fakeservicemanager/ServiceManager.h
+++ b/libs/fakeservicemanager/ServiceManager.h
@@ -53,6 +53,11 @@
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 1fd43ab..d8ce7a5 100644
--- a/libs/ftl/enum_test.cpp
+++ b/libs/ftl/enum_test.cpp
@@ -72,7 +72,7 @@
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 b7594df..fb9ed22 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -495,27 +495,6 @@
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 @@
}
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 27d86bb..eec4a87 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -340,6 +340,79 @@
}
}
+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 31456cd..91b2fb1 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -565,6 +565,13 @@
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 @@
if (composerState.read(*parcel) == BAD_VALUE) {
return BAD_VALUE;
}
-
composerStates[surfaceControlHandle] = composerState;
}
@@ -2088,12 +2094,6 @@
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 8d356aa..1c7b270 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -52,7 +52,8 @@
}
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 fb4fb7e..4b5cee2 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -235,12 +235,6 @@
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 @@
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 968ace9..cd6afd2 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -113,6 +113,12 @@
* 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 @@
eLayerStackChanged = 0x00000080,
/* unused 0x00000400, */
eShadowRadiusChanged = 0x00000800,
- eLayerCreated = 0x00001000,
+ /* unused 0x00001000, */
eBufferCropChanged = 0x00002000,
eRelativeLayerChanged = 0x00004000,
eReparent = 0x00008000,
@@ -181,6 +187,7 @@
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 4f92878..61eeab3 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -174,8 +174,7 @@
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 @@
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 6f1263b..06a0aca 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_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 @@
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 @@
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 0ebd11c..120ed48 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -762,10 +762,6 @@
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 e73c3b8..930d819 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -35,6 +35,7 @@
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 ac84627..0bee1b6 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -252,6 +252,13 @@
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 a065ce2..6195052 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -1318,10 +1318,6 @@
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 829bbdd..265cbf0 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -95,4 +95,7 @@
*/
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 b6a9476..1c8658b 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -115,12 +115,9 @@
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 @@
// 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 84daea0..d90ee57 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -552,7 +552,7 @@
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 @@
"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 6288194..76b85d6 100644
--- a/libs/nativedisplay/ADisplay.cpp
+++ b/libs/nativedisplay/ADisplay.cpp
@@ -50,11 +50,6 @@
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 @@
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 @@
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 0a1fcbe..d650c26 100644
--- a/libs/nativedisplay/include-private/private/android/choreographer.h
+++ b/libs/nativedisplay/include-private/private/android/choreographer.h
@@ -65,7 +65,7 @@
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 bd94b55..0f44902 100644
--- a/libs/nativedisplay/include/apex/display.h
+++ b/libs/nativedisplay/include/apex/display.h
@@ -107,11 +107,6 @@
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 b1b6498..6579313 100644
--- a/libs/nativedisplay/libnativedisplay.map.txt
+++ b/libs/nativedisplay/libnativedisplay.map.txt
@@ -50,7 +50,6 @@
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 6882ea3..0128859 100644
--- a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
+++ b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
@@ -593,6 +593,10 @@
}
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 cb3361b..2578ee8 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -509,10 +509,6 @@
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 b4cab39..2c51ccd 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -43,6 +43,9 @@
// 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 cc90946..763b82d 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -656,6 +656,7 @@
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 @@
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 @@
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 36305ae..6077c2e 100644
--- a/libs/renderengine/skia/filters/LinearEffect.cpp
+++ b/libs/renderengine/skia/filters/LinearEffect.cpp
@@ -44,14 +44,15 @@
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 8eb6670..e0a556b 100644
--- a/libs/renderengine/skia/filters/LinearEffect.h
+++ b/libs/renderengine/skia/filters/LinearEffect.h
@@ -37,13 +37,14 @@
// 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 2a25b0b..e197150 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -49,6 +49,50 @@
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 @@
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 @@
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 @@
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 @@
}
}
-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 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));
-}
-
TEST_P(RenderEngineTest, test_tonemapPQMatches) {
if (!GetParam()->useColorManagement()) {
- return;
+ GTEST_SKIP();
}
if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
+ GTEST_SKIP();
+ }
+
+ initializeRenderEngine();
+
+ 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_tonemapHLGMatches) {
+ if (!GetParam()->useColorManagement()) {
+ GTEST_SKIP();
+ }
+
+ if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
+ GTEST_SKIP();
+ }
+
+ initializeRenderEngine();
+
+ 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);
+ });
+}
+
+TEST_P(RenderEngineTest, r8_behaves_as_mask) {
+ if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
return;
}
initializeRenderEngine();
- constexpr int32_t kGreyLevels = 256;
+ const auto r8Buffer = allocateR8Buffer(2, 1);
+ if (!r8Buffer) {
+ return;
+ }
+ {
+ uint8_t* pixels;
+ 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();
+ }
- const auto rect = Rect(0, 0, kGreyLevels, 1);
+ const auto rect = Rect(0, 0, 2, 1);
const renderengine::DisplaySettings display{
.physicalDisplay = rect,
.clip = rect,
- .maxLuminance = 750.0f,
- .outputDataspace = ui::Dataspace::DISPLAY_P3,
+ .outputDataspace = ui::Dataspace::SRGB,
};
- 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{
+ 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,
- .sourceDataspace = static_cast<ui::Dataspace>(HAL_DATASPACE_STANDARD_BT2020 |
- HAL_DATASPACE_TRANSFER_ST2084 |
- HAL_DATASPACE_RANGE_FULL),
+ };
+ const renderengine::LayerSettings r8Layer{
+ .geometry.boundaries = rect.toFloatRect(),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = r8Buffer,
+ },
+ },
+ .alpha = 1.0f,
};
- 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 da88e85..5b4631a 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -280,6 +280,22 @@
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 712a27a..43828cc 100644
--- a/libs/shaders/include/shaders/shaders.h
+++ b/libs/shaders/include/shaders/shaders.h
@@ -100,6 +100,7 @@
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 6019c4a..03da3ec 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 generateXYZTransforms(std::string& shader) {
+void generateXYZTransforms(std::string& shader) {
shader.append(R"(
uniform float4x4 in_rgbToXyz;
uniform float4x4 in_xyzToRgb;
@@ -171,8 +173,8 @@
}
// 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 @@
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 @@
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 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 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 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 @@
}
// 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 @@
}
}
+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 @@
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 @@
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 bd7b72d..b9abf8c 100644
--- a/libs/tonemap/include/tonemap/tonemap.h
+++ b/libs/tonemap/include/tonemap/tonemap.h
@@ -42,7 +42,9 @@
// 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 7a7958f..1d46482 100644
--- a/libs/tonemap/tests/tonemap_test.cpp
+++ b/libs/tonemap/tests/tonemap_test.cpp
@@ -61,7 +61,7 @@
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 @@
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 c2372fe..bc0a884 100644
--- a/libs/tonemap/tonemap.cpp
+++ b/libs/tonemap/tonemap.cpp
@@ -407,7 +407,6 @@
)");
switch (sourceDataspaceInt & kTransferMask) {
case kTransferST2084:
- case kTransferHLG:
switch (destinationDataspaceInt & kTransferMask) {
case kTransferST2084:
program.append(R"(
@@ -428,39 +427,22 @@
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 @@
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 @@
double targetNits = 0.0;
switch (sourceDataspaceInt & kTransferMask) {
case kTransferST2084:
- case kTransferHLG:
switch (destinationDataspaceInt & kTransferMask) {
case kTransferST2084:
targetNits = maxRGB;
@@ -587,19 +592,9 @@
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 @@
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 @@
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 d5c4ef0..78ba996 100644
--- a/libs/ui/DynamicDisplayInfo.cpp
+++ b/libs/ui/DynamicDisplayInfo.cpp
@@ -41,7 +41,8 @@
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 @@
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 @@
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 33ab7c4..cc96f83 100644
--- a/libs/ui/Fence.cpp
+++ b/libs/ui/Fence.cpp
@@ -132,9 +132,13 @@
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 040a62b..f23f10a 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -110,6 +110,15 @@
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 882674f..15c60bc 100644
--- a/libs/ui/Gralloc3.cpp
+++ b/libs/ui/Gralloc3.cpp
@@ -101,6 +101,15 @@
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 1f8a2f0..1a42642 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -41,6 +41,7 @@
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 @@
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 @@
}
return bits;
}();
- return validUsageBits;
+ return validUsageBits | kRemovedUsageBits;
}
uint64_t getValidUsageBits41() {
@@ -92,9 +96,48 @@
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) {
+
+// See if gralloc "4.1" is available.
+static bool hasIAllocatorAidl() {
+ // Avoid re-querying repeatedly for this information;
+ static bool sHasIAllocatorAidl = []() -> bool {
+ if (__builtin_available(android 31, *)) {
+ return AServiceManager_isDeclared(kAidlAllocatorServiceName.c_str());
+ }
+ return false;
+ }();
+ 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;
@@ -102,21 +145,8 @@
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());
- }
- return false;
- }();
- return sHasIAllocatorAidl;
+ return validateBufferDescriptorInfo(outDescriptorInfo);
}
} // anonymous namespace
@@ -140,21 +170,6 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 b66b281..03d15e4 100644
--- a/libs/ui/StaticDisplayInfo.cpp
+++ b/libs/ui/StaticDisplayInfo.cpp
@@ -29,7 +29,8 @@
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 @@
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 @@
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 a4c2f71..ce75a65 100644
--- a/libs/ui/include/ui/DynamicDisplayInfo.h
+++ b/libs/ui/include/ui/DynamicDisplayInfo.h
@@ -35,7 +35,7 @@
// 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 @@
// 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 6bafcd6..fe38709 100644
--- a/libs/ui/include/ui/Gralloc4.h
+++ b/libs/ui/include/ui/Gralloc4.h
@@ -155,10 +155,6 @@
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 e86ca29..cc7c869 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 @@
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 1e8eb1e..178bc29 100644
--- a/services/batteryservice/include/batteryservice/BatteryService.h
+++ b/services/batteryservice/include/batteryservice/BatteryService.h
@@ -40,6 +40,7 @@
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 ec58325..22a69e5 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -49,6 +49,7 @@
srcs: [
"InputClassifier.cpp",
"InputClassifierConverter.cpp",
+ "UnwantedInteractionBlocker.cpp",
"InputManager.cpp",
],
}
@@ -60,6 +61,7 @@
"android.hardware.input.classifier@1.0",
"libbase",
"libbinder",
+ "libchrome",
"libcrypto",
"libcutils",
"libhidlbase",
@@ -76,6 +78,7 @@
],
static_libs: [
"libattestation",
+ "libpalmrejection",
],
}
diff --git a/services/inputflinger/InputClassifierConverter.cpp b/services/inputflinger/InputClassifierConverter.cpp
index fc8c7c3..b58a188 100644
--- a/services/inputflinger/InputClassifierConverter.cpp
+++ b/services/inputflinger/InputClassifierConverter.cpp
@@ -325,11 +325,6 @@
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 @@
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 158d0eb..73b63e3 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -188,6 +188,25 @@
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 221e193..7a9862d 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 @@
}
}
+/**
+ * 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 @@
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 e000283..35d2b0f 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 @@
* 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 @@
/* 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 @@
status_t stop() override;
InputReaderInterface& getReader() override;
+ UnwantedInteractionBlockerInterface& getUnwantedInteractionBlocker() override;
InputClassifierInterface& getClassifier() override;
InputDispatcherInterface& getDispatcher() override;
@@ -107,6 +117,8 @@
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 3d85bef..9b72ff4 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 0000000..64dbb8c
--- /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 0000000..14068fd
--- /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 441a1de..aa3643c 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -120,7 +120,8 @@
// 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 @@
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 @@
mAppSwitchSawKeyDown(false),
mAppSwitchDueTime(LONG_LONG_MAX),
mNextUnblockedEvent(nullptr),
+ mMonitorDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT),
mDispatchEnabled(false),
mDispatchFrozen(false),
mInputFilterEnabled(false),
@@ -706,8 +708,13 @@
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 @@
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 @@
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 7564839..bff6cac 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -148,6 +148,9 @@
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 @@
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 db63104..dff5894 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -142,6 +142,8 @@
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 1aab856..41ecef3 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 @@
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 @@
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 0000000..2327266
--- /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 09a62f3..db67877 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -880,6 +880,42 @@
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 7cff672..a050963 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -309,15 +309,15 @@
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 @@
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 @@
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 564db56..31d331b 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -303,7 +303,7 @@
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 @@
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 1f96294..18e912d 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -306,6 +306,7 @@
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 @@
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 518aaa0..694daa9 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -61,6 +61,9 @@
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 @@
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 @@
// 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 @@
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 @@
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 fb1d166..daeaa1d 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -69,6 +69,8 @@
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 @@
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 fcb56ef..dc5fcec 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -66,7 +66,7 @@
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 9a8ca01..c84c6c4 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -56,7 +56,7 @@
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 37d1d74..6b5d37f 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
@@ -26,7 +26,7 @@
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 1d42b30..516aa51 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
@@ -30,7 +30,7 @@
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 b9aef54..7b185e0 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -51,6 +51,10 @@
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 d83d74d..fce6409 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -45,12 +45,13 @@
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 @@
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 ad11b05..a8e9bae 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -25,7 +25,7 @@
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 bba95ad..307bf5b 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.h
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h
@@ -26,7 +26,7 @@
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 a8602a4..1d63c0e 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -95,7 +95,7 @@
KeyboardInputMapper::~KeyboardInputMapper() {}
-uint32_t KeyboardInputMapper::getSources() {
+uint32_t KeyboardInputMapper::getSources() const {
return mSource;
}
@@ -375,6 +375,10 @@
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 fc92320..3787696 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -26,7 +26,7 @@
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 @@
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 4bd1cd8..ff3a592 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -282,7 +282,7 @@
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 b83a8fc..eca25f6 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -31,7 +31,7 @@
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 e0c9404..1859355 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -27,7 +27,7 @@
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 677a372..b01c2bc 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -57,7 +57,7 @@
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 1797fe3..27a6177 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.h
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.h
@@ -28,7 +28,7 @@
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 3237824..ebb5de6 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
@@ -25,7 +25,7 @@
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 4d74163..64b9aa2 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.h
@@ -26,7 +26,7 @@
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 0a5de27..f729ba9 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -178,7 +178,7 @@
TouchInputMapper::~TouchInputMapper() {}
-uint32_t TouchInputMapper::getSources() {
+uint32_t TouchInputMapper::getSources() const {
return mSource;
}
@@ -565,6 +565,12 @@
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 9fd30e4..c948f56 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -138,7 +138,7 @@
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 1976fed..33db527 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -25,7 +25,7 @@
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 7ce621a..d3c22b6 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.h
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h
@@ -26,7 +26,7 @@
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 e686924..9d200bd 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -49,6 +49,7 @@
"LatencyTracker_test.cpp",
"TestInputListener.cpp",
"UinputDevice.cpp",
+ "UnwantedInteractionBlocker_test.cpp",
],
aidl: {
include_dirs: [
@@ -57,7 +58,7 @@
],
},
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 0814bc2..f97a9ec 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 @@
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 @@
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 @@
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 @@
* 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 @@
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 @@
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);
+TEST_F(InputDispatcherMonitorTest, MonitorCannotPilferPointers) {
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- window->setFocusable(true);
-
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
- setFocusedWindow(window);
-
- window->consumeFocusEvent(true);
-
- 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);
+
+ monitor.consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ window->consumeMotionMove(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();
-}
-
-// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
/**
+ * 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 @@
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 @@
}
};
-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 2c5b321..54cf15d 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -439,6 +439,8 @@
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 @@
}
}
+ 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 @@
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 @@
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 @@
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 @@
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 @@
<< "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 @@
// 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 @@
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 @@
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 0000000..a3220cc
--- /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 cdd95ca..09aed46 100644
--- a/services/sensorservice/AidlSensorHalWrapper.cpp
+++ b/services/sensorservice/AidlSensorHalWrapper.cpp
@@ -250,6 +250,41 @@
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 @@
*dst = {
.timestamp = src.timestamp,
.sensorHandle = src.sensor,
- .sensorType = (SensorType) src.type,
+ .sensorType = (SensorType)src.type,
};
switch (dst->sensorType) {
@@ -409,6 +444,35 @@
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 d5b629d..3c4f8d9 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -17,6 +17,7 @@
"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 0000000..2f91479
--- /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 0000000..fd46a98
--- /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 517d383..971491d 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 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 @@
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 @@
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 @@
// 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 @@
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 @@
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 baa01c9..c304381 100644
--- a/services/sensorservice/SensorServiceUtils.cpp
+++ b/services/sensorservice/SensorServiceUtils.cpp
@@ -30,12 +30,18 @@
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 af0f524..d9958f3 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -118,9 +118,6 @@
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 e797b5d..b7e2ff3 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -306,7 +306,7 @@
: 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 0c93872..40fc342 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -316,8 +316,7 @@
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 @@
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 2f613d7..248e013 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -70,8 +70,7 @@
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 16cb41b..47aacc9 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -56,6 +56,9 @@
// 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 b777241..ebe112b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -62,6 +62,7 @@
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 @@
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 49cb912..08cfaa6 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -90,6 +90,10 @@
// 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 6fb3e08..84afb59 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
@@ -34,6 +34,7 @@
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 186e191..29c146b 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -57,6 +57,16 @@
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 @@
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 192ee04..65f9731 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 @@
/*
* 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 @@
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 @@
// 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 95cc5a8..4ccf11f 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -484,6 +484,14 @@
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 0918510..8d26747 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -401,6 +401,19 @@
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 @@
}
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 b27e30c..125ce74 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -164,6 +164,7 @@
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 @@
DisplayFunctionalTest() {
EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
-
+ mDisplay = createDisplay();
+ mRenderSurface = createRenderSurface();
mDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
}
@@ -980,24 +982,29 @@
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> mDisplay = impl::createDisplayTemplated<
- Display>(mCompositionEngine,
- DisplayCreationArgsBuilder()
- .setId(DEFAULT_DISPLAY_ID)
- .setPixels(DEFAULT_RESOLUTION)
- .setIsSecure(true)
- .setPowerAdvisor(&mPowerAdvisor)
- .build());
+ std::shared_ptr<Display> createDisplay() {
+ return 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 bd3022b..660f664 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -39,7 +39,7 @@
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 @@
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 @@
(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 f67bf06..82dcc66 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -846,7 +846,8 @@
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 @@
? 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 @@
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 6e6552f..e72bc9f 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -1291,7 +1291,7 @@
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 @@
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 @@
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 @@
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 @@
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 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 @@
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 @@
TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrMixedComposition) {
verify().ifMixedCompositionIs(true)
.andIfUsesHdr(true)
+ .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();
+}
+
+TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings,
+ forHdrMixedCompositionWithDisplayBrightness) {
+ verify().ifMixedCompositionIs(true)
+ .andIfUsesHdr(true)
+ .withDisplayBrightnessNits(kDisplayLuminance)
+ .andIfSkipColorTransform(false)
+ .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, 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, 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, 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 @@
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 58dc244..656ef9a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -1337,5 +1337,56 @@
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 a36ea72..f542161 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -92,10 +92,12 @@
}
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 @@
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 0c9063d..3cae30f 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -157,6 +157,9 @@
// 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 1448e56..b1057c3 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 @@
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 @@
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 6770017..374a436 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -63,7 +63,7 @@
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 22f424f..fe55e6b 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_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 @@
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 @@
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 @@
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 @@
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 57eb128..0a605a8 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -72,6 +72,7 @@
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 44e4597..2696bd8 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 @@
mDisplayData.clear();
}
-void HWComposer::setCallback(HWC2::ComposerCallback* callback) {
+void HWComposer::setCallback(HWC2::ComposerCallback& callback) {
loadCapabilities();
loadLayerMetadataSupport();
@@ -159,10 +102,7 @@
}
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 @@
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::NONE) {
+ LOG_DISPLAY_ERROR(displayId, to_string(error).c_str());
+ return std::nullopt;
}
- if (error == hal::Error::BAD_PARAMETER) {
- RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE);
- }
- 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 93773fa..29335d5 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -105,7 +105,7 @@
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 @@
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 @@
~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 @@
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 d3acecb..746ac64 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::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 @@
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 c8c7800..1ffca6e 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -172,7 +172,7 @@
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 @@
~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 fa2c92d..645d4d1 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -945,16 +945,10 @@
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 @@
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;
+ 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;
}
- 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});
+ // 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();
- 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);
- }
+ // 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));
- // Transform the layer bounds from layer coordinate space to display coordinate space.
- Rect transformedLayerBounds = t.transform(layerBounds);
+ // Apply the insets to the input bounds.
+ const FloatRect insetBoundsInLayer(inputBoundsInLayer.left + xSurfaceInset,
+ inputBoundsInLayer.top + ySurfaceInset,
+ inputBoundsInLayer.right - xSurfaceInset,
+ inputBoundsInLayer.bottom - ySurfaceInset);
- // 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;
+ // Crop the input bounds to ensure it is within the parent's bounds.
+ const FloatRect croppedInsetBoundsInLayer = mBounds.intersect(insetBoundsInLayer);
- // 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;
- }
+ const ui::Transform layerToScreen = getInputTransform();
+ const ui::Transform layerToDisplay = screenToDisplay * layerToScreen;
- // 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();
+ const Rect roundedFrameInDisplay{layerToDisplay.transform(croppedInsetBoundsInLayer)};
+ info.frameLeft = roundedFrameInDisplay.left;
+ info.frameTop = roundedFrameInDisplay.top;
+ info.frameRight = roundedFrameInDisplay.right;
+ info.frameBottom = roundedFrameInDisplay.bottom;
- // 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;
+ ui::Transform inputToLayer;
+ inputToLayer.set(insetBoundsInLayer.left, insetBoundsInLayer.top);
+ const ui::Transform inputToDisplay = layerToDisplay * inputToLayer;
- // Position the touchable region relative to frame screen location and restrict it to frame
- // bounds.
- info.touchableRegion = inputTransform.transform(info.touchableRegion);
+ // 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 4cdd8fa..605a27e 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -98,6 +98,7 @@
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 @@
// 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 @@
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 @@
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 3becb5c..3b9cfa6 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 @@
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 @@
int explicitExact = 0;
float maxExplicitWeight = 0;
int seamedFocusedLayers = 0;
+
for (const auto& layer : layers) {
switch (layer.vote) {
case LayerVoteType::NoVote:
@@ -349,10 +315,9 @@
// 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 @@
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 @@
[](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 @@
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 @@
// 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 @@
// 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 @@
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 @@
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 849d297..ade1787 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 @@
}
};
- // 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 @@
.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 @@
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 @@
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 @@
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 a85e748..665d36982 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -537,18 +537,19 @@
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 @@
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 @@
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 @@
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 @@
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};
- return refreshRateConfigs
- ->getBestRefreshRate(mPolicy.contentRequirements, {.touch = touchActive, .idle = idle},
- consideredSignals)
- .getMode();
+ const auto [refreshRate, consideredSignals] =
+ configs->getBestRefreshRate(mPolicy.contentRequirements, signals);
+
+ 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 bc9024a..468c4cc 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -269,15 +269,14 @@
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 aa371cb..3459a8f 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -271,12 +271,6 @@
}
-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 @@
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 @@
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 @@
return type == hal::ContentType::GAME;
});
+ info->preferredBootDisplayMode = display->getPreferredBootModeId();
+
return NO_ERROR;
}
@@ -1457,20 +1456,6 @@
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 @@
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 needsTraversal = false;
if (clearTransactionFlags(eTransactionFlushNeeded)) {
- needsTraversal = flushTransactionQueues(vsyncId);
+ needsTraversal |= commitCreatedLayers();
+ needsTraversal |= flushTransactionQueues(vsyncId);
}
const bool shouldCommit =
@@ -2178,6 +2169,11 @@
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 @@
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 @@
mTimeStats->recordDisplayEventConnectionCount(sfConnections + appConnections);
if (isDisplayConnected && !display->isPoweredOn()) {
+ getRenderEngine().cleanupPostRender();
return;
}
@@ -3602,7 +3595,7 @@
}
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 @@
return NO_MEMORY;
}
- setLayerCreatedState(handle, lbc, parent, addToRoot);
+ {
+ std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
+ mCreatedLayers.emplace_back(layer, 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);
-
- 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 @@
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 @@
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 @@
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 @@
}
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 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 @@
}
}
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 @@
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 @@
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 @@
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 @@
}
}
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 @@
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 @@
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 @@
return result;
}
- setTransactionFlags(eTransactionNeeded);
*outLayerId = layer->sequence;
return result;
}
@@ -5463,7 +5396,6 @@
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 @@
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 @@
}
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 @@
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 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 @@
layer->updateTransformHint(mActiveDisplayTransformHint);
mInterceptor->saveSurfaceCreation(layer);
- return layer;
}
void SurfaceFlinger::sample() {
@@ -7235,6 +7148,26 @@
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 8ca9982..c6a4d85 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -537,8 +537,6 @@
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 @@
void onComposerHalVsyncPeriodTimingChanged(hal::HWDisplayId,
const hal::VsyncPeriodChangeTimeline&) override;
void onComposerHalSeamlessPossible(hal::HWDisplayId) override;
+ void onComposerHalVsyncIdle(hal::HWDisplayId) override;
// ICompositor overrides:
@@ -729,7 +728,7 @@
// 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 @@
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 @@
// 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 d136e0b..006efdf 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.cpp
+++ b/services/surfaceflinger/Tracing/LayerTracing.cpp
@@ -98,16 +98,20 @@
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 8ca3587..bd448c9 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.h
+++ b/services/surfaceflinger/Tracing/LayerTracing.h
@@ -47,7 +47,7 @@
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 5136295..a46b795 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.cpp
+++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp
@@ -128,7 +128,9 @@
mCommittedTransactions.clear();
} // unlock mMainThreadLock
- addEntry(committedTransactions, removedLayers);
+ if (!committedTransactions.empty() || !removedLayers.empty()) {
+ addEntry(committedTransactions, removedLayers);
+ }
}
}
@@ -275,6 +277,7 @@
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::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 0000000..0ead163
--- /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 0000000..4ecf770
--- /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 0000000..816d2f1
--- /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 0000000..6a6e3db
--- /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 0000000..4f89cd9
--- /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 0000000..0a458c2
--- /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 9b076bd..fcf4499 100644
--- a/services/surfaceflinger/layerproto/transactions.proto
+++ b/services/surfaceflinger/layerproto/transactions.proto
@@ -89,7 +89,6 @@
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 5af17ec..48f18b9 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -21,6 +21,24 @@
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 @@
address: true,
},
srcs: [
+ ":libsurfaceflinger_mock_sources",
":libsurfaceflinger_sources",
"libsurfaceflinger_unittest_main.cpp",
"CachingTest.cpp",
@@ -101,18 +120,6 @@
"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 2af0480..0069441 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -89,6 +89,7 @@
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 @@
}),
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 @@
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 2bafabd..4efcc05 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 LayerVoteType = RefreshRateConfigs::LayerVoteType;
using LayerRequirement = RefreshRateConfigs::LayerRequirement;
+struct TestableRefreshRateConfigs : RefreshRateConfigs {
+ using RefreshRateConfigs::RefreshRateConfigs;
+
+ RefreshRate getMinSupportedRefreshRate() const {
+ std::lock_guard lock(mLock);
+ return *mMinSupportedRefreshRate;
+ }
+
+ RefreshRate getMaxSupportedRefreshRate() const {
+ std::lock_guard lock(mLock);
+ return *mMaxSupportedRefreshRate;
+ }
+
+ RefreshRate getMinRefreshRateByPolicy() const {
+ std::lock_guard lock(mLock);
+ return getMinRefreshRateByPolicyLocked();
+ }
+
+ const std::vector<Fps>& knownFrameRates() const { return mKnownFrameRates; }
+
+ using RefreshRateConfigs::GetBestRefreshRateCache;
+ auto& mutableGetBestRefreshRateCache() { return mGetBestRefreshRateCache; }
+
+ auto getBestRefreshRateAndSignals(const std::vector<LayerRequirement>& layers,
+ GlobalSignals signals) const {
+ return RefreshRateConfigs::getBestRefreshRate(layers, signals);
+ }
+
+ RefreshRate getBestRefreshRate(const std::vector<LayerRequirement>& layers = {},
+ GlobalSignals signals = {}) const {
+ return getBestRefreshRateAndSignals(layers, signals).first;
+ }
+};
+
class RefreshRateConfigsTest : public testing::Test {
protected:
- using GetBestRefreshRateInvocation = RefreshRateConfigs::GetBestRefreshRateInvocation;
-
RefreshRateConfigsTest();
~RefreshRateConfigsTest();
- RefreshRate createRefreshRate(DisplayModePtr displayMode) {
+ static RefreshRate asRefreshRate(DisplayModePtr displayMode) {
return {displayMode, RefreshRate::ConstructorTag(0)};
}
- Fps findClosestKnownFrameRate(const RefreshRateConfigs& refreshRateConfigs, Fps frameRate) {
- return refreshRateConfigs.findClosestKnownFrameRate(frameRate);
- }
+ 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};
- std::vector<Fps> getKnownFrameRate(const RefreshRateConfigs& refreshRateConfigs) {
- return refreshRateConfigs.mKnownFrameRates;
- }
+ 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);
- RefreshRate getMinRefreshRateByPolicy(const RefreshRateConfigs& refreshRateConfigs) {
- std::lock_guard lock(refreshRateConfigs.mLock);
- return refreshRateConfigs.getMinRefreshRateByPolicyLocked();
- }
+ // 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};
- RefreshRate getMinSupportedRefreshRate(const RefreshRateConfigs& refreshRateConfigs) {
- std::lock_guard lock(refreshRateConfigs.mLock);
- return *refreshRateConfigs.mMinSupportedRefreshRate;
- }
-
- RefreshRate getMaxSupportedRefreshRate(const RefreshRateConfigs& refreshRateConfigs) {
- std::lock_guard lock(refreshRateConfigs.mLock);
- return *refreshRateConfigs.mMaxSupportedRefreshRate;
- }
-
- void setLastBestRefreshRateInvocation(RefreshRateConfigs& refreshRateConfigs,
- const GetBestRefreshRateInvocation& invocation) {
- std::lock_guard lock(refreshRateConfigs.mLock);
- refreshRateConfigs.lastBestRefreshRateInvocation.emplace(
- GetBestRefreshRateInvocation(invocation));
- }
-
- std::optional<GetBestRefreshRateInvocation> getLastBestRefreshRateInvocation(
- const RefreshRateConfigs& refreshRateConfigs) {
- std::lock_guard lock(refreshRateConfigs.mLock);
- return refreshRateConfigs.lastBestRefreshRateInvocation;
- }
-
- // 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 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 @@
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 = getMinSupportedRefreshRate(*refreshRateConfigs);
- const auto& performanceRate = getMaxSupportedRefreshRate(*refreshRateConfigs);
+ const auto minRate = configs.getMinSupportedRefreshRate();
+ const auto performanceRate = configs.getMaxSupportedRefreshRate();
- ASSERT_EQ(mExpected60Config, minRate);
- ASSERT_EQ(mExpected90Config, performanceRate);
+ EXPECT_EQ(asRefreshRate(kMode60), minRate);
+ EXPECT_EQ(asRefreshRate(kMode90), performanceRate);
- const auto& minRateByPolicy = getMinRefreshRateByPolicy(*refreshRateConfigs);
- const auto& performanceRateByPolicy = refreshRateConfigs->getMaxRefreshRateByPolicy();
- ASSERT_EQ(minRateByPolicy, minRate);
- ASSERT_EQ(performanceRateByPolicy, performanceRate);
+ const auto minRateByPolicy = configs.getMinRefreshRateByPolicy();
+ const auto performanceRateByPolicy = configs.getMaxRefreshRateByPolicy();
+
+ 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);
+ {
+ TestableRefreshRateConfigs configs(kModes_60_72_90, kModeId72);
- // If there are no layers we select the default frame rate, which is the max of the primary
- // range.
- EXPECT_EQ(mExpected90Config, 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());
- ASSERT_EQ(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 60_Hz}}),
- NO_ERROR);
- EXPECT_EQ(mExpected60Config, refreshRateConfigs->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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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);
- RefreshRateConfigs::Policy policy;
- policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
- policy.allowGroupSwitching = true;
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+ TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
- refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+ RefreshRateConfigs::Policy policy;
+ policy.defaultMode = configs.getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+
+ 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 @@
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);
- RefreshRateConfigs::Policy policy;
- policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
- policy.allowGroupSwitching = true;
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+ TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
- refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+ RefreshRateConfigs::Policy policy;
+ policy.defaultMode = configs.getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+
+ 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 @@
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);
- RefreshRateConfigs::Policy policy;
- policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
- policy.allowGroupSwitching = true;
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+ TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
- refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+ RefreshRateConfigs::Policy policy;
+ policy.defaultMode = configs.getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+
+ 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 @@
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);
- RefreshRateConfigs::Policy policy;
- policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
- policy.allowGroupSwitching = true;
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+ TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
- refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+ RefreshRateConfigs::Policy policy;
+ policy.defaultMode = configs.getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+
+ 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 @@
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);
- RefreshRateConfigs::Policy policy;
- policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
- policy.allowGroupSwitching = true;
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+ TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
- refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+ RefreshRateConfigs::Policy policy;
+ policy.defaultMode = configs.getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+
+ 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 @@
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 @@
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 @@
.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 @@
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 @@
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 @@
};
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 @@
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 @@
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 @@
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) {
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ReadsCache) {
+ TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60);
+
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});
- auto refreshRateConfigs =
- std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
- /*currentConfigId=*/HWC_CONFIG_ID_60);
+ configs.mutableGetBestRefreshRateCache() = {args, result};
- setLastBestRefreshRateInvocation(*refreshRateConfigs,
- GetBestRefreshRateInvocation{.globalSignals = {.touch = true,
- .idle = true},
- .outSignalsConsidered =
- {.touch = true},
- .resultingBestRefreshRate =
- createRefreshRate(
- mConfig90)});
-
- EXPECT_EQ(createRefreshRate(mConfig90),
- refreshRateConfigs->getBestRefreshRate({}, {.touch = true, .idle = true}));
-
- const GlobalSignals cachedSignalsConsidered{.touch = true};
- setLastBestRefreshRateInvocation(*refreshRateConfigs,
- GetBestRefreshRateInvocation{.globalSignals = {.touch = true,
- .idle = true},
- .outSignalsConsidered =
- cachedSignalsConsidered,
- .resultingBestRefreshRate =
- createRefreshRate(
- mConfig30)});
-
- GlobalSignals signalsConsidered;
- EXPECT_EQ(createRefreshRate(mConfig30),
- refreshRateConfigs->getBestRefreshRate({}, {.touch = true, .idle = true},
- &signalsConsidered));
-
- 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 @@
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 @@
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 @@
// 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 @@
},
};
- 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;
- auto refreshRateConfigs =
- std::make_unique<RefreshRateConfigs>(m60_90Device,
- /*currentConfigId=*/HWC_CONFIG_ID_90);
+ RefreshRateConfigs configs(kModes_60_90, kModeId90);
+
// 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;
- // Tests with 120Hz
- auto refreshRateConfigs =
- std::make_unique<RefreshRateConfigs>(m60_120Device,
- /*currentConfigId=*/HWC_CONFIG_ID_120);
+ RefreshRateConfigs configs(kModes_60_120, kModeId120);
+
// 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, 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 @@
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 5ac5812..39dbb07 100644
--- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
@@ -91,10 +91,9 @@
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 @@
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 4273401..0765d5b 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 @@
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 @@
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 fa3b260..c335e2a 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -1473,7 +1473,7 @@
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 33401d2..df70bf4 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -179,6 +179,7 @@
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 @@
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 @@
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 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 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 @@
"vkGetPhysicalDeviceSurfaceFormatsKHR",
"vkGetPhysicalDeviceSurfacePresentModesKHR",
"vkGetPhysicalDeviceSurfaceSupportKHR",
+ "vkGetPhysicalDeviceToolProperties",
"vkGetPhysicalDeviceToolPropertiesEXT",
"vkGetPhysicalDeviceVideoCapabilitiesKHR",
"vkGetPhysicalDeviceVideoFormatPropertiesKHR",
@@ -680,18 +755,25 @@
{ "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 @@
{ "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 @@
{ "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 @@
{ "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 @@
{ "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 @@
{ "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 @@
{ "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 @@
{ "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 @@
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 @@
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 @@
}
__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 @@
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 ad5cc34..4998018 100644
--- a/vulkan/libvulkan/api_gen.h
+++ b/vulkan/libvulkan/api_gen.h
@@ -60,6 +60,7 @@
PFN_vkGetPhysicalDeviceExternalFenceProperties GetPhysicalDeviceExternalFenceProperties;
PFN_vkEnumeratePhysicalDeviceGroups EnumeratePhysicalDeviceGroups;
PFN_vkGetPhysicalDevicePresentRectanglesKHR GetPhysicalDevicePresentRectanglesKHR;
+ PFN_vkGetPhysicalDeviceToolProperties GetPhysicalDeviceToolProperties;
// clang-format on
};
@@ -207,6 +208,9 @@
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 @@
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 cf774fd..9225062 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -365,7 +365,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 047e774..819f6b2 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -58,6 +58,7 @@
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 df97d7f..f49e8f3 100644
--- a/vulkan/libvulkan/libvulkan.map.txt
+++ b/vulkan/libvulkan/libvulkan.map.txt
@@ -11,20 +11,27 @@
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 @@
vkCmdDrawIndirect;
vkCmdDrawIndirectCount; # introduced=31
vkCmdEndQuery;
+ vkCmdEndRendering; #introduced=33
vkCmdEndRenderPass;
vkCmdEndRenderPass2; # introduced=31
vkCmdExecuteCommands;
@@ -43,24 +51,44 @@
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 @@
vkCreateInstance;
vkCreatePipelineCache;
vkCreatePipelineLayout;
+ vkCreatePrivateDataSlot; #introduced=33
vkCreateQueryPool;
vkCreateRenderPass;
vkCreateRenderPass2; # introduced=31
@@ -103,6 +132,7 @@
vkDestroyPipeline;
vkDestroyPipelineCache;
vkDestroyPipelineLayout;
+ vkDestroyPrivateDataSlot; #introduced=33
vkDestroyQueryPool;
vkDestroyRenderPass;
vkDestroySampler;
@@ -130,9 +160,12 @@
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 @@
vkGetPhysicalDeviceSurfaceFormatsKHR;
vkGetPhysicalDeviceSurfacePresentModesKHR;
vkGetPhysicalDeviceSurfaceSupportKHR;
+ vkGetPhysicalDeviceToolProperties; #introduced=33
vkGetPipelineCacheData;
+ vkGetPrivateData; #introduced=33
vkGetQueryPoolResults;
vkGetRenderAreaGranularity;
vkGetSemaphoreCounterValue; # introduced=31
@@ -180,6 +215,7 @@
vkQueueBindSparse;
vkQueuePresentKHR;
vkQueueSubmit;
+ vkQueueSubmit2; #introduced=33
vkQueueWaitIdle;
vkResetCommandBuffer;
vkResetCommandPool;
@@ -188,6 +224,7 @@
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 b94233b..3c91150 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -260,7 +260,7 @@
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 @@
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 @@
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 edda12c..f6dcf09 100644
--- a/vulkan/nulldrv/null_driver_gen.cpp
+++ b/vulkan/nulldrv/null_driver_gen.cpp
@@ -68,18 +68,25 @@
{"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 @@
{"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 @@
{"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 @@
{"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 @@
{"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 @@
{"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 @@
{"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 @@
{"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 e59cae9..3e003e3 100644
--- a/vulkan/nulldrv/null_driver_gen.h
+++ b/vulkan/nulldrv/null_driver_gen.h
@@ -200,6 +200,9 @@
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 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 438e5dd..da6b00a 100644
--- a/vulkan/vkjson/vkjson.cc
+++ b/vulkan/vkjson/vkjson.cc
@@ -387,6 +387,19 @@
}
};
+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 @@
visitor->Visit("maxResourceSize", &properties->maxResourceSize);
}
+// clang-format off
template <typename Visitor>
inline bool Iterate(Visitor* visitor, VkPhysicalDeviceLimits* limits) {
return
@@ -605,6 +619,200 @@
}
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 @@
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 @@
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 52e7bee..88f6e7d 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 @@
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 @@
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 @@
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 5872495..0ffe7e0 100644
--- a/vulkan/vkjson/vkjson_instance.cc
+++ b/vulkan/vkjson/vkjson_instance.cc
@@ -157,76 +157,64 @@
}
}
- 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;
+ device.maintenance3_properties.pNext = properties.pNext;
+ properties.pNext = &device.maintenance3_properties;
- vkGetPhysicalDeviceProperties2(physical_device, &properties2);
-
- 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 @@
}
}
+ 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;
}