diff options
28 files changed, 668 insertions, 226 deletions
diff --git a/include/input/Input.h b/include/input/Input.h index ec08cdd163..456977bb34 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -25,6 +25,7 @@ #include <android/input.h> #ifdef __linux__ #include <android/os/IInputConstants.h> +#include <android/os/MotionEventFlag.h> #endif #include <android/os/PointerIconType.h> #include <math.h> @@ -69,15 +70,17 @@ enum { * actual intent. */ AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED = - android::os::IInputConstants::MOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED, + static_cast<int32_t>(android::os::MotionEventFlag::WINDOW_IS_PARTIALLY_OBSCURED), + AMOTION_EVENT_FLAG_HOVER_EXIT_PENDING = - android::os::IInputConstants::MOTION_EVENT_FLAG_HOVER_EXIT_PENDING, + static_cast<int32_t>(android::os::MotionEventFlag::HOVER_EXIT_PENDING), + /** * This flag indicates that the event has been generated by a gesture generator. It * provides a hint to the GestureDetector to not apply any touch slop. */ AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE = - android::os::IInputConstants::MOTION_EVENT_FLAG_IS_GENERATED_GESTURE, + static_cast<int32_t>(android::os::MotionEventFlag::IS_GENERATED_GESTURE), /** * This flag indicates that the event will not cause a focus change if it is directed to an @@ -86,27 +89,27 @@ enum { * into focus. */ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE = - android::os::IInputConstants::MOTION_EVENT_FLAG_NO_FOCUS_CHANGE, + static_cast<int32_t>(android::os::MotionEventFlag::NO_FOCUS_CHANGE), /** * This event was generated or modified by accessibility service. */ AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT = - android::os::IInputConstants::INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT, + static_cast<int32_t>(android::os::MotionEventFlag::IS_ACCESSIBILITY_EVENT), AMOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS = - android::os::IInputConstants::MOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS, + static_cast<int32_t>(android::os::MotionEventFlag::TARGET_ACCESSIBILITY_FOCUS), /* Motion event is inconsistent with previously sent motion events. */ - AMOTION_EVENT_FLAG_TAINTED = android::os::IInputConstants::INPUT_EVENT_FLAG_TAINTED, + AMOTION_EVENT_FLAG_TAINTED = static_cast<int32_t>(android::os::MotionEventFlag::TAINTED), /** Private flag, not used in Java. */ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION = - android::os::IInputConstants::MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION, + static_cast<int32_t>(android::os::MotionEventFlag::PRIVATE_FLAG_SUPPORTS_ORIENTATION), /** Private flag, not used in Java. */ - AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION = android::os::IInputConstants:: - MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION, + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION = static_cast<int32_t>( + android::os::MotionEventFlag::PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION), /** Mask for all private flags that are not used in Java. */ AMOTION_EVENT_PRIVATE_FLAG_MASK = AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION | diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h index 9cf62bc7d6..7ee291df4c 100644 --- a/libs/gui/include/gui/LayerMetadata.h +++ b/libs/gui/include/gui/LayerMetadata.h @@ -74,6 +74,7 @@ enum class GameMode : int32_t { } // namespace android::gui using android::gui::METADATA_ACCESSIBILITY_ID; +using android::gui::METADATA_CALLING_UID; using android::gui::METADATA_DEQUEUE_TIME; using android::gui::METADATA_GAME_MODE; using android::gui::METADATA_MOUSE_CURSOR; diff --git a/libs/input/Android.bp b/libs/input/Android.bp index d782f42071..c2a7ebb614 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -30,6 +30,7 @@ filegroup { "android/os/InputEventInjectionResult.aidl", "android/os/InputEventInjectionSync.aidl", "android/os/InputConfig.aidl", + "android/os/MotionEventFlag.aidl", "android/os/PointerIconType.aidl", ], } diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl index a77dfa59fe..e23fc94c5e 100644 --- a/libs/input/android/os/IInputConstants.aidl +++ b/libs/input/android/os/IInputConstants.aidl @@ -49,130 +49,24 @@ interface IInputConstants const int POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY = 0x20000; /** - * This flag indicates that the window that received this motion event is partly - * or wholly obscured by another visible window above it and the event directly passed through - * the obscured area. - * - * A security sensitive application can check this flag to identify situations in which - * a malicious application may have covered up part of its content for the purpose - * of misleading the user or hijacking touches. An appropriate response might be - * to drop the suspect touches or to take additional precautions to confirm the user's - * actual intent. - */ - const int MOTION_EVENT_FLAG_WINDOW_IS_OBSCURED = 0x1; - - /** - * This flag indicates that the window that received this motion event is partly - * or wholly obscured by another visible window above it and the event did not directly pass - * through the obscured area. - * - * A security sensitive application can check this flag to identify situations in which - * a malicious application may have covered up part of its content for the purpose - * of misleading the user or hijacking touches. An appropriate response might be - * to drop the suspect touches or to take additional precautions to confirm the user's - * actual intent. - * - * Unlike FLAG_WINDOW_IS_OBSCURED, this is only true if the window that received this event is - * obstructed in areas other than the touched location. - */ - const int MOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 0x2; - - /** - * This private flag is only set on {@link #ACTION_HOVER_MOVE} events and indicates that - * this event will be immediately followed by a {@link #ACTION_HOVER_EXIT}. It is used to - * prevent generating redundant {@link #ACTION_HOVER_ENTER} events. - * @hide - */ - const int MOTION_EVENT_FLAG_HOVER_EXIT_PENDING = 0x4; - - /** - * This flag indicates that the event has been generated by a gesture generator. It - * provides a hint to the GestureDetector to not apply any touch slop. - * - * @hide - */ - const int MOTION_EVENT_FLAG_IS_GENERATED_GESTURE = 0x8; - - /** - * This flag is only set for events with {@link #ACTION_POINTER_UP} and {@link #ACTION_CANCEL}. - * It indicates that the pointer going up was an unintentional user touch. When FLAG_CANCELED - * is set, the typical actions that occur in response for a pointer going up (such as click - * handlers, end of drawing) should be aborted. This flag is typically set when the user was - * accidentally touching the screen, such as by gripping the device, or placing the palm on the - * screen. - * - * @see #ACTION_POINTER_UP - * @see #ACTION_CANCEL + * Common input event flag used for both motion and key events for a gesture or pointer being + * canceled. */ const int INPUT_EVENT_FLAG_CANCELED = 0x20; /** - * This flag indicates that the event will not cause a focus change if it is directed to an - * unfocused window, even if it an {@link #ACTION_DOWN}. This is typically used with pointer - * gestures to allow the user to direct gestures to an unfocused window without bringing the - * window into focus. - * @hide - */ - const int MOTION_EVENT_FLAG_NO_FOCUS_CHANGE = 0x40; - - /** - * This flag indicates that the event has a valid value for AXIS_ORIENTATION. - * - * This is a private flag that is not used in Java. - * @hide - */ - const int MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION = 0x80; - - /** - * This flag indicates that the pointers' AXIS_ORIENTATION can be used to precisely determine - * the direction in which the tool is pointing. The value of the orientation axis will be in - * the range [-pi, pi], which represents a full circle. This is usually supported by devices - * like styluses. - * - * Conversely, AXIS_ORIENTATION cannot be used to tell which direction the tool is pointing - * when this flag is not set. In this case, the axis value will have a range of [-pi/2, pi/2], - * which represents half a circle. This is usually the case for devices like touchscreens and - * touchpads, for which it is difficult to tell which direction along the major axis of the - * touch ellipse the finger is pointing. - * - * This is a private flag that is not used in Java. - * @hide - */ - const int MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION = 0x100; - - /** - * The input event was generated or modified by accessibility service. - * Shared by both KeyEvent and MotionEvent flags, so this value should not overlap with either - * set of flags, including in input/Input.h and in android/input.h. + * Common input event flag used for both motion and key events, indicating that the event + * was generated or modified by accessibility service. */ const int INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT = 0x800; /** - * Private flag that indicates when the system has detected that this motion event - * may be inconsistent with respect to the sequence of previously delivered motion events, - * such as when a pointer move event is sent but the pointer is not down. - * - * @hide - * @see #isTainted - * @see #setTainted + * Common input event flag used for both motion and key events, indicating that the system has + * detected this event may be inconsistent with the current event sequence or gesture, such as + * when a pointer move event is sent but the pointer is not down. */ const int INPUT_EVENT_FLAG_TAINTED = 0x80000000; - /** - * Private flag indicating that this event was synthesized by the system and should be delivered - * to the accessibility focused view first. When being dispatched such an event is not handled - * by predecessors of the accessibility focused view and after the event reaches that view the - * flag is cleared and normal event dispatch is performed. This ensures that the platform can - * click on any view that has accessibility focus which is semantically equivalent to asking the - * view to perform a click accessibility action but more generic as views not implementing click - * action correctly can still be activated. - * - * @hide - * @see #isTargetAccessibilityFocus() - * @see #setTargetAccessibilityFocus(boolean) - */ - const int MOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS = 0x40000000; - /* The default pointer acceleration value. */ const int DEFAULT_POINTER_ACCELERATION = 3; diff --git a/libs/input/android/os/MotionEventFlag.aidl b/libs/input/android/os/MotionEventFlag.aidl new file mode 100644 index 0000000000..2093b0636a --- /dev/null +++ b/libs/input/android/os/MotionEventFlag.aidl @@ -0,0 +1,152 @@ +/** + * Copyright 2024, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import android.os.IInputConstants; + +/** + * Flag definitions for MotionEvents. + * @hide + */ +@Backing(type="int") +enum MotionEventFlag { + + /** + * This flag indicates that the window that received this motion event is partly + * or wholly obscured by another visible window above it and the event directly passed through + * the obscured area. + * + * A security sensitive application can check this flag to identify situations in which + * a malicious application may have covered up part of its content for the purpose + * of misleading the user or hijacking touches. An appropriate response might be + * to drop the suspect touches or to take additional precautions to confirm the user's + * actual intent. + */ + WINDOW_IS_OBSCURED = 0x1, + + /** + * This flag indicates that the window that received this motion event is partly + * or wholly obscured by another visible window above it and the event did not directly pass + * through the obscured area. + * + * A security sensitive application can check this flag to identify situations in which + * a malicious application may have covered up part of its content for the purpose + * of misleading the user or hijacking touches. An appropriate response might be + * to drop the suspect touches or to take additional precautions to confirm the user's + * actual intent. + * + * Unlike FLAG_WINDOW_IS_OBSCURED, this is only true if the window that received this event is + * obstructed in areas other than the touched location. + */ + WINDOW_IS_PARTIALLY_OBSCURED = 0x2, + + /** + * This private flag is only set on {@link #ACTION_HOVER_MOVE} events and indicates that + * this event will be immediately followed by a {@link #ACTION_HOVER_EXIT}. It is used to + * prevent generating redundant {@link #ACTION_HOVER_ENTER} events. + * @hide + */ + HOVER_EXIT_PENDING = 0x4, + + /** + * This flag indicates that the event has been generated by a gesture generator. It + * provides a hint to the GestureDetector to not apply any touch slop. + * + * @hide + */ + IS_GENERATED_GESTURE = 0x8, + + /** + * This flag is only set for events with {@link #ACTION_POINTER_UP} and {@link #ACTION_CANCEL}. + * It indicates that the pointer going up was an unintentional user touch. When FLAG_CANCELED + * is set, the typical actions that occur in response for a pointer going up (such as click + * handlers, end of drawing) should be aborted. This flag is typically set when the user was + * accidentally touching the screen, such as by gripping the device, or placing the palm on the + * screen. + * + * @see #ACTION_POINTER_UP + * @see #ACTION_CANCEL + */ + CANCELED = IInputConstants.INPUT_EVENT_FLAG_CANCELED, + + /** + * This flag indicates that the event will not cause a focus change if it is directed to an + * unfocused window, even if it an {@link #ACTION_DOWN}. This is typically used with pointer + * gestures to allow the user to direct gestures to an unfocused window without bringing the + * window into focus. + * @hide + */ + NO_FOCUS_CHANGE = 0x40, + + /** + * This flag indicates that the event has a valid value for AXIS_ORIENTATION. + * + * This is a private flag that is not used in Java. + * @hide + */ + PRIVATE_FLAG_SUPPORTS_ORIENTATION = 0x80, + + /** + * This flag indicates that the pointers' AXIS_ORIENTATION can be used to precisely determine + * the direction in which the tool is pointing. The value of the orientation axis will be in + * the range [-pi, pi], which represents a full circle. This is usually supported by devices + * like styluses. + * + * Conversely, AXIS_ORIENTATION cannot be used to tell which direction the tool is pointing + * when this flag is not set. In this case, the axis value will have a range of [-pi/2, pi/2], + * which represents half a circle. This is usually the case for devices like touchscreens and + * touchpads, for which it is difficult to tell which direction along the major axis of the + * touch ellipse the finger is pointing. + * + * This is a private flag that is not used in Java. + * @hide + */ + PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION = 0x100, + + /** + * The input event was generated or modified by accessibility service. + * Shared by both KeyEvent and MotionEvent flags, so this value should not overlap with either + * set of flags, including in input/Input.h and in android/input.h. + */ + IS_ACCESSIBILITY_EVENT = IInputConstants.INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT, + + /** + * Private flag that indicates when the system has detected that this motion event + * may be inconsistent with respect to the sequence of previously delivered motion events, + * such as when a pointer move event is sent but the pointer is not down. + * + * @hide + * @see #isTainted + * @see #setTainted + */ + TAINTED = IInputConstants.INPUT_EVENT_FLAG_TAINTED, + + /** + * Private flag indicating that this event was synthesized by the system and should be delivered + * to the accessibility focused view first. When being dispatched such an event is not handled + * by predecessors of the accessibility focused view and after the event reaches that view the + * flag is cleared and normal event dispatch is performed. This ensures that the platform can + * click on any view that has accessibility focus which is semantically equivalent to asking the + * view to perform a click accessibility action but more generic as views not implementing click + * action correctly can still be activated. + * + * @hide + * @see #isTargetAccessibilityFocus() + * @see #setTargetAccessibilityFocus(boolean) + */ + TARGET_ACCESSIBILITY_FOCUS = 0x40000000, +} diff --git a/libs/input/rust/input.rs b/libs/input/rust/input.rs index 564d94dbb4..c46b7bb85c 100644 --- a/libs/input/rust/input.rs +++ b/libs/input/rust/input.rs @@ -19,6 +19,7 @@ use crate::ffi::RustInputDeviceIdentifier; use bitflags::bitflags; use inputconstants::aidl::android::os::IInputConstants; +use inputconstants::aidl::android::os::MotionEventFlag::MotionEventFlag; use std::fmt; /// The InputDevice ID. @@ -193,31 +194,34 @@ impl MotionAction { bitflags! { /// MotionEvent flags. + /// The source of truth for the flag definitions are the MotionEventFlag AIDL enum. + /// The flag values are redefined here as a bitflags API. #[derive(Debug)] pub struct MotionFlags: u32 { /// FLAG_WINDOW_IS_OBSCURED - const WINDOW_IS_OBSCURED = IInputConstants::MOTION_EVENT_FLAG_WINDOW_IS_OBSCURED as u32; + const WINDOW_IS_OBSCURED = MotionEventFlag::WINDOW_IS_OBSCURED.0 as u32; /// FLAG_WINDOW_IS_PARTIALLY_OBSCURED - const WINDOW_IS_PARTIALLY_OBSCURED = IInputConstants::MOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED as u32; + const WINDOW_IS_PARTIALLY_OBSCURED = MotionEventFlag::WINDOW_IS_PARTIALLY_OBSCURED.0 as u32; /// FLAG_HOVER_EXIT_PENDING - const HOVER_EXIT_PENDING = IInputConstants::MOTION_EVENT_FLAG_HOVER_EXIT_PENDING as u32; + const HOVER_EXIT_PENDING = MotionEventFlag::HOVER_EXIT_PENDING.0 as u32; /// FLAG_IS_GENERATED_GESTURE - const IS_GENERATED_GESTURE = IInputConstants::MOTION_EVENT_FLAG_IS_GENERATED_GESTURE as u32; + const IS_GENERATED_GESTURE = MotionEventFlag::IS_GENERATED_GESTURE.0 as u32; /// FLAG_CANCELED - const CANCELED = IInputConstants::INPUT_EVENT_FLAG_CANCELED as u32; + const CANCELED = MotionEventFlag::CANCELED.0 as u32; /// FLAG_NO_FOCUS_CHANGE - const NO_FOCUS_CHANGE = IInputConstants::MOTION_EVENT_FLAG_NO_FOCUS_CHANGE as u32; + const NO_FOCUS_CHANGE = MotionEventFlag::NO_FOCUS_CHANGE.0 as u32; /// PRIVATE_FLAG_SUPPORTS_ORIENTATION - const PRIVATE_SUPPORTS_ORIENTATION = IInputConstants::MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION as u32; + const PRIVATE_FLAG_SUPPORTS_ORIENTATION = + MotionEventFlag::PRIVATE_FLAG_SUPPORTS_ORIENTATION.0 as u32; /// PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION - const PRIVATE_SUPPORTS_DIRECTIONAL_ORIENTATION = - IInputConstants::MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION as u32; + const PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION = + MotionEventFlag::PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION.0 as u32; /// FLAG_IS_ACCESSIBILITY_EVENT - const IS_ACCESSIBILITY_EVENT = IInputConstants::INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT as u32; + const IS_ACCESSIBILITY_EVENT = MotionEventFlag::IS_ACCESSIBILITY_EVENT.0 as u32; /// FLAG_TAINTED - const TAINTED = IInputConstants::INPUT_EVENT_FLAG_TAINTED as u32; + const TAINTED = MotionEventFlag::TAINTED.0 as u32; /// FLAG_TARGET_ACCESSIBILITY_FOCUS - const TARGET_ACCESSIBILITY_FOCUS = IInputConstants::MOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS as u32; + const TARGET_ACCESSIBILITY_FOCUS = MotionEventFlag::TARGET_ACCESSIBILITY_FOCUS.0 as u32; } } @@ -333,10 +337,24 @@ pub enum KeyboardType { #[cfg(test)] mod tests { use crate::input::SourceClass; + use crate::MotionFlags; use crate::Source; + use inputconstants::aidl::android::os::MotionEventFlag::MotionEventFlag; + #[test] fn convert_source_class_pointer() { let source = Source::from_bits(input_bindgen::AINPUT_SOURCE_CLASS_POINTER).unwrap(); assert!(source.is_from_class(SourceClass::Pointer)); } + + /// Ensure all MotionEventFlag constants are re-defined in rust. + #[test] + fn all_motion_event_flags_defined() { + for flag in MotionEventFlag::enum_values() { + assert!( + MotionFlags::from_bits(flag.0 as u32).is_some(), + "MotionEventFlag value {flag:?} is not redefined as a rust MotionFlags" + ); + } + } } diff --git a/libs/input/rust/keyboard_classification_config.rs b/libs/input/rust/keyboard_classification_config.rs new file mode 100644 index 0000000000..ab74efb674 --- /dev/null +++ b/libs/input/rust/keyboard_classification_config.rs @@ -0,0 +1,127 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::input::KeyboardType; + +// TODO(b/263559234): Categorize some of these to KeyboardType::None based on ability to produce +// key events at all. (Requires setup allowing InputDevice to dynamically add/remove +// KeyboardInputMapper based on blocklist and KeyEvents in case a KeyboardType::None device ends +// up producing a key event) +pub static CLASSIFIED_DEVICES: &[( + /* vendorId */ u16, + /* productId */ u16, + KeyboardType, + /* is_finalized */ bool, +)] = &[ + // HP X4000 Wireless Mouse + (0x03f0, 0xa407, KeyboardType::NonAlphabetic, true), + // Microsoft Wireless Mobile Mouse 6000 + (0x045e, 0x0745, KeyboardType::NonAlphabetic, true), + // Microsoft Surface Precision Mouse + (0x045e, 0x0821, KeyboardType::NonAlphabetic, true), + // Microsoft Pro IntelliMouse + (0x045e, 0x082a, KeyboardType::NonAlphabetic, true), + // Microsoft Bluetooth Mouse + (0x045e, 0x082f, KeyboardType::NonAlphabetic, true), + // Xbox One Elite Series 2 gamepad + (0x045e, 0x0b05, KeyboardType::NonAlphabetic, true), + // Logitech T400 + (0x046d, 0x4026, KeyboardType::NonAlphabetic, true), + // Logitech M720 Triathlon (Unifying) + (0x046d, 0x405e, KeyboardType::NonAlphabetic, true), + // Logitech MX Master 2S (Unifying) + (0x046d, 0x4069, KeyboardType::NonAlphabetic, true), + // Logitech M585 (Unifying) + (0x046d, 0x406b, KeyboardType::NonAlphabetic, true), + // Logitech MX Anywhere 2 (Unifying) + (0x046d, 0x4072, KeyboardType::NonAlphabetic, true), + // Logitech Pebble M350 + (0x046d, 0x4080, KeyboardType::NonAlphabetic, true), + // Logitech T630 Ultrathin + (0x046d, 0xb00d, KeyboardType::NonAlphabetic, true), + // Logitech M558 + (0x046d, 0xb011, KeyboardType::NonAlphabetic, true), + // Logitech MX Master (Bluetooth) + (0x046d, 0xb012, KeyboardType::NonAlphabetic, true), + // Logitech MX Anywhere 2 (Bluetooth) + (0x046d, 0xb013, KeyboardType::NonAlphabetic, true), + // Logitech M720 Triathlon (Bluetooth) + (0x046d, 0xb015, KeyboardType::NonAlphabetic, true), + // Logitech M535 + (0x046d, 0xb016, KeyboardType::NonAlphabetic, true), + // Logitech MX Master / Anywhere 2 (Bluetooth) + (0x046d, 0xb017, KeyboardType::NonAlphabetic, true), + // Logitech MX Master 2S (Bluetooth) + (0x046d, 0xb019, KeyboardType::NonAlphabetic, true), + // Logitech MX Anywhere 2S (Bluetooth) + (0x046d, 0xb01a, KeyboardType::NonAlphabetic, true), + // Logitech M585/M590 (Bluetooth) + (0x046d, 0xb01b, KeyboardType::NonAlphabetic, true), + // Logitech G603 Lightspeed Gaming Mouse (Bluetooth) + (0x046d, 0xb01c, KeyboardType::NonAlphabetic, true), + // Logitech MX Master (Bluetooth) + (0x046d, 0xb01e, KeyboardType::NonAlphabetic, true), + // Logitech MX Anywhere 2 (Bluetooth) + (0x046d, 0xb01f, KeyboardType::NonAlphabetic, true), + // Logitech MX Master 3 (Bluetooth) + (0x046d, 0xb023, KeyboardType::NonAlphabetic, true), + // Logitech G604 Lightspeed Gaming Mouse (Bluetooth) + (0x046d, 0xb024, KeyboardType::NonAlphabetic, true), + // Logitech Spotlight Presentation Remote (Bluetooth) + (0x046d, 0xb503, KeyboardType::NonAlphabetic, true), + // Logitech R500 (Bluetooth) + (0x046d, 0xb505, KeyboardType::NonAlphabetic, true), + // Logitech M500s + (0x046d, 0xc093, KeyboardType::NonAlphabetic, true), + // Logitech Spotlight Presentation Remote (USB dongle) + (0x046d, 0xc53e, KeyboardType::NonAlphabetic, true), + // Elecom Enelo IR LED Mouse 350 + (0x056e, 0x0134, KeyboardType::NonAlphabetic, true), + // Elecom EPRIM Blue LED 5 button mouse 228 + (0x056e, 0x0141, KeyboardType::NonAlphabetic, true), + // Elecom Blue LED Mouse 203 + (0x056e, 0x0159, KeyboardType::NonAlphabetic, true), + // Zebra LS2208 barcode scanner + (0x05e0, 0x1200, KeyboardType::NonAlphabetic, true), + // RDing FootSwitch1F1 + (0x0c45, 0x7403, KeyboardType::NonAlphabetic, true), + // SteelSeries Sensei RAW Frost Blue + (0x1038, 0x1369, KeyboardType::NonAlphabetic, true), + // SteelSeries Rival 3 Wired + (0x1038, 0x1824, KeyboardType::NonAlphabetic, true), + // SteelSeries Rival 3 Wireless (USB dongle) + (0x1038, 0x1830, KeyboardType::NonAlphabetic, true), + // Yubico.com Yubikey + (0x1050, 0x0010, KeyboardType::NonAlphabetic, true), + // Yubico.com Yubikey 4 OTP+U2F+CCID + (0x1050, 0x0407, KeyboardType::NonAlphabetic, true), + // Lenovo USB-C Wired Compact Mouse + (0x17ef, 0x6123, KeyboardType::NonAlphabetic, true), + // Corsair Katar Pro Wireless (USB dongle) + (0x1b1c, 0x1b94, KeyboardType::NonAlphabetic, true), + // Corsair Katar Pro Wireless (Bluetooth) + (0x1bae, 0x1b1c, KeyboardType::NonAlphabetic, true), + // Kensington Pro Fit Full-size + (0x1bcf, 0x08a0, KeyboardType::NonAlphabetic, true), + // Huion HS64 + (0x256c, 0x006d, KeyboardType::NonAlphabetic, true), + // XP-Pen Star G640 + (0x28bd, 0x0914, KeyboardType::NonAlphabetic, true), + // XP-Pen Artist 12 Pro + (0x28bd, 0x091f, KeyboardType::NonAlphabetic, true), + // XP-Pen Deco mini7W + (0x28bd, 0x0928, KeyboardType::NonAlphabetic, true), +]; diff --git a/libs/input/rust/keyboard_classifier.rs b/libs/input/rust/keyboard_classifier.rs index 1063fac664..8721ef7687 100644 --- a/libs/input/rust/keyboard_classifier.rs +++ b/libs/input/rust/keyboard_classifier.rs @@ -35,6 +35,7 @@ //! TODO(b/263559234): Data store implementation to store information about past classification use crate::input::{DeviceId, InputDevice, KeyboardType}; +use crate::keyboard_classification_config::CLASSIFIED_DEVICES; use crate::{DeviceClass, ModifierState}; use std::collections::HashMap; @@ -126,6 +127,14 @@ impl KeyboardClassifier { (KeyboardType::NonAlphabetic, true) }; } + + // Check in known device list for classification + for data in CLASSIFIED_DEVICES.iter() { + if device.identifier.vendor == data.0 && device.identifier.product == data.1 { + return (data.2, data.3); + } + } + // Any composite device with multiple device classes should be categorized as non-alphabetic // keyboard initially if device.classes.contains(DeviceClass::Touch) @@ -169,6 +178,7 @@ impl KeyboardClassifier { #[cfg(test)] mod tests { use crate::input::{DeviceId, InputDevice, KeyboardType}; + use crate::keyboard_classification_config::CLASSIFIED_DEVICES; use crate::keyboard_classifier::KeyboardClassifier; use crate::{DeviceClass, ModifierState, RustInputDeviceIdentifier}; @@ -326,6 +336,17 @@ mod tests { assert!(!classifier.is_finalized(DEVICE_ID)); } + #[test] + fn classify_known_devices() { + let mut classifier = KeyboardClassifier::new(); + for device in CLASSIFIED_DEVICES.iter() { + classifier + .notify_keyboard_changed(create_device_with_vendor_product_ids(device.0, device.1)); + assert_eq!(classifier.get_keyboard_type(DEVICE_ID), device.2); + assert_eq!(classifier.is_finalized(DEVICE_ID), device.3); + } + } + fn create_device(classes: DeviceClass) -> InputDevice { InputDevice { device_id: DEVICE_ID, @@ -342,4 +363,21 @@ mod tests { classes, } } + + fn create_device_with_vendor_product_ids(vendor: u16, product: u16) -> InputDevice { + InputDevice { + device_id: DEVICE_ID, + identifier: RustInputDeviceIdentifier { + name: "test_device".to_string(), + location: "location".to_string(), + unique_id: "unique_id".to_string(), + bus: 123, + vendor, + product, + version: 567, + descriptor: "descriptor".to_string(), + }, + classes: DeviceClass::Keyboard | DeviceClass::AlphabeticKey | DeviceClass::External, + } + } } diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs index 5010475334..af8f889cb5 100644 --- a/libs/input/rust/lib.rs +++ b/libs/input/rust/lib.rs @@ -18,6 +18,7 @@ mod input; mod input_verifier; +mod keyboard_classification_config; mod keyboard_classifier; pub use input::{ diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp index bd501073d7..a1f917dc03 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp @@ -29,7 +29,6 @@ #include <GrDirectContext.h> #include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h> #include <include/gpu/ganesh/vk/GrVkDirectContext.h> -#include <vk/GrVkExtensions.h> #include <vk/GrVkTypes.h> #include <android-base/stringprintf.h> diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.h b/libs/renderengine/skia/SkiaVkRenderEngine.h index 0a2f9b2228..d2bb3d53cf 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.h +++ b/libs/renderengine/skia/SkiaVkRenderEngine.h @@ -17,8 +17,6 @@ #ifndef SF_SKIAVKRENDERENGINE_H_ #define SF_SKIAVKRENDERENGINE_H_ -#include <vk/GrVkBackendContext.h> - #include "SkiaRenderEngine.h" #include "VulkanInterface.h" #include "compat/SkiaGpuContext.h" diff --git a/libs/renderengine/skia/VulkanInterface.cpp b/libs/renderengine/skia/VulkanInterface.cpp index 5e756b03ed..37b69f6590 100644 --- a/libs/renderengine/skia/VulkanInterface.cpp +++ b/libs/renderengine/skia/VulkanInterface.cpp @@ -32,21 +32,8 @@ namespace android { namespace renderengine { namespace skia { -GrVkBackendContext VulkanInterface::getGaneshBackendContext() { - GrVkBackendContext backendContext; - backendContext.fInstance = mInstance; - backendContext.fPhysicalDevice = mPhysicalDevice; - backendContext.fDevice = mDevice; - backendContext.fQueue = mQueue; - backendContext.fGraphicsQueueIndex = mQueueIndex; - backendContext.fMaxAPIVersion = mApiVersion; - backendContext.fVkExtensions = &mGrExtensions; - backendContext.fDeviceFeatures2 = mPhysicalDeviceFeatures2; - backendContext.fGetProc = mGrGetProc; - backendContext.fProtectedContext = mIsProtected ? Protected::kYes : Protected::kNo; - backendContext.fDeviceLostContext = this; // VulkanInterface is long-lived - backendContext.fDeviceLostProc = onVkDeviceFault; - return backendContext; +VulkanBackendContext VulkanInterface::getGaneshBackendContext() { + return this->getGraphiteBackendContext(); }; VulkanBackendContext VulkanInterface::getGraphiteBackendContext() { @@ -57,7 +44,7 @@ VulkanBackendContext VulkanInterface::getGraphiteBackendContext() { backendContext.fQueue = mQueue; backendContext.fGraphicsQueueIndex = mQueueIndex; backendContext.fMaxAPIVersion = mApiVersion; - backendContext.fVkExtensions = &mGrExtensions; + backendContext.fVkExtensions = &mVulkanExtensions; backendContext.fDeviceFeatures2 = mPhysicalDeviceFeatures2; backendContext.fGetProc = mGrGetProc; backendContext.fProtectedContext = mIsProtected ? Protected::kYes : Protected::kNo; @@ -429,11 +416,11 @@ void VulkanInterface::init(bool protectedContent) { mDeviceExtensionNames.push_back(devExt.extensionName); } - mGrExtensions.init(sGetProc, instance, physicalDevice, enabledInstanceExtensionNames.size(), - enabledInstanceExtensionNames.data(), enabledDeviceExtensionNames.size(), - enabledDeviceExtensionNames.data()); + mVulkanExtensions.init(sGetProc, instance, physicalDevice, enabledInstanceExtensionNames.size(), + enabledInstanceExtensionNames.data(), enabledDeviceExtensionNames.size(), + enabledDeviceExtensionNames.data()); - if (!mGrExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) { + if (!mVulkanExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) { BAIL("Vulkan driver doesn't support external semaphore fd"); } @@ -458,7 +445,7 @@ void VulkanInterface::init(bool protectedContent) { tailPnext = &mProtectedMemoryFeatures->pNext; } - if (mGrExtensions.hasExtension(VK_EXT_DEVICE_FAULT_EXTENSION_NAME, 1)) { + if (mVulkanExtensions.hasExtension(VK_EXT_DEVICE_FAULT_EXTENSION_NAME, 1)) { mDeviceFaultFeatures = new VkPhysicalDeviceFaultFeaturesEXT; mDeviceFaultFeatures->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT; mDeviceFaultFeatures->pNext = nullptr; @@ -484,7 +471,7 @@ void VulkanInterface::init(bool protectedContent) { queuePriority, }; - if (mGrExtensions.hasExtension(VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, 2)) { + if (mVulkanExtensions.hasExtension(VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, 2)) { queueNextPtr = &queuePriorityCreateInfo; } @@ -606,7 +593,7 @@ void VulkanInterface::teardown() { mQueue = VK_NULL_HANDLE; // Implicitly destroyed by destroying mDevice. mQueueIndex = 0; mApiVersion = 0; - mGrExtensions = skgpu::VulkanExtensions(); + mVulkanExtensions = skgpu::VulkanExtensions(); mGrGetProc = nullptr; mIsProtected = false; mIsRealtimePriority = false; diff --git a/libs/renderengine/skia/VulkanInterface.h b/libs/renderengine/skia/VulkanInterface.h index f20b00251b..d0fe4d1544 100644 --- a/libs/renderengine/skia/VulkanInterface.h +++ b/libs/renderengine/skia/VulkanInterface.h @@ -16,7 +16,7 @@ #pragma once -#include <include/gpu/vk/GrVkBackendContext.h> +#include <include/gpu/vk/VulkanBackendContext.h> #include <include/gpu/vk/VulkanExtensions.h> #include <include/gpu/vk/VulkanTypes.h> @@ -24,10 +24,6 @@ using namespace skgpu; -namespace skgpu { -struct VulkanBackendContext; -} // namespace skgpu - namespace android { namespace renderengine { namespace skia { @@ -48,7 +44,8 @@ public: bool takeOwnership(); void teardown(); - GrVkBackendContext getGaneshBackendContext(); + // TODO(b/309785258) Combine these into one now that they are the same implementation. + VulkanBackendContext getGaneshBackendContext(); VulkanBackendContext getGraphiteBackendContext(); VkSemaphore createExportableSemaphore(); VkSemaphore importSemaphoreFromSyncFd(int syncFd); @@ -86,7 +83,7 @@ private: VkQueue mQueue = VK_NULL_HANDLE; int mQueueIndex = 0; uint32_t mApiVersion = 0; - skgpu::VulkanExtensions mGrExtensions; + skgpu::VulkanExtensions mVulkanExtensions; VkPhysicalDeviceFeatures2* mPhysicalDeviceFeatures2 = nullptr; VkPhysicalDeviceSamplerYcbcrConversionFeatures* mSamplerYcbcrConversionFeatures = nullptr; VkPhysicalDeviceProtectedMemoryFeatures* mProtectedMemoryFeatures = nullptr; diff --git a/libs/renderengine/skia/compat/GaneshGpuContext.cpp b/libs/renderengine/skia/compat/GaneshGpuContext.cpp index b2eae009ed..b121fe83eb 100644 --- a/libs/renderengine/skia/compat/GaneshGpuContext.cpp +++ b/libs/renderengine/skia/compat/GaneshGpuContext.cpp @@ -25,7 +25,7 @@ #include <include/gpu/ganesh/gl/GrGLDirectContext.h> #include <include/gpu/ganesh/vk/GrVkDirectContext.h> #include <include/gpu/gl/GrGLInterface.h> -#include <include/gpu/vk/GrVkBackendContext.h> +#include <include/gpu/vk/VulkanBackendContext.h> #include "../AutoBackendTexture.h" #include "GaneshBackendTexture.h" @@ -56,10 +56,10 @@ std::unique_ptr<SkiaGpuContext> SkiaGpuContext::MakeGL_Ganesh( } std::unique_ptr<SkiaGpuContext> SkiaGpuContext::MakeVulkan_Ganesh( - const GrVkBackendContext& grVkBackendContext, + const skgpu::VulkanBackendContext& vkBackendContext, GrContextOptions::PersistentCache& skSLCacheMonitor) { return std::make_unique<GaneshGpuContext>( - GrDirectContexts::MakeVulkan(grVkBackendContext, ganeshOptions(skSLCacheMonitor))); + GrDirectContexts::MakeVulkan(vkBackendContext, ganeshOptions(skSLCacheMonitor))); } GaneshGpuContext::GaneshGpuContext(sk_sp<GrDirectContext> grContext) : mGrContext(grContext) { diff --git a/libs/renderengine/skia/compat/SkiaGpuContext.h b/libs/renderengine/skia/compat/SkiaGpuContext.h index 282dfe7abe..9fa6fb8521 100644 --- a/libs/renderengine/skia/compat/SkiaGpuContext.h +++ b/libs/renderengine/skia/compat/SkiaGpuContext.h @@ -23,7 +23,6 @@ #include <include/gpu/GrDirectContext.h> #include <include/gpu/gl/GrGLInterface.h> #include <include/gpu/graphite/Context.h> -#include <include/gpu/vk/GrVkBackendContext.h> #include "include/gpu/vk/VulkanBackendContext.h" #include "SkiaBackendTexture.h" @@ -52,10 +51,10 @@ public: GrContextOptions::PersistentCache& skSLCacheMonitor); /** - * grVkBackendContext must remain valid until after SkiaGpuContext is destroyed. + * vkBackendContext must remain valid until after SkiaGpuContext is destroyed. */ static std::unique_ptr<SkiaGpuContext> MakeVulkan_Ganesh( - const GrVkBackendContext& grVkBackendContext, + const skgpu::VulkanBackendContext& vkBackendContext, GrContextOptions::PersistentCache& skSLCacheMonitor); // TODO: b/293371537 - Need shader / pipeline monitoring support in Graphite. diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp index ca53a0dfa6..a4ffd51d43 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp @@ -317,6 +317,21 @@ void updateMetadata(LayerSnapshot& snapshot, const RequestedLayerState& requeste } } +void updateMetadataAndGameMode(LayerSnapshot& snapshot, const RequestedLayerState& requested, + const LayerSnapshotBuilder::Args& args, + const LayerSnapshot& parentSnapshot) { + if (snapshot.changes.test(RequestedLayerState::Changes::GameMode)) { + snapshot.gameMode = requested.metadata.has(gui::METADATA_GAME_MODE) + ? requested.gameMode + : parentSnapshot.gameMode; + } + updateMetadata(snapshot, requested, args); + if (args.includeMetadata) { + snapshot.layerMetadata = parentSnapshot.layerMetadata; + snapshot.layerMetadata.merge(requested.metadata); + } +} + void clearChanges(LayerSnapshot& snapshot) { snapshot.changes.clear(); snapshot.clientChanges = 0; @@ -762,6 +777,11 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a RequestedLayerState::Changes::Input)) { updateInput(snapshot, requested, parentSnapshot, path, args); } + if (forceUpdate || + (args.includeMetadata && + snapshot.changes.test(RequestedLayerState::Changes::Metadata))) { + updateMetadataAndGameMode(snapshot, requested, args, parentSnapshot); + } return; } @@ -801,15 +821,8 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a } } - if (forceUpdate || snapshot.changes.test(RequestedLayerState::Changes::GameMode)) { - snapshot.gameMode = requested.metadata.has(gui::METADATA_GAME_MODE) - ? requested.gameMode - : parentSnapshot.gameMode; - updateMetadata(snapshot, requested, args); - if (args.includeMetadata) { - snapshot.layerMetadata = parentSnapshot.layerMetadata; - snapshot.layerMetadata.merge(requested.metadata); - } + if (forceUpdate || snapshot.changes.test(RequestedLayerState::Changes::Metadata)) { + updateMetadataAndGameMode(snapshot, requested, args, parentSnapshot); } if (forceUpdate || snapshot.clientChanges & layer_state_t::eFixedTransformHintChanged || @@ -1178,6 +1191,15 @@ void LayerSnapshotBuilder::forEachVisibleSnapshot(const Visitor& visitor) { } } +void LayerSnapshotBuilder::forEachSnapshot(const Visitor& visitor, + const ConstPredicate& predicate) { + for (int i = 0; i < mNumInterestingSnapshots; i++) { + std::unique_ptr<LayerSnapshot>& snapshot = mSnapshots.at((size_t)i); + if (!predicate(*snapshot)) continue; + visitor(snapshot); + } +} + void LayerSnapshotBuilder::forEachInputSnapshot(const ConstVisitor& visitor) const { for (int i = mNumInterestingSnapshots - 1; i >= 0; i--) { LayerSnapshot& snapshot = *mSnapshots[(size_t)i]; diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h index 1cec0183b9..dbbad7664a 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h @@ -86,6 +86,11 @@ public: // Visit each visible snapshot in z-order and move the snapshot if needed void forEachVisibleSnapshot(const Visitor& visitor); + typedef std::function<bool(const LayerSnapshot& snapshot)> ConstPredicate; + // Visit each snapshot that satisfies the predicate and move the snapshot if needed with visible + // snapshots in z-order + void forEachSnapshot(const Visitor& visitor, const ConstPredicate& predicate); + // Visit each snapshot interesting to input reverse z-order void forEachInputSnapshot(const ConstVisitor& visitor) const; diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp index 3e8d74094d..c3c299964a 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp @@ -328,6 +328,7 @@ void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerSta changes |= RequestedLayerState::Changes::GameMode; } } + changes |= RequestedLayerState::Changes::Metadata; } if (clientState.what & layer_state_t::eFrameRateChanged) { const auto compatibility = diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 60681a2bc8..26e11e5693 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -123,19 +123,22 @@ void Scheduler::setPacesetterDisplay(std::optional<PhysicalDisplayId> pacesetter promotePacesetterDisplay(pacesetterIdOpt); } -void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) { +void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr, + PhysicalDisplayId activeDisplayId) { auto schedulePtr = std::make_shared<VsyncSchedule>(selectorPtr->getActiveMode().modePtr, mFeatures, [this](PhysicalDisplayId id, bool enable) { onHardwareVsyncRequest(id, enable); }); - registerDisplayInternal(displayId, std::move(selectorPtr), std::move(schedulePtr)); + registerDisplayInternal(displayId, std::move(selectorPtr), std::move(schedulePtr), + activeDisplayId); } void Scheduler::registerDisplayInternal(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr, - VsyncSchedulePtr schedulePtr) { + VsyncSchedulePtr schedulePtr, + PhysicalDisplayId activeDisplayId) { demotePacesetterDisplay(); auto [pacesetterVsyncSchedule, isNew] = [&]() FTL_FAKE_GUARD(kMainThreadContext) { @@ -145,7 +148,7 @@ void Scheduler::registerDisplayInternal(PhysicalDisplayId displayId, std::move(schedulePtr), mFeatures) .second; - return std::make_pair(promotePacesetterDisplayLocked(), isNew); + return std::make_pair(promotePacesetterDisplayLocked(activeDisplayId), isNew); }(); applyNewVsyncSchedule(std::move(pacesetterVsyncSchedule)); @@ -158,7 +161,9 @@ void Scheduler::registerDisplayInternal(PhysicalDisplayId displayId, dispatchHotplug(displayId, Hotplug::Connected); } -void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) { +void Scheduler::unregisterDisplay(PhysicalDisplayId displayId, PhysicalDisplayId activeDisplayId) { + LOG_ALWAYS_FATAL_IF(displayId == activeDisplayId, "Cannot unregister the active display!"); + dispatchHotplug(displayId, Hotplug::Disconnected); demotePacesetterDisplay(); @@ -173,7 +178,7 @@ void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) { // headless virtual display.) LOG_ALWAYS_FATAL_IF(mDisplays.empty(), "Cannot unregister all displays!"); - pacesetterVsyncSchedule = promotePacesetterDisplayLocked(); + pacesetterVsyncSchedule = promotePacesetterDisplayLocked(activeDisplayId); } applyNewVsyncSchedule(std::move(pacesetterVsyncSchedule)); } diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index ccaa05f970..1a4aa79818 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -101,9 +101,16 @@ public: using ConstVsyncSchedulePtr = std::shared_ptr<const VsyncSchedule>; using VsyncSchedulePtr = std::shared_ptr<VsyncSchedule>; - void registerDisplay(PhysicalDisplayId, RefreshRateSelectorPtr) REQUIRES(kMainThreadContext) + // After registration/unregistration, `activeDisplayId` is promoted to pacesetter. Note that the + // active display is never unregistered, since hotplug disconnect never happens for activatable + // displays, i.e. a foldable's internal displays or otherwise the (internal or external) primary + // display. + // TODO: b/255635821 - Remove active display parameters. + void registerDisplay(PhysicalDisplayId, RefreshRateSelectorPtr, + PhysicalDisplayId activeDisplayId) REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock); - void unregisterDisplay(PhysicalDisplayId) REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock); + void unregisterDisplay(PhysicalDisplayId, PhysicalDisplayId activeDisplayId) + REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock); void run(); @@ -390,8 +397,9 @@ private: // the caller on the main thread to avoid deadlock, since the timer thread locks it before exit. void demotePacesetterDisplay() REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock, mPolicyLock); - void registerDisplayInternal(PhysicalDisplayId, RefreshRateSelectorPtr, VsyncSchedulePtr) - REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock); + void registerDisplayInternal(PhysicalDisplayId, RefreshRateSelectorPtr, VsyncSchedulePtr, + PhysicalDisplayId activeDisplayId) REQUIRES(kMainThreadContext) + EXCLUDES(mDisplayLock); struct Policy; diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp index dd3c4b074c..0644acaee2 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp @@ -360,7 +360,11 @@ bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) { purgeTimelines(now); for (auto& timeline : mTimelines) { - if (timeline.validUntil() && timeline.validUntil()->ns() > vsync) { + const bool isVsyncValid = FlagManager::getInstance().vrr_bugfix_24q4() + ? timeline.isWithin(TimePoint::fromNs(vsync)) == + VsyncTimeline::VsyncOnTimeline::Unique + : timeline.validUntil() && timeline.validUntil()->ns() > vsync; + if (isVsyncValid) { return timeline.isVSyncInPhase(model, vsync, frameRate); } } @@ -395,8 +399,14 @@ void VSyncPredictor::setRenderRate(Fps renderRate, bool applyImmediately) { mLastCommittedVsync = TimePoint::fromNs(0); } else { - mTimelines.back().freeze( - TimePoint::fromNs(mLastCommittedVsync.ns() + mIdealPeriod.ns() / 2)); + if (FlagManager::getInstance().vrr_bugfix_24q4()) { + // We need to freeze the timeline at the committed vsync so that we don't + // overshoot the deadline. + mTimelines.back().freeze(mLastCommittedVsync); + } else { + mTimelines.back().freeze( + TimePoint::fromNs(mLastCommittedVsync.ns() + mIdealPeriod.ns() / 2)); + } } mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, renderRate); purgeTimelines(TimePoint::fromNs(mClock->now())); @@ -611,7 +621,10 @@ void VSyncPredictor::purgeTimelines(android::TimePoint now) { while (mTimelines.size() > 1) { const auto validUntilOpt = mTimelines.front().validUntil(); - if (validUntilOpt && *validUntilOpt < now) { + const bool isTimelineOutDated = FlagManager::getInstance().vrr_bugfix_24q4() + ? mTimelines.front().isWithin(now) == VsyncTimeline::VsyncOnTimeline::Outside + : validUntilOpt && *validUntilOpt < now; + if (isTimelineOutDated) { mTimelines.pop_front(); } else { break; @@ -660,9 +673,12 @@ std::optional<TimePoint> VSyncPredictor::VsyncTimeline::nextAnticipatedVSyncTime vsyncTime += missedVsync.fixup.ns(); ATRACE_FORMAT_INSTANT("lastFrameMissed"); } else if (mightBackpressure && lastVsyncOpt) { - // lastVsyncOpt is based on the old timeline before we shifted it. we should correct it - // first before trying to use it. - lastVsyncOpt = snapToVsyncAlignedWithRenderRate(model, *lastVsyncOpt); + if (!FlagManager::getInstance().vrr_bugfix_24q4()) { + // lastVsyncOpt does not need to be corrected with the new rate, and + // it should be used as is to avoid skipping a frame when changing rates are + // aligned at vsync time. + lastVsyncOpt = snapToVsyncAlignedWithRenderRate(model, *lastVsyncOpt); + } const auto vsyncDiff = vsyncTime - *lastVsyncOpt; if (vsyncDiff <= minFramePeriodOpt->ns() - threshold) { // avoid a duplicate vsync @@ -681,7 +697,10 @@ std::optional<TimePoint> VSyncPredictor::VsyncTimeline::nextAnticipatedVSyncTime } ATRACE_FORMAT_INSTANT("vsync in %.2fms", float(vsyncTime - TimePoint::now().ns()) / 1e6f); - if (mValidUntil && vsyncTime > mValidUntil->ns()) { + const bool isVsyncInvalid = FlagManager::getInstance().vrr_bugfix_24q4() + ? isWithin(TimePoint::fromNs(vsyncTime)) == VsyncOnTimeline::Outside + : mValidUntil && vsyncTime > mValidUntil->ns(); + if (isVsyncInvalid) { ATRACE_FORMAT_INSTANT("no longer valid for vsync in %.2f", static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f); return std::nullopt; diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h index 8ce61d86c6..66a7d71435 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.h +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h @@ -106,6 +106,24 @@ private: void shiftVsyncSequence(Duration phase); void setRenderRate(std::optional<Fps> renderRateOpt) { mRenderRateOpt = renderRateOpt; } + enum class VsyncOnTimeline { + Unique, // Within timeline, not shared with next timeline. + Shared, // Within timeline, shared with next timeline. + Outside, // Outside of the timeline. + }; + VsyncOnTimeline isWithin(TimePoint vsync) { + const auto threshold = mIdealPeriod.ns() / 2; + if (!mValidUntil || vsync.ns() < mValidUntil->ns() - threshold) { + // if mValidUntil is absent then timeline is not frozen and + // vsync should be unique to that timeline. + return VsyncOnTimeline::Unique; + } + if (vsync.ns() > mValidUntil->ns() + threshold) { + return VsyncOnTimeline::Outside; + } + return VsyncOnTimeline::Shared; + } + private: nsecs_t snapToVsyncAlignedWithRenderRate(Model model, nsecs_t vsync); VsyncSequence getVsyncSequenceLocked(Model, nsecs_t vsync); diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 596ec12d9e..d7727808a4 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -3855,7 +3855,8 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, ftl::FakeGuard guard(kMainThreadContext); // For hotplug reconnect, renew the registration since display modes have been reloaded. - mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector()); + mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector(), + mActiveDisplayId); } if (display->isVirtual()) { @@ -3894,7 +3895,7 @@ void SurfaceFlinger::processDisplayRemoved(const wp<IBinder>& displayToken) { if (display->isVirtual()) { releaseVirtualDisplay(display->getVirtualId()); } else { - mScheduler->unregisterDisplay(display->getPhysicalId()); + mScheduler->unregisterDisplay(display->getPhysicalId(), mActiveDisplayId); } } @@ -4506,7 +4507,8 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { getFactory(), activeRefreshRate, *mTimeStats); // The pacesetter must be registered before EventThread creation below. - mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector()); + mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector(), + mActiveDisplayId); if (FlagManager::getInstance().vrr_config()) { mScheduler->setRenderRate(display->getPhysicalId(), activeMode.fps, /*applyImmediately*/ true); @@ -9335,7 +9337,9 @@ std::vector<std::pair<Layer*, LayerFE*>> SurfaceFlinger::moveSnapshotsToComposit std::vector<std::pair<Layer*, LayerFE*>> layers; if (mLayerLifecycleManagerEnabled) { nsecs_t currentTime = systemTime(); - mLayerSnapshotBuilder.forEachVisibleSnapshot( + const bool needsMetadata = mCompositionEngine->getFeatureFlags().test( + compositionengine::Feature::kSnapshotLayerMetadata); + mLayerSnapshotBuilder.forEachSnapshot( [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) FTL_FAKE_GUARD( kMainThreadContext) { if (cursorOnly && @@ -9358,6 +9362,12 @@ std::vector<std::pair<Layer*, LayerFE*>> SurfaceFlinger::moveSnapshotsToComposit layerFE->mSnapshot = std::move(snapshot); refreshArgs.layers.push_back(layerFE); layers.emplace_back(legacyLayer.get(), layerFE.get()); + }, + [needsMetadata](const frontend::LayerSnapshot& snapshot) { + return snapshot.isVisible || + (needsMetadata && + snapshot.changes.test( + frontend::RequestedLayerState::Changes::Metadata)); }); } if (!mLayerLifecycleManagerEnabled) { diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index 8b9ac93310..54d465902b 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -291,13 +291,106 @@ TEST_F(LayerSnapshotTest, GameMode) { transactions.back().states.front().layerId = 1; transactions.back().states.front().state.layerId = static_cast<int32_t>(1); mLifecycleManager.applyTransactions(transactions); - EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::GameMode); + EXPECT_EQ(mLifecycleManager.getGlobalChanges(), + RequestedLayerState::Changes::GameMode | RequestedLayerState::Changes::Metadata); UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::eMetadataChanged); EXPECT_EQ(static_cast<int32_t>(getSnapshot(1)->gameMode), 42); EXPECT_EQ(static_cast<int32_t>(getSnapshot(11)->gameMode), 42); } +TEST_F(LayerSnapshotTest, UpdateMetadata) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().state.what = layer_state_t::eMetadataChanged; + // This test focuses on metadata used by ARC++ to ensure LayerMetadata is updated correctly, + // and not using stale data. + transactions.back().states.front().state.metadata = LayerMetadata(); + transactions.back().states.front().state.metadata.setInt32(METADATA_OWNER_UID, 123); + transactions.back().states.front().state.metadata.setInt32(METADATA_WINDOW_TYPE, 234); + transactions.back().states.front().state.metadata.setInt32(METADATA_TASK_ID, 345); + transactions.back().states.front().state.metadata.setInt32(METADATA_MOUSE_CURSOR, 456); + transactions.back().states.front().state.metadata.setInt32(METADATA_ACCESSIBILITY_ID, 567); + transactions.back().states.front().state.metadata.setInt32(METADATA_OWNER_PID, 678); + transactions.back().states.front().state.metadata.setInt32(METADATA_CALLING_UID, 789); + + transactions.back().states.front().layerId = 1; + transactions.back().states.front().state.layerId = static_cast<int32_t>(1); + + mLifecycleManager.applyTransactions(transactions); + EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Metadata); + + // Setting includeMetadata=true to ensure metadata update is applied to LayerSnapshot + LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(), + .layerLifecycleManager = mLifecycleManager, + .includeMetadata = true, + .displays = mFrontEndDisplayInfos, + .globalShadowSettings = globalShadowSettings, + .supportsBlur = true, + .supportedLayerGenericMetadata = {}, + .genericLayerMetadataKeyMap = {}}; + update(mSnapshotBuilder, args); + + EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::eMetadataChanged); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_OWNER_UID, -1), 123); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_WINDOW_TYPE, -1), 234); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_TASK_ID, -1), 345); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_MOUSE_CURSOR, -1), 456); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_ACCESSIBILITY_ID, -1), 567); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_OWNER_PID, -1), 678); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_CALLING_UID, -1), 789); +} + +TEST_F(LayerSnapshotTest, UpdateMetadataOfHiddenLayers) { + hideLayer(1); + + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().state.what = layer_state_t::eMetadataChanged; + // This test focuses on metadata used by ARC++ to ensure LayerMetadata is updated correctly, + // and not using stale data. + transactions.back().states.front().state.metadata = LayerMetadata(); + transactions.back().states.front().state.metadata.setInt32(METADATA_OWNER_UID, 123); + transactions.back().states.front().state.metadata.setInt32(METADATA_WINDOW_TYPE, 234); + transactions.back().states.front().state.metadata.setInt32(METADATA_TASK_ID, 345); + transactions.back().states.front().state.metadata.setInt32(METADATA_MOUSE_CURSOR, 456); + transactions.back().states.front().state.metadata.setInt32(METADATA_ACCESSIBILITY_ID, 567); + transactions.back().states.front().state.metadata.setInt32(METADATA_OWNER_PID, 678); + transactions.back().states.front().state.metadata.setInt32(METADATA_CALLING_UID, 789); + + transactions.back().states.front().layerId = 1; + transactions.back().states.front().state.layerId = static_cast<int32_t>(1); + + mLifecycleManager.applyTransactions(transactions); + EXPECT_EQ(mLifecycleManager.getGlobalChanges(), + RequestedLayerState::Changes::Metadata | RequestedLayerState::Changes::Visibility | + RequestedLayerState::Changes::VisibleRegion | + RequestedLayerState::Changes::AffectsChildren); + + // Setting includeMetadata=true to ensure metadata update is applied to LayerSnapshot + LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(), + .layerLifecycleManager = mLifecycleManager, + .includeMetadata = true, + .displays = mFrontEndDisplayInfos, + .globalShadowSettings = globalShadowSettings, + .supportsBlur = true, + .supportedLayerGenericMetadata = {}, + .genericLayerMetadataKeyMap = {}}; + update(mSnapshotBuilder, args); + + EXPECT_EQ(static_cast<int64_t>(getSnapshot(1)->clientChanges), + layer_state_t::eMetadataChanged | layer_state_t::eFlagsChanged); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_OWNER_UID, -1), 123); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_WINDOW_TYPE, -1), 234); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_TASK_ID, -1), 345); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_MOUSE_CURSOR, -1), 456); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_ACCESSIBILITY_ID, -1), 567); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_OWNER_PID, -1), 678); + EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_CALLING_UID, -1), 789); +} + TEST_F(LayerSnapshotTest, NoLayerVoteForParentWithChildVotes) { // ROOT // ├── 1 @@ -1304,6 +1397,17 @@ TEST_F(LayerSnapshotTest, NonVisibleLayerWithInput) { EXPECT_TRUE(foundInputLayer); } +TEST_F(LayerSnapshotTest, ForEachSnapshotsWithPredicate) { + std::vector<uint32_t> visitedUniqueSequences; + mSnapshotBuilder.forEachSnapshot( + [&](const std::unique_ptr<frontend::LayerSnapshot>& snapshot) { + visitedUniqueSequences.push_back(snapshot->uniqueSequence); + }, + [](const frontend::LayerSnapshot& snapshot) { return snapshot.uniqueSequence == 111; }); + EXPECT_EQ(visitedUniqueSequences.size(), 1u); + EXPECT_EQ(visitedUniqueSequences[0], 111u); +} + TEST_F(LayerSnapshotTest, canOccludePresentation) { setFlags(12, layer_state_t::eCanOccludePresentation, layer_state_t::eCanOccludePresentation); LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(), diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index 4fb06907d0..fc54a8b74f 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -343,12 +343,15 @@ TEST_F(SchedulerTest, chooseDisplayModesSingleDisplayHighHintTouchSignal) { } TEST_F(SchedulerTest, chooseDisplayModesMultipleDisplays) { + constexpr PhysicalDisplayId kActiveDisplayId = kDisplayId1; mScheduler->registerDisplay(kDisplayId1, std::make_shared<RefreshRateSelector>(kDisplay1Modes, - kDisplay1Mode60->getId())); + kDisplay1Mode60->getId()), + kActiveDisplayId); mScheduler->registerDisplay(kDisplayId2, std::make_shared<RefreshRateSelector>(kDisplay2Modes, - kDisplay2Mode60->getId())); + kDisplay2Mode60->getId()), + kActiveDisplayId); mScheduler->setDisplayPowerMode(kDisplayId1, hal::PowerMode::ON); mScheduler->setDisplayPowerMode(kDisplayId2, hal::PowerMode::ON); @@ -411,10 +414,10 @@ TEST_F(SchedulerTest, chooseDisplayModesMultipleDisplays) { { // The kDisplayId3 does not support 120Hz, The pacesetter display rate is chosen to be 120 // Hz. In this case only the display kDisplayId3 choose 60Hz as it does not support 120Hz. - mScheduler - ->registerDisplay(kDisplayId3, - std::make_shared<RefreshRateSelector>(kDisplay3Modes, - kDisplay3Mode60->getId())); + mScheduler->registerDisplay(kDisplayId3, + std::make_shared<RefreshRateSelector>(kDisplay3Modes, + kDisplay3Mode60->getId()), + kActiveDisplayId); mScheduler->setDisplayPowerMode(kDisplayId3, hal::PowerMode::ON); const GlobalSignals globalSignals = {.touch = true}; @@ -457,12 +460,15 @@ TEST_F(SchedulerTest, chooseDisplayModesMultipleDisplays) { } TEST_F(SchedulerTest, onFrameSignalMultipleDisplays) { + constexpr PhysicalDisplayId kActiveDisplayId = kDisplayId1; mScheduler->registerDisplay(kDisplayId1, std::make_shared<RefreshRateSelector>(kDisplay1Modes, - kDisplay1Mode60->getId())); + kDisplay1Mode60->getId()), + kActiveDisplayId); mScheduler->registerDisplay(kDisplayId2, std::make_shared<RefreshRateSelector>(kDisplay2Modes, - kDisplay2Mode60->getId())); + kDisplay2Mode60->getId()), + kActiveDisplayId); using VsyncIds = std::vector<std::pair<PhysicalDisplayId, VsyncId>>; @@ -585,7 +591,8 @@ TEST_F(SchedulerTest, nextFrameIntervalTest) { mFlinger.getTimeStats(), mSchedulerCallback}; - scheduler.registerDisplay(kMode->getPhysicalDisplayId(), vrrSelectorPtr, vrrTracker); + scheduler.registerDisplay(kMode->getPhysicalDisplayId(), vrrSelectorPtr, std::nullopt, + vrrTracker); vrrSelectorPtr->setActiveMode(kMode->getId(), frameRate); scheduler.setRenderRate(kMode->getPhysicalDisplayId(), frameRate, /*applyImmediately*/ false); vrrTracker->addVsyncTimestamp(0); diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index 198a5def8c..f0638094c7 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -53,7 +53,7 @@ public: factory, selectorPtr->getActiveMode().fps, timeStats) { const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId(); registerDisplay(displayId, std::move(selectorPtr), std::move(controller), - std::move(tracker)); + std::move(tracker), displayId); ON_CALL(*this, postMessage).WillByDefault([](sp<MessageHandler>&& handler) { // Execute task to prevent broken promise exception on destruction. @@ -85,14 +85,16 @@ public: void registerDisplay( PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr, + std::optional<PhysicalDisplayId> activeDisplayIdOpt = {}, std::shared_ptr<VSyncTracker> vsyncTracker = std::make_shared<mock::VSyncTracker>()) { registerDisplay(displayId, std::move(selectorPtr), - std::make_unique<mock::VsyncController>(), vsyncTracker); + std::make_unique<mock::VsyncController>(), vsyncTracker, + activeDisplayIdOpt.value_or(displayId)); } void registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr, std::unique_ptr<VsyncController> controller, - std::shared_ptr<VSyncTracker> tracker) { + std::shared_ptr<VSyncTracker> tracker, PhysicalDisplayId activeDisplayId) { ftl::FakeGuard guard(kMainThreadContext); Scheduler::registerDisplayInternal(displayId, std::move(selectorPtr), std::shared_ptr<VsyncSchedule>( @@ -101,16 +103,12 @@ public: mock::VSyncDispatch>(), std::move(controller), mockRequestHardwareVsync - .AsStdFunction()))); + .AsStdFunction())), + activeDisplayId); } testing::MockFunction<void(PhysicalDisplayId, bool)> mockRequestHardwareVsync; - void unregisterDisplay(PhysicalDisplayId displayId) { - ftl::FakeGuard guard(kMainThreadContext); - Scheduler::unregisterDisplay(displayId); - } - void setDisplayPowerMode(PhysicalDisplayId displayId, hal::PowerMode powerMode) { ftl::FakeGuard guard(kMainThreadContext); Scheduler::setDisplayPowerMode(displayId, powerMode); diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 007383b3d9..4197cbd271 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -1118,8 +1118,8 @@ public: if (mFlinger.scheduler() && mSchedulerRegistration) { mFlinger.scheduler()->registerDisplay(*physicalId, mCreationArgs.refreshRateSelector, - std::move(controller), - std::move(tracker)); + std::move(controller), std::move(tracker), + mFlinger.mutableActiveDisplayId()); } } diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp index 5109ea6793..f36a8a6fd7 100644 --- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp @@ -673,6 +673,36 @@ TEST_F(VSyncPredictorTest, setRenderRateIsIgnoredIfNotDivisor) { EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 6 * mPeriod)); } +TEST_F(VSyncPredictorTest, setRenderRateWhenRenderRateGoesDown) { + SET_FLAG_FOR_TEST(flags::vrr_config, true); + SET_FLAG_FOR_TEST(flags::vrr_bugfix_24q4, true); + + const int32_t kGroup = 0; + const auto kResolution = ui::Size(1920, 1080); + const auto vsyncRate = Fps::fromPeriodNsecs(500); + const auto minFrameRate = Fps::fromPeriodNsecs(1000); + hal::VrrConfig vrrConfig; + vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs(); + const ftl::NonNull<DisplayModePtr> kMode = + ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), vsyncRate, kGroup, + kResolution, DEFAULT_DISPLAY_ID) + .setVrrConfig(std::move(vrrConfig)) + .build()); + + VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize, + kMinimumSamplesForPrediction, kOutlierTolerancePercent}; + + Fps frameRate = Fps::fromPeriodNsecs(1000); + vrrTracker.setRenderRate(frameRate, /*applyImmediately*/ false); + vrrTracker.addVsyncTimestamp(0); + EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700)); + EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000, 1000)); + + frameRate = Fps::fromPeriodNsecs(3000); + vrrTracker.setRenderRate(frameRate, /*applyImmediately*/ false); + EXPECT_TRUE(vrrTracker.isVSyncInPhase(2000, frameRate)); +} + TEST_F(VSyncPredictorTest, setRenderRateHighIsAppliedImmediately) { SET_FLAG_FOR_TEST(flags::vrr_config, true); |