diff options
18 files changed, 616 insertions, 107 deletions
diff --git a/cmds/sfdo/Android.bp b/cmds/sfdo/Android.bp new file mode 100644 index 0000000000..c19c9da7bf --- /dev/null +++ b/cmds/sfdo/Android.bp @@ -0,0 +1,17 @@ +cc_binary { + name: "sfdo", + + srcs: ["sfdo.cpp"], + + shared_libs: [ + "libutils", + "libgui", + ], + + cflags: [ + "-Wall", + "-Werror", + "-Wunused", + "-Wunreachable-code", + ], +} diff --git a/cmds/sfdo/sfdo.cpp b/cmds/sfdo/sfdo.cpp new file mode 100644 index 0000000000..55326ea737 --- /dev/null +++ b/cmds/sfdo/sfdo.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <inttypes.h> +#include <stdint.h> +#include <any> +#include <unordered_map> + +#include <cutils/properties.h> +#include <sys/resource.h> +#include <utils/Log.h> + +#include <gui/ISurfaceComposer.h> +#include <gui/SurfaceComposerClient.h> +#include <gui/SurfaceControl.h> +#include <private/gui/ComposerServiceAIDL.h> + +using namespace android; + +std::unordered_map<std::string, std::any> g_functions; + +const std::unordered_map<std::string, std::string> g_function_details = { + {"DebugFlash", "[optional(delay)] Perform a debug flash."}, + {"FrameRateIndicator", "[hide | show] displays the framerate in the top left corner."}, + {"scheduleComposite", "Force composite ahead of next VSYNC."}, + {"scheduleCommit", "Force commit ahead of next VSYNC."}, + {"scheduleComposite", "PENDING - if you have a good understanding let me know!"}, +}; + +static void ShowUsage() { + std::cout << "usage: sfdo [help, FrameRateIndicator show, DebugFlash enabled, ...]\n\n"; + for (const auto& sf : g_functions) { + const std::string fn = sf.first; + std::string fdetails = "TODO"; + if (g_function_details.find(fn) != g_function_details.end()) + fdetails = g_function_details.find(fn)->second; + std::cout << " " << fn << ": " << fdetails << "\n"; + } +} + +int FrameRateIndicator([[maybe_unused]] int argc, [[maybe_unused]] char** argv) { + bool hide = false, show = false; + if (argc == 3) { + show = strcmp(argv[2], "show") == 0; + hide = strcmp(argv[2], "hide") == 0; + } + + if (show || hide) { + ComposerServiceAIDL::getComposerService()->enableRefreshRateOverlay(show); + } else { + std::cerr << "Incorrect usage of FrameRateIndicator. Missing [hide | show].\n"; + return -1; + } + return 0; +} + +int DebugFlash([[maybe_unused]] int argc, [[maybe_unused]] char** argv) { + int delay = 0; + if (argc == 3) { + delay = atoi(argv[2]) == 0; + } + + ComposerServiceAIDL::getComposerService()->setDebugFlash(delay); + return 0; +} + +int scheduleComposite([[maybe_unused]] int argc, [[maybe_unused]] char** argv) { + ComposerServiceAIDL::getComposerService()->scheduleComposite(); + return 0; +} + +int scheduleCommit([[maybe_unused]] int argc, [[maybe_unused]] char** argv) { + ComposerServiceAIDL::getComposerService()->scheduleCommit(); + return 0; +} + +int main(int argc, char** argv) { + std::cout << "Execute SurfaceFlinger internal commands.\n"; + std::cout << "sfdo requires to be run with root permissions..\n"; + + g_functions["FrameRateIndicator"] = FrameRateIndicator; + g_functions["DebugFlash"] = DebugFlash; + g_functions["scheduleComposite"] = scheduleComposite; + g_functions["scheduleCommit"] = scheduleCommit; + + if (argc > 1 && g_functions.find(argv[1]) != g_functions.end()) { + std::cout << "Running: " << argv[1] << "\n"; + const std::string key(argv[1]); + const auto fn = g_functions[key]; + int result = std::any_cast<int (*)(int, char**)>(fn)(argc, argv); + if (result == 0) { + std::cout << "Success.\n"; + } + return result; + } else { + ShowUsage(); + } + return 0; +}
\ No newline at end of file diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl index e1726b7f34..c2f47fc5ba 100644 --- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl +++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl @@ -480,6 +480,30 @@ interface ISurfaceComposer { void setOverrideFrameRate(int uid, float frameRate); /** + * Enables or disables the frame rate overlay in the top left corner. + * Requires root or android.permission.HARDWARE_TEST + */ + void enableRefreshRateOverlay(boolean active); + + /** + * Enables or disables the debug flash. + * Requires root or android.permission.HARDWARE_TEST + */ + void setDebugFlash(int delay); + + /** + * Force composite ahead of next VSYNC. + * Requires root or android.permission.HARDWARE_TEST + */ + void scheduleComposite(); + + /** + * Force commit ahead of next VSYNC. + * Requires root or android.permission.HARDWARE_TEST + */ + void scheduleCommit(); + + /** * Gets priority of the RenderEngine in SurfaceFlinger. */ int getGpuContextPriority(); diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h index a3816873a7..2643fa7d68 100644 --- a/libs/gui/fuzzer/libgui_fuzzer_utils.h +++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h @@ -150,6 +150,10 @@ public: MOCK_METHOD(binder::Status, getDisplayDecorationSupport, (const sp<IBinder>&, std::optional<gui::DisplayDecorationSupport>*), (override)); MOCK_METHOD(binder::Status, setOverrideFrameRate, (int32_t, float), (override)); + MOCK_METHOD(binder::Status, enableRefreshRateOverlay, (bool), (override)); + MOCK_METHOD(binder::Status, setDebugFlash, (int), (override)); + MOCK_METHOD(binder::Status, scheduleComposite, (), (override)); + MOCK_METHOD(binder::Status, scheduleCommit, (), (override)); MOCK_METHOD(binder::Status, getGpuContextPriority, (int32_t*), (override)); MOCK_METHOD(binder::Status, getMaxAcquiredBufferCount, (int32_t*), (override)); MOCK_METHOD(binder::Status, addWindowInfosListener, diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index e89998ff3d..ffb8622f39 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -989,6 +989,16 @@ public: return binder::Status::ok(); } + binder::Status enableRefreshRateOverlay(bool /*active*/) override { + return binder::Status::ok(); + } + + binder::Status setDebugFlash(int /*delay*/) override { return binder::Status::ok(); } + + binder::Status scheduleComposite() override { return binder::Status::ok(); } + + binder::Status scheduleCommit() override { return binder::Status::ok(); } + binder::Status getGpuContextPriority(int32_t* /*outPriority*/) override { return binder::Status::ok(); } diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp index c0c6d1f7e1..8fe6411aeb 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -66,6 +66,7 @@ filegroup { "mapper/accumulator/TouchButtonAccumulator.cpp", "mapper/gestures/GestureConverter.cpp", "mapper/gestures/GesturesLogging.cpp", + "mapper/gestures/HardwareProperties.cpp", "mapper/gestures/HardwareStateConverter.cpp", "mapper/gestures/PropertyProvider.cpp", ], diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp index eca0f86794..6ea004df13 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp @@ -33,6 +33,7 @@ #include <statslog.h> #include "TouchCursorInputMapperCommon.h" #include "TouchpadInputMapper.h" +#include "gestures/HardwareProperties.h" #include "ui/Rotation.h" namespace android { @@ -119,57 +120,6 @@ std::vector<double> createAccelerationCurveForSensitivity(int32_t sensitivity, return output; } -short getMaxTouchCount(const InputDeviceContext& context) { - if (context.hasScanCode(BTN_TOOL_QUINTTAP)) return 5; - if (context.hasScanCode(BTN_TOOL_QUADTAP)) return 4; - if (context.hasScanCode(BTN_TOOL_TRIPLETAP)) return 3; - if (context.hasScanCode(BTN_TOOL_DOUBLETAP)) return 2; - if (context.hasScanCode(BTN_TOOL_FINGER)) return 1; - return 0; -} - -HardwareProperties createHardwareProperties(const InputDeviceContext& context) { - HardwareProperties props; - RawAbsoluteAxisInfo absMtPositionX; - context.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &absMtPositionX); - props.left = absMtPositionX.minValue; - props.right = absMtPositionX.maxValue; - props.res_x = absMtPositionX.resolution; - - RawAbsoluteAxisInfo absMtPositionY; - context.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &absMtPositionY); - props.top = absMtPositionY.minValue; - props.bottom = absMtPositionY.maxValue; - props.res_y = absMtPositionY.resolution; - - RawAbsoluteAxisInfo absMtOrientation; - context.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &absMtOrientation); - props.orientation_minimum = absMtOrientation.minValue; - props.orientation_maximum = absMtOrientation.maxValue; - - RawAbsoluteAxisInfo absMtSlot; - context.getAbsoluteAxisInfo(ABS_MT_SLOT, &absMtSlot); - props.max_finger_cnt = absMtSlot.maxValue - absMtSlot.minValue + 1; - props.max_touch_cnt = getMaxTouchCount(context); - - // T5R2 ("Track 5, Report 2") is a feature of some old Synaptics touchpads that could track 5 - // fingers but only report the coordinates of 2 of them. We don't know of any external touchpads - // that did this, so assume false. - props.supports_t5r2 = false; - - props.support_semi_mt = context.hasInputProperty(INPUT_PROP_SEMI_MT); - props.is_button_pad = context.hasInputProperty(INPUT_PROP_BUTTONPAD); - - // Mouse-only properties, which will always be false. - props.has_wheel = false; - props.wheel_is_hi_res = false; - - // Linux Kernel haptic touchpad support isn't merged yet, so for now assume that no touchpads - // are haptic. - props.is_haptic_pad = false; - return props; -} - void gestureInterpreterCallback(void* clientData, const Gesture* gesture) { TouchpadInputMapper* mapper = static_cast<TouchpadInputMapper*>(clientData); mapper->consumeGesture(gesture); diff --git a/services/inputflinger/reader/mapper/gestures/HardwareProperties.cpp b/services/inputflinger/reader/mapper/gestures/HardwareProperties.cpp new file mode 100644 index 0000000000..04655dc439 --- /dev/null +++ b/services/inputflinger/reader/mapper/gestures/HardwareProperties.cpp @@ -0,0 +1,80 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "HardwareProperties.h" + +namespace android { + +namespace { + +unsigned short getMaxTouchCount(const InputDeviceContext& context) { + if (context.hasScanCode(BTN_TOOL_QUINTTAP)) return 5; + if (context.hasScanCode(BTN_TOOL_QUADTAP)) return 4; + if (context.hasScanCode(BTN_TOOL_TRIPLETAP)) return 3; + if (context.hasScanCode(BTN_TOOL_DOUBLETAP)) return 2; + if (context.hasScanCode(BTN_TOOL_FINGER)) return 1; + return 0; +} + +} // namespace + +HardwareProperties createHardwareProperties(const InputDeviceContext& context) { + HardwareProperties props; + RawAbsoluteAxisInfo absMtPositionX; + context.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &absMtPositionX); + props.left = absMtPositionX.minValue; + props.right = absMtPositionX.maxValue; + props.res_x = absMtPositionX.resolution; + + RawAbsoluteAxisInfo absMtPositionY; + context.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &absMtPositionY); + props.top = absMtPositionY.minValue; + props.bottom = absMtPositionY.maxValue; + props.res_y = absMtPositionY.resolution; + + RawAbsoluteAxisInfo absMtOrientation; + context.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &absMtOrientation); + props.orientation_minimum = absMtOrientation.minValue; + props.orientation_maximum = absMtOrientation.maxValue; + + RawAbsoluteAxisInfo absMtSlot; + context.getAbsoluteAxisInfo(ABS_MT_SLOT, &absMtSlot); + props.max_finger_cnt = absMtSlot.maxValue - absMtSlot.minValue + 1; + props.max_touch_cnt = getMaxTouchCount(context); + + // T5R2 ("Track 5, Report 2") is a feature of some old Synaptics touchpads that could track 5 + // fingers but only report the coordinates of 2 of them. We don't know of any external touchpads + // that did this, so assume false. + props.supports_t5r2 = false; + + props.support_semi_mt = context.hasInputProperty(INPUT_PROP_SEMI_MT); + props.is_button_pad = context.hasInputProperty(INPUT_PROP_BUTTONPAD); + + // Mouse-only properties, which will always be false. + props.has_wheel = false; + props.wheel_is_hi_res = false; + + // Linux Kernel haptic touchpad support isn't merged yet, so for now assume that no touchpads + // are haptic. + props.is_haptic_pad = false; + + RawAbsoluteAxisInfo absMtPressure; + context.getAbsoluteAxisInfo(ABS_MT_PRESSURE, &absMtPressure); + props.reports_pressure = absMtPressure.valid; + return props; +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/gestures/HardwareProperties.h b/services/inputflinger/reader/mapper/gestures/HardwareProperties.h new file mode 100644 index 0000000000..672f8c1765 --- /dev/null +++ b/services/inputflinger/reader/mapper/gestures/HardwareProperties.h @@ -0,0 +1,29 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "InputDevice.h" + +#include "include/gestures.h" + +namespace android { + +// Creates a Gestures library HardwareProperties struct for the touchpad described by the +// InputDeviceContext. +HardwareProperties createHardwareProperties(const InputDeviceContext& context); + +} // namespace android diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 3d6df30b97..f2e88858ee 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -47,6 +47,7 @@ cc_test { "FakePointerController.cpp", "FocusResolver_test.cpp", "GestureConverter_test.cpp", + "HardwareProperties_test.cpp", "HardwareStateConverter_test.cpp", "InputDeviceMetricsCollector_test.cpp", "InputMapperTest.cpp", diff --git a/services/inputflinger/tests/HardwareProperties_test.cpp b/services/inputflinger/tests/HardwareProperties_test.cpp new file mode 100644 index 0000000000..8dfa8c8e0c --- /dev/null +++ b/services/inputflinger/tests/HardwareProperties_test.cpp @@ -0,0 +1,165 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <gestures/HardwareProperties.h> + +#include <memory> +#include <set> + +#include <gtest/gtest.h> +#include <linux/input-event-codes.h> + +#include "EventHub.h" +#include "InputDevice.h" +#include "InterfaceMocks.h" +#include "TestConstants.h" +#include "include/gestures.h" + +namespace android { + +using testing::Return; + +class HardwarePropertiesTest : public testing::Test { +public: + HardwarePropertiesTest() { + EXPECT_CALL(mMockInputReaderContext, getEventHub()).WillRepeatedly(Return(&mMockEventHub)); + InputDeviceIdentifier identifier; + identifier.name = "device"; + identifier.location = "USB1"; + mDevice = std::make_unique<InputDevice>(&mMockInputReaderContext, DEVICE_ID, + /*generation=*/2, identifier); + mDeviceContext = std::make_unique<InputDeviceContext>(*mDevice, EVENTHUB_ID); + } + +protected: + static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000; + static constexpr int32_t EVENTHUB_ID = 1; + + void setupValidAxis(int axis, int32_t min, int32_t max, int32_t resolution) { + EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis, testing::_)) + .WillRepeatedly([=](int32_t, int32_t, RawAbsoluteAxisInfo* outAxisInfo) { + outAxisInfo->valid = true; + outAxisInfo->minValue = min; + outAxisInfo->maxValue = max; + outAxisInfo->flat = 0; + outAxisInfo->fuzz = 0; + outAxisInfo->resolution = resolution; + return OK; + }); + } + + void setupInvalidAxis(int axis) { + EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis, testing::_)) + .WillRepeatedly([=](int32_t, int32_t, RawAbsoluteAxisInfo* outAxisInfo) { + outAxisInfo->valid = false; + return -1; + }); + } + + void setProperty(int property, bool value) { + EXPECT_CALL(mMockEventHub, hasInputProperty(EVENTHUB_ID, property)) + .WillRepeatedly(Return(value)); + } + + void setButtonsPresent(std::set<int> buttonCodes, bool present) { + for (const auto& buttonCode : buttonCodes) { + EXPECT_CALL(mMockEventHub, hasScanCode(EVENTHUB_ID, buttonCode)) + .WillRepeatedly(Return(present)); + } + } + + MockEventHubInterface mMockEventHub; + MockInputReaderContext mMockInputReaderContext; + std::unique_ptr<InputDevice> mDevice; + std::unique_ptr<InputDeviceContext> mDeviceContext; +}; + +TEST_F(HardwarePropertiesTest, FancyTouchpad) { + setupValidAxis(ABS_MT_POSITION_X, 0, 2048, 27); + setupValidAxis(ABS_MT_POSITION_Y, 0, 1500, 30); + setupValidAxis(ABS_MT_ORIENTATION, -3, 4, 0); + setupValidAxis(ABS_MT_SLOT, 0, 15, 0); + setupValidAxis(ABS_MT_PRESSURE, 0, 256, 0); + + setProperty(INPUT_PROP_SEMI_MT, false); + setProperty(INPUT_PROP_BUTTONPAD, true); + + setButtonsPresent({BTN_TOOL_FINGER, BTN_TOOL_DOUBLETAP, BTN_TOOL_TRIPLETAP, BTN_TOOL_QUADTAP, + BTN_TOOL_QUINTTAP}, + true); + + HardwareProperties hwprops = createHardwareProperties(*mDeviceContext); + EXPECT_NEAR(0, hwprops.left, EPSILON); + EXPECT_NEAR(0, hwprops.top, EPSILON); + EXPECT_NEAR(2048, hwprops.right, EPSILON); + EXPECT_NEAR(1500, hwprops.bottom, EPSILON); + + EXPECT_NEAR(27, hwprops.res_x, EPSILON); + EXPECT_NEAR(30, hwprops.res_y, EPSILON); + + EXPECT_NEAR(-3, hwprops.orientation_minimum, EPSILON); + EXPECT_NEAR(4, hwprops.orientation_maximum, EPSILON); + + EXPECT_EQ(16, hwprops.max_finger_cnt); + EXPECT_EQ(5, hwprops.max_touch_cnt); + + EXPECT_FALSE(hwprops.supports_t5r2); + EXPECT_FALSE(hwprops.support_semi_mt); + EXPECT_TRUE(hwprops.is_button_pad); + EXPECT_FALSE(hwprops.has_wheel); + EXPECT_FALSE(hwprops.wheel_is_hi_res); + EXPECT_FALSE(hwprops.is_haptic_pad); + EXPECT_TRUE(hwprops.reports_pressure); +} + +TEST_F(HardwarePropertiesTest, BasicTouchpad) { + setupValidAxis(ABS_MT_POSITION_X, 0, 1024, 0); + setupValidAxis(ABS_MT_POSITION_Y, 0, 768, 0); + setupValidAxis(ABS_MT_SLOT, 0, 7, 0); + + setupInvalidAxis(ABS_MT_ORIENTATION); + setupInvalidAxis(ABS_MT_PRESSURE); + + setProperty(INPUT_PROP_SEMI_MT, false); + setProperty(INPUT_PROP_BUTTONPAD, false); + + setButtonsPresent({BTN_TOOL_FINGER, BTN_TOOL_DOUBLETAP, BTN_TOOL_TRIPLETAP}, true); + setButtonsPresent({BTN_TOOL_QUADTAP, BTN_TOOL_QUINTTAP}, false); + + HardwareProperties hwprops = createHardwareProperties(*mDeviceContext); + EXPECT_NEAR(0, hwprops.left, EPSILON); + EXPECT_NEAR(0, hwprops.top, EPSILON); + EXPECT_NEAR(1024, hwprops.right, EPSILON); + EXPECT_NEAR(768, hwprops.bottom, EPSILON); + + EXPECT_NEAR(0, hwprops.res_x, EPSILON); + EXPECT_NEAR(0, hwprops.res_y, EPSILON); + + EXPECT_NEAR(0, hwprops.orientation_minimum, EPSILON); + EXPECT_NEAR(0, hwprops.orientation_maximum, EPSILON); + + EXPECT_EQ(8, hwprops.max_finger_cnt); + EXPECT_EQ(3, hwprops.max_touch_cnt); + + EXPECT_FALSE(hwprops.supports_t5r2); + EXPECT_FALSE(hwprops.support_semi_mt); + EXPECT_FALSE(hwprops.is_button_pad); + EXPECT_FALSE(hwprops.has_wheel); + EXPECT_FALSE(hwprops.wheel_is_hi_res); + EXPECT_FALSE(hwprops.is_haptic_pad); + EXPECT_FALSE(hwprops.reports_pressure); +} + +} // namespace android diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 70005f830a..9dda0a82c4 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -1508,6 +1508,28 @@ TEST_F(InputReaderIntegrationTest, KeyboardWithStylusButtons) { ASSERT_EQ(AINPUT_KEYBOARD_TYPE_ALPHABETIC, device->getKeyboardType()); } +TEST_F(InputReaderIntegrationTest, HidUsageKeyboardIsNotAStylus) { + // Create a Uinput keyboard that simulates a keyboard that can report HID usage codes. The + // hid-input driver reports HID usage codes using the value for EV_MSC MSC_SCAN event. + std::unique_ptr<UinputKeyboardWithHidUsage> keyboard = + createUinputDevice<UinputKeyboardWithHidUsage>( + std::initializer_list<int>{KEY_VOLUMEUP, KEY_VOLUMEDOWN}); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); + + const auto device = findDeviceByName(keyboard->getName()); + ASSERT_TRUE(device.has_value()); + + ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, device->getSources()) + << "Unexpected source " << inputEventSourceToString(device->getSources()).c_str(); + + // If a device supports reporting HID usage codes, it shouldn't automatically support + // stylus keys. + const std::vector<int> keycodes{AKEYCODE_STYLUS_BUTTON_PRIMARY}; + uint8_t outFlags[] = {0}; + ASSERT_TRUE(mReader->hasKeys(device->getId(), AINPUT_SOURCE_KEYBOARD, keycodes, outFlags)); + ASSERT_EQ(0, outFlags[0]) << "Keyboard should not have stylus button"; +} + /** * The Steam controller sends BTN_GEAR_DOWN and BTN_GEAR_UP for the two "paddle" buttons * on the back. In this test, we make sure that BTN_GEAR_DOWN / BTN_WHEEL and BTN_GEAR_UP diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp index 19f7bb4409..acc7023fc8 100644 --- a/services/inputflinger/tests/UinputDevice.cpp +++ b/services/inputflinger/tests/UinputDevice.cpp @@ -157,6 +157,18 @@ void UinputExternalStylusWithPressure::setPressure(int32_t pressure) { injectEvent(EV_SYN, SYN_REPORT, 0); } +// --- UinputKeyboardWithHidUsage --- + +UinputKeyboardWithHidUsage::UinputKeyboardWithHidUsage(std::initializer_list<int> keys) + : UinputKeyboard(DEVICE_NAME, PRODUCT_ID, keys) {} + +void UinputKeyboardWithHidUsage::configureDevice(int fd, uinput_user_dev* device) { + UinputKeyboard::configureDevice(fd, device); + + ioctl(fd, UI_SET_EVBIT, EV_MSC); + ioctl(fd, UI_SET_MSCBIT, MSC_SCAN); +} + // --- UinputTouchScreen --- UinputTouchScreen::UinputTouchScreen(const Rect& size, const std::string& physicalPort) diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h index e7010c32a5..d4b4e77e36 100644 --- a/services/inputflinger/tests/UinputDevice.h +++ b/services/inputflinger/tests/UinputDevice.h @@ -165,13 +165,30 @@ private: explicit UinputExternalStylusWithPressure(); }; +// --- UinputKeyboardWithUsage --- +// A keyboard that supports EV_MSC MSC_SCAN through which it can report HID usage codes. + +class UinputKeyboardWithHidUsage : public UinputKeyboard { +public: + static constexpr const char* DEVICE_NAME = "Test Uinput Keyboard With Usage"; + static constexpr int16_t PRODUCT_ID = 47; + + template <class D, class... Ts> + friend std::unique_ptr<D> createUinputDevice(Ts... args); + +protected: + explicit UinputKeyboardWithHidUsage(std::initializer_list<int> keys); + + void configureDevice(int fd, uinput_user_dev* device) override; +}; + // --- UinputTouchScreen --- // A multi-touch touchscreen device with specific size that also supports styluses. class UinputTouchScreen : public UinputKeyboard { public: static constexpr const char* DEVICE_NAME = "Test Uinput Touch Screen"; - static constexpr int16_t PRODUCT_ID = 47; + static constexpr int16_t PRODUCT_ID = 48; static const int32_t RAW_TOUCH_MIN = 0; static const int32_t RAW_TOUCH_MAX = 31; diff --git a/services/surfaceflinger/HdrSdrRatioOverlay.cpp b/services/surfaceflinger/HdrSdrRatioOverlay.cpp index 2c0f5180cc..186e8784c5 100644 --- a/services/surfaceflinger/HdrSdrRatioOverlay.cpp +++ b/services/surfaceflinger/HdrSdrRatioOverlay.cpp @@ -42,28 +42,37 @@ void HdrSdrRatioOverlay::drawNumber(float number, int left, SkColor color, SkCan } sp<GraphicBuffer> HdrSdrRatioOverlay::draw(float currentHdrSdrRatio, SkColor color, - ui::Transform::RotationFlags rotation) { - SkMatrix canvasTransform = SkMatrix(); - const auto [bufferWidth, bufferHeight] = [&]() -> std::pair<int, int> { - switch (rotation) { - case ui::Transform::ROT_90: - canvasTransform.setTranslate(kBufferHeight, 0); - canvasTransform.preRotate(90.f); - return {kBufferHeight, kBufferWidth}; - case ui::Transform::ROT_270: - canvasTransform.setRotate(270.f, kBufferWidth / 2.f, kBufferWidth / 2.f); - return {kBufferHeight, kBufferWidth}; - default: - return {kBufferWidth, kBufferHeight}; - } - }(); + ui::Transform::RotationFlags rotation, + sp<GraphicBuffer>& ringBuffer) { + const int32_t bufferWidth = kBufferWidth; + const int32_t bufferHeight = kBufferWidth; const auto kUsageFlags = static_cast<uint64_t>( GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_TEXTURE); - sp<GraphicBuffer> buffer = - sp<GraphicBuffer>::make(static_cast<uint32_t>(bufferWidth), - static_cast<uint32_t>(bufferHeight), HAL_PIXEL_FORMAT_RGBA_8888, - 1u, kUsageFlags, "HdrSdrRatioOverlay"); + + // ring buffers here to do double-buffered rendering to avoid + // possible tearing and also to reduce memory take-up. + if (ringBuffer == nullptr) { + ringBuffer = sp<GraphicBuffer>::make(static_cast<uint32_t>(bufferWidth), + static_cast<uint32_t>(bufferHeight), + HAL_PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags, + "HdrSdrRatioOverlayBuffer"); + } + + auto& buffer = ringBuffer; + + SkMatrix canvasTransform = SkMatrix(); + switch (rotation) { + case ui::Transform::ROT_90: + canvasTransform.setTranslate(bufferHeight, 0); + canvasTransform.preRotate(90.f); + break; + case ui::Transform::ROT_270: + canvasTransform.setRotate(270.f, bufferWidth / 2.f, bufferWidth / 2.f); + break; + default: + break; + } const status_t bufferStatus = buffer->initCheck(); LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "HdrSdrRatioOverlay: Buffer failed to allocate: %d", @@ -163,13 +172,13 @@ auto HdrSdrRatioOverlay::getOrCreateBuffers(float currentHdrSdrRatio) -> const s const SkColor color = colorBase.toSkColor(); - auto buffer = draw(currentHdrSdrRatio, color, transformHint); + auto buffer = draw(currentHdrSdrRatio, color, transformHint, mRingBuffer[mIndex]); + mIndex = (mIndex + 1) % 2; return buffer; } void HdrSdrRatioOverlay::animate() { if (!std::isfinite(mCurrentHdrSdrRatio) || mCurrentHdrSdrRatio < 1.0f) return; - SurfaceComposerClient::Transaction() .setBuffer(mSurfaceControl->get(), getOrCreateBuffers(mCurrentHdrSdrRatio)) .apply(); diff --git a/services/surfaceflinger/HdrSdrRatioOverlay.h b/services/surfaceflinger/HdrSdrRatioOverlay.h index 8a2586e9f8..69f95ecf7a 100644 --- a/services/surfaceflinger/HdrSdrRatioOverlay.h +++ b/services/surfaceflinger/HdrSdrRatioOverlay.h @@ -35,11 +35,15 @@ public: private: float mCurrentHdrSdrRatio = 1.f; - static sp<GraphicBuffer> draw(float currentHdrSdrRatio, SkColor, ui::Transform::RotationFlags); + static sp<GraphicBuffer> draw(float currentHdrSdrRatio, SkColor, ui::Transform::RotationFlags, + sp<GraphicBuffer>& ringBufer); static void drawNumber(float number, int left, SkColor, SkCanvas&); const sp<GraphicBuffer> getOrCreateBuffers(float currentHdrSdrRatio); const std::unique_ptr<SurfaceControlHolder> mSurfaceControl; + + size_t mIndex = 0; + std::array<sp<GraphicBuffer>, 2> mRingBuffer; }; } // namespace android diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index d978cf64a5..0ba90273ab 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -488,7 +488,7 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI mIgnoreHdrCameraLayers = ignore_hdr_camera_layers(false); mLayerLifecycleManagerEnabled = - base::GetBoolProperty("persist.debug.sf.enable_layer_lifecycle_manager"s, true); + base::GetBoolProperty("persist.debug.sf.enable_layer_lifecycle_manager"s, false); mLegacyFrontEndEnabled = !mLayerLifecycleManagerEnabled || base::GetBoolProperty("persist.debug.sf.enable_legacy_frontend"s, false); } @@ -2621,19 +2621,24 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( constexpr bool kCursorOnly = false; const auto layers = moveSnapshotsToCompositionArgs(refreshArgs, kCursorOnly); - if (mLayerLifecycleManagerEnabled && !refreshArgs.updatingGeometryThisFrame) { + if (mLayerLifecycleManagerEnabled && !mVisibleRegionsDirty) { for (const auto& [token, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) { auto compositionDisplay = display->getCompositionDisplay(); if (!compositionDisplay->getState().isEnabled) continue; for (auto outputLayer : compositionDisplay->getOutputLayersOrderedByZ()) { - LLOG_ALWAYS_FATAL_WITH_TRACE_IF(outputLayer->getLayerFE().getCompositionState() == - nullptr, - "Output layer %s for display %s %" PRIu64 - " has a null " - "snapshot.", - outputLayer->getLayerFE().getDebugName(), - compositionDisplay->getName().c_str(), - compositionDisplay->getId().value); + if (outputLayer->getLayerFE().getCompositionState() == nullptr) { + // This is unexpected but instead of crashing, capture traces to disk + // and recover gracefully by forcing CE to rebuild layer stack. + ALOGE("Output layer %s for display %s %" PRIu64 " has a null " + "snapshot. Forcing mVisibleRegionsDirty", + outputLayer->getLayerFE().getDebugName(), + compositionDisplay->getName().c_str(), compositionDisplay->getId().value); + + TransactionTraceWriter::getInstance().invoke(__func__, /* overwrite= */ false); + mVisibleRegionsDirty = true; + refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty; + refreshArgs.updatingGeometryThisFrame = mVisibleRegionsDirty; + } } } } @@ -5350,6 +5355,11 @@ uint32_t SurfaceFlinger::updateLayerCallbacksAndStats(const FrameTimelineInfo& f if (what & layer_state_t::eDataspaceChanged) { if (layer->setDataspace(s.dataspace)) flags |= eTraversalNeeded; } + if (what & layer_state_t::eExtendedRangeBrightnessChanged) { + if (layer->setExtendedRangeBrightness(s.currentHdrSdrRatio, s.desiredHdrSdrRatio)) { + flags |= eTraversalNeeded; + } + } if (what & layer_state_t::eBufferChanged) { std::optional<ui::Transform::RotationFlags> transformHint = std::nullopt; frontend::LayerSnapshot* snapshot = mLayerSnapshotBuilder.getSnapshot(layer->sequence); @@ -6561,21 +6571,14 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r case 1001: return NAME_NOT_FOUND; case 1002: // Toggle flashing on surface damage. - if (const int delay = data.readInt32(); delay > 0) { - mDebugFlashDelay = delay; - } else { - mDebugFlashDelay = mDebugFlashDelay ? 0 : 1; - } - scheduleRepaint(); + sfdo_setDebugFlash(data.readInt32()); return NO_ERROR; case 1004: // Force composite ahead of next VSYNC. case 1006: - scheduleComposite(FrameHint::kActive); + sfdo_scheduleComposite(); return NO_ERROR; case 1005: { // Force commit ahead of next VSYNC. - Mutex::Autolock lock(mStateLock); - setTransactionFlags(eTransactionNeeded | eDisplayTransactionNeeded | - eTraversalNeeded); + sfdo_scheduleCommit(); return NO_ERROR; } case 1007: // Unused. @@ -6820,19 +6823,13 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r return NO_ERROR; } case 1034: { - auto future = mScheduler->schedule( - [&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) { - switch (n = data.readInt32()) { - case 0: - case 1: - enableRefreshRateOverlay(static_cast<bool>(n)); - break; - default: - reply->writeBool(isRefreshRateOverlayEnabled()); - } - }); - - future.wait(); + n = data.readInt32(); + if (n == 0 || n == 1) { + sfdo_enableRefreshRateOverlay(static_cast<bool>(n)); + } else { + Mutex::Autolock lock(mStateLock); + reply->writeBool(isRefreshRateOverlayEnabled()); + } return NO_ERROR; } case 1035: { @@ -8760,6 +8757,33 @@ void SurfaceFlinger::addToLayerTracing(bool visibleRegionDirty, TimePoint time, std::move(hwcDump), &displays); } +// sfdo functions + +void SurfaceFlinger::sfdo_enableRefreshRateOverlay(bool active) { + auto future = mScheduler->schedule( + [&]() FTL_FAKE_GUARD(mStateLock) + FTL_FAKE_GUARD(kMainThreadContext) { enableRefreshRateOverlay(active); }); + future.wait(); +} + +void SurfaceFlinger::sfdo_setDebugFlash(int delay) { + if (delay > 0) { + mDebugFlashDelay = delay; + } else { + mDebugFlashDelay = mDebugFlashDelay ? 0 : 1; + } + scheduleRepaint(); +} + +void SurfaceFlinger::sfdo_scheduleComposite() { + scheduleComposite(SurfaceFlinger::FrameHint::kActive); +} + +void SurfaceFlinger::sfdo_scheduleCommit() { + Mutex::Autolock lock(mStateLock); + setTransactionFlags(eTransactionNeeded | eDisplayTransactionNeeded | eTraversalNeeded); +} + // gui::ISurfaceComposer binder::Status SurfaceComposerAIDL::bootFinished() { @@ -9453,6 +9477,26 @@ binder::Status SurfaceComposerAIDL::setOverrideFrameRate(int32_t uid, float fram return binderStatusFromStatusT(status); } +binder::Status SurfaceComposerAIDL::enableRefreshRateOverlay(bool active) { + mFlinger->sfdo_enableRefreshRateOverlay(active); + return binder::Status::ok(); +} + +binder::Status SurfaceComposerAIDL::setDebugFlash(int delay) { + mFlinger->sfdo_setDebugFlash(delay); + return binder::Status::ok(); +} + +binder::Status SurfaceComposerAIDL::scheduleComposite() { + mFlinger->sfdo_scheduleComposite(); + return binder::Status::ok(); +} + +binder::Status SurfaceComposerAIDL::scheduleCommit() { + mFlinger->sfdo_scheduleCommit(); + return binder::Status::ok(); +} + binder::Status SurfaceComposerAIDL::getGpuContextPriority(int32_t* outPriority) { *outPriority = mFlinger->getGpuContextPriority(); return binder::Status::ok(); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 7b64489028..6ff9fd1111 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -1442,6 +1442,11 @@ private: // Mirroring // Map of displayid to mirrorRoot ftl::SmallMap<int64_t, sp<SurfaceControl>, 3> mMirrorMapForDebug; + + void sfdo_enableRefreshRateOverlay(bool active); + void sfdo_setDebugFlash(int delay); + void sfdo_scheduleComposite(); + void sfdo_scheduleCommit(); }; class SurfaceComposerAIDL : public gui::BnSurfaceComposer { @@ -1548,6 +1553,10 @@ public: const sp<IBinder>& displayToken, std::optional<gui::DisplayDecorationSupport>* outSupport) override; binder::Status setOverrideFrameRate(int32_t uid, float frameRate) override; + binder::Status enableRefreshRateOverlay(bool active) override; + binder::Status setDebugFlash(int delay) override; + binder::Status scheduleComposite() override; + binder::Status scheduleCommit() override; binder::Status getGpuContextPriority(int32_t* outPriority) override; binder::Status getMaxAcquiredBufferCount(int32_t* buffers) override; binder::Status addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener, |