Merge "[native] Use newer GrDirectContexts::MakeGL" into main
diff --git a/include/input/InputVerifier.h b/include/input/InputVerifier.h
index b857482..14dd463 100644
--- a/include/input/InputVerifier.h
+++ b/include/input/InputVerifier.h
@@ -46,7 +46,7 @@
 public:
     InputVerifier(const std::string& name);
 
-    android::base::Result<void> processMovement(int32_t deviceId, int32_t action,
+    android::base::Result<void> processMovement(int32_t deviceId, int32_t source, int32_t action,
                                                 uint32_t pointerCount,
                                                 const PointerProperties* pointerProperties,
                                                 const PointerCoords* pointerCoords, int32_t flags);
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 9a27d23..f17a654 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -200,6 +200,7 @@
 
 cc_aconfig_library {
     name: "libguiflags",
+    host_supported: true,
     vendor_available: true,
     aconfig_declarations: "libgui_flags",
 }
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 36a01d3..ab4af1a 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -90,6 +90,28 @@
         "--allowlist-var=AMOTION_EVENT_ACTION_DOWN",
         "--allowlist-var=AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT",
         "--allowlist-var=MAX_POINTER_ID",
+        "--allowlist-var=AINPUT_SOURCE_CLASS_NONE",
+        "--allowlist-var=AINPUT_SOURCE_CLASS_BUTTON",
+        "--allowlist-var=AINPUT_SOURCE_CLASS_POINTER",
+        "--allowlist-var=AINPUT_SOURCE_CLASS_NAVIGATION",
+        "--allowlist-var=AINPUT_SOURCE_CLASS_POSITION",
+        "--allowlist-var=AINPUT_SOURCE_CLASS_JOYSTICK",
+        "--allowlist-var=AINPUT_SOURCE_UNKNOWN",
+        "--allowlist-var=AINPUT_SOURCE_KEYBOARD",
+        "--allowlist-var=AINPUT_SOURCE_DPAD",
+        "--allowlist-var=AINPUT_SOURCE_GAMEPAD",
+        "--allowlist-var=AINPUT_SOURCE_TOUCHSCREEN",
+        "--allowlist-var=AINPUT_SOURCE_MOUSE",
+        "--allowlist-var=AINPUT_SOURCE_STYLUS",
+        "--allowlist-var=AINPUT_SOURCE_BLUETOOTH_STYLUS",
+        "--allowlist-var=AINPUT_SOURCE_TRACKBALL",
+        "--allowlist-var=AINPUT_SOURCE_MOUSE_RELATIVE",
+        "--allowlist-var=AINPUT_SOURCE_TOUCHPAD",
+        "--allowlist-var=AINPUT_SOURCE_TOUCH_NAVIGATION",
+        "--allowlist-var=AINPUT_SOURCE_JOYSTICK",
+        "--allowlist-var=AINPUT_SOURCE_HDMI",
+        "--allowlist-var=AINPUT_SOURCE_SENSOR",
+        "--allowlist-var=AINPUT_SOURCE_ROTARY_ENCODER",
     ],
 
     static_libs: [
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 1600013..30cedb0 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -632,8 +632,8 @@
                                 MotionEvent::actionToString(action).c_str()));
     if (verifyEvents()) {
         Result<void> result =
-                mInputVerifier.processMovement(deviceId, action, pointerCount, pointerProperties,
-                                               pointerCoords, flags);
+                mInputVerifier.processMovement(deviceId, source, action, pointerCount,
+                                               pointerProperties, pointerCoords, flags);
         if (!result.ok()) {
             LOG(FATAL) << "Bad stream: " << result.error();
         }
@@ -1276,13 +1276,13 @@
         PointerCoords& resampledCoords = touchState.lastResample.pointers[i];
         const PointerCoords& currentCoords = current->getPointerById(id);
         resampledCoords = currentCoords;
+        resampledCoords.isResampled = true;
         if (other->idBits.hasBit(id) && shouldResampleTool(event->getToolType(i))) {
             const PointerCoords& otherCoords = other->getPointerById(id);
             resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X,
                                          lerp(currentCoords.getX(), otherCoords.getX(), alpha));
             resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y,
                                          lerp(currentCoords.getY(), otherCoords.getY(), alpha));
-            resampledCoords.isResampled = true;
             ALOGD_IF(debugResampling(),
                      "[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), "
                      "other (%0.3f, %0.3f), alpha %0.3f",
diff --git a/libs/input/InputVerifier.cpp b/libs/input/InputVerifier.cpp
index 6c602e0..cec2445 100644
--- a/libs/input/InputVerifier.cpp
+++ b/libs/input/InputVerifier.cpp
@@ -33,7 +33,7 @@
 InputVerifier::InputVerifier(const std::string& name)
       : mVerifier(android::input::verifier::create(rust::String::lossy(name))){};
 
-Result<void> InputVerifier::processMovement(DeviceId deviceId, int32_t action,
+Result<void> InputVerifier::processMovement(DeviceId deviceId, int32_t source, int32_t action,
                                             uint32_t pointerCount,
                                             const PointerProperties* pointerProperties,
                                             const PointerCoords* pointerCoords, int32_t flags) {
@@ -43,8 +43,8 @@
     }
     rust::Slice<const RustPointerProperties> properties{rpp.data(), rpp.size()};
     rust::String errorMessage =
-            android::input::verifier::process_movement(*mVerifier, deviceId, action, properties,
-                                                       static_cast<uint32_t>(flags));
+            android::input::verifier::process_movement(*mVerifier, deviceId, source, action,
+                                                       properties, static_cast<uint32_t>(flags));
     if (errorMessage.empty()) {
         return {};
     } else {
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index a0563f9..e8575a6 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -27,3 +27,10 @@
   description: "Set to true to enable timer support for the touchpad Gestures library"
   bug: "297192727"
 }
+
+flag {
+  name: "enable_multi_device_input"
+  namespace: "input"
+  description: "Set to true to enable multi-device input: touch and stylus can be active at the same time, but in different windows"
+  bug: "211379801"
+}
diff --git a/libs/input/rust/input.rs b/libs/input/rust/input.rs
index 9d3b386..9725b00 100644
--- a/libs/input/rust/input.rs
+++ b/libs/input/rust/input.rs
@@ -23,6 +23,54 @@
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
 pub struct DeviceId(pub i32);
 
+#[repr(u32)]
+pub enum SourceClass {
+    None = input_bindgen::AINPUT_SOURCE_CLASS_NONE,
+    Button = input_bindgen::AINPUT_SOURCE_CLASS_BUTTON,
+    Pointer = input_bindgen::AINPUT_SOURCE_CLASS_POINTER,
+    Navigation = input_bindgen::AINPUT_SOURCE_CLASS_NAVIGATION,
+    Position = input_bindgen::AINPUT_SOURCE_CLASS_POSITION,
+    Joystick = input_bindgen::AINPUT_SOURCE_CLASS_JOYSTICK,
+}
+
+bitflags! {
+    /// Source of the input device or input events.
+    pub struct Source: u32 {
+        /// SOURCE_UNKNOWN
+        const Unknown = input_bindgen::AINPUT_SOURCE_UNKNOWN;
+        /// SOURCE_KEYBOARD
+        const Keyboard = input_bindgen::AINPUT_SOURCE_KEYBOARD;
+        /// SOURCE_DPAD
+        const Dpad = input_bindgen::AINPUT_SOURCE_DPAD;
+        /// SOURCE_GAMEPAD
+        const Gamepad = input_bindgen::AINPUT_SOURCE_GAMEPAD;
+        /// SOURCE_TOUCHSCREEN
+        const Touchscreen = input_bindgen::AINPUT_SOURCE_TOUCHSCREEN;
+        /// SOURCE_MOUSE
+        const Mouse = input_bindgen::AINPUT_SOURCE_MOUSE;
+        /// SOURCE_STYLUS
+        const Stylus = input_bindgen::AINPUT_SOURCE_STYLUS;
+        /// SOURCE_BLUETOOTH_STYLUS
+        const BluetoothStylus = input_bindgen::AINPUT_SOURCE_BLUETOOTH_STYLUS;
+        /// SOURCE_TRACKBALL
+        const Trackball = input_bindgen::AINPUT_SOURCE_TRACKBALL;
+        /// SOURCE_MOUSE_RELATIVE
+        const MouseRelative = input_bindgen::AINPUT_SOURCE_MOUSE_RELATIVE;
+        /// SOURCE_TOUCHPAD
+        const Touchpad = input_bindgen::AINPUT_SOURCE_TOUCHPAD;
+        /// SOURCE_TOUCH_NAVIGATION
+        const TouchNavigation = input_bindgen::AINPUT_SOURCE_TOUCH_NAVIGATION;
+        /// SOURCE_JOYSTICK
+        const Joystick = input_bindgen::AINPUT_SOURCE_JOYSTICK;
+        /// SOURCE_HDMI
+        const HDMI = input_bindgen::AINPUT_SOURCE_HDMI;
+        /// SOURCE_SENSOR
+        const Sensor = input_bindgen::AINPUT_SOURCE_SENSOR;
+        /// SOURCE_ROTARY_ENCODER
+        const RotaryEncoder = input_bindgen::AINPUT_SOURCE_ROTARY_ENCODER;
+    }
+}
+
 /// A rust enum representation of a MotionEvent action.
 #[repr(u32)]
 pub enum MotionAction {
@@ -134,3 +182,11 @@
         const NO_FOCUS_CHANGE = input_bindgen::AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE;
     }
 }
+
+impl Source {
+    /// Return true if this source is from the provided class
+    pub fn is_from_class(&self, source_class: SourceClass) -> bool {
+        let class_bits = source_class as u32;
+        self.bits() & class_bits == class_bits
+    }
+}
diff --git a/libs/input/rust/input_verifier.rs b/libs/input/rust/input_verifier.rs
index 64c0466..2d94e70 100644
--- a/libs/input/rust/input_verifier.rs
+++ b/libs/input/rust/input_verifier.rs
@@ -17,7 +17,7 @@
 //! Contains the InputVerifier, used to validate a stream of input events.
 
 use crate::ffi::RustPointerProperties;
-use crate::input::{DeviceId, MotionAction, MotionFlags};
+use crate::input::{DeviceId, MotionAction, MotionFlags, Source, SourceClass};
 use log::info;
 use std::collections::HashMap;
 use std::collections::HashSet;
@@ -51,10 +51,15 @@
     pub fn process_movement(
         &mut self,
         device_id: DeviceId,
+        source: Source,
         action: u32,
         pointer_properties: &[RustPointerProperties],
         flags: MotionFlags,
     ) -> Result<(), String> {
+        if !source.is_from_class(SourceClass::Pointer) {
+            // Skip non-pointer sources like MOUSE_RELATIVE for now
+            return Ok(());
+        }
         if self.should_log {
             info!(
                 "Processing {} for device {:?} ({} pointer{}) on {}",
@@ -68,6 +73,13 @@
 
         match action.into() {
             MotionAction::Down => {
+                if pointer_properties.len() != 1 {
+                    return Err(format!(
+                        "{}: Invalid DOWN event: there are {} pointers in the event",
+                        self.name,
+                        pointer_properties.len()
+                    ));
+                }
                 let it = self
                     .touching_pointer_ids_by_device
                     .entry(device_id)
@@ -90,10 +102,19 @@
                     ));
                 }
                 let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
+                if it.len() != pointer_properties.len() - 1 {
+                    return Err(format!(
+                        "{}: There are currently {} touching pointers, but the incoming \
+                         POINTER_DOWN event has {}",
+                        self.name,
+                        it.len(),
+                        pointer_properties.len()
+                    ));
+                }
                 let pointer_id = pointer_properties[action_index].id;
                 if it.contains(&pointer_id) {
                     return Err(format!(
-                        "{}: Pointer with id={} not found in the properties",
+                        "{}: Pointer with id={} already present found in the properties",
                         self.name, pointer_id
                     ));
                 }
@@ -108,11 +129,10 @@
                 }
             }
             MotionAction::PointerUp { action_index } => {
-                if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
+                if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
                     return Err(format!(
-                        "{}: Received POINTER_UP but no pointers are currently down for device \
-                        {:?}",
-                        self.name, device_id
+                        "{}: ACTION_POINTER_UP touching pointers don't match",
+                        self.name
                     ));
                 }
                 let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
@@ -120,6 +140,13 @@
                 it.remove(&pointer_id);
             }
             MotionAction::Up => {
+                if pointer_properties.len() != 1 {
+                    return Err(format!(
+                        "{}: Invalid UP event: there are {} pointers in the event",
+                        self.name,
+                        pointer_properties.len()
+                    ));
+                }
                 if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
                     return Err(format!(
                         "{} Received ACTION_UP but no pointers are currently down for device {:?}",
@@ -246,6 +273,7 @@
     use crate::DeviceId;
     use crate::MotionFlags;
     use crate::RustPointerProperties;
+    use crate::Source;
     #[test]
     fn single_pointer_stream() {
         let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
@@ -253,6 +281,7 @@
         assert!(verifier
             .process_movement(
                 DeviceId(1),
+                Source::Touchscreen,
                 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
                 &pointer_properties,
                 MotionFlags::empty(),
@@ -261,6 +290,7 @@
         assert!(verifier
             .process_movement(
                 DeviceId(1),
+                Source::Touchscreen,
                 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
                 &pointer_properties,
                 MotionFlags::empty(),
@@ -269,6 +299,7 @@
         assert!(verifier
             .process_movement(
                 DeviceId(1),
+                Source::Touchscreen,
                 input_bindgen::AMOTION_EVENT_ACTION_UP,
                 &pointer_properties,
                 MotionFlags::empty(),
@@ -283,6 +314,7 @@
         assert!(verifier
             .process_movement(
                 DeviceId(1),
+                Source::Touchscreen,
                 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
                 &pointer_properties,
                 MotionFlags::empty(),
@@ -291,6 +323,7 @@
         assert!(verifier
             .process_movement(
                 DeviceId(1),
+                Source::Touchscreen,
                 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
                 &pointer_properties,
                 MotionFlags::empty(),
@@ -299,6 +332,7 @@
         assert!(verifier
             .process_movement(
                 DeviceId(2),
+                Source::Touchscreen,
                 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
                 &pointer_properties,
                 MotionFlags::empty(),
@@ -307,6 +341,7 @@
         assert!(verifier
             .process_movement(
                 DeviceId(2),
+                Source::Touchscreen,
                 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
                 &pointer_properties,
                 MotionFlags::empty(),
@@ -315,6 +350,7 @@
         assert!(verifier
             .process_movement(
                 DeviceId(1),
+                Source::Touchscreen,
                 input_bindgen::AMOTION_EVENT_ACTION_UP,
                 &pointer_properties,
                 MotionFlags::empty(),
@@ -329,6 +365,7 @@
         assert!(verifier
             .process_movement(
                 DeviceId(1),
+                Source::Touchscreen,
                 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
                 &pointer_properties,
                 MotionFlags::empty(),
@@ -337,6 +374,7 @@
         assert!(verifier
             .process_movement(
                 DeviceId(1),
+                Source::Touchscreen,
                 input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
                 &pointer_properties,
                 MotionFlags::CANCELED,
@@ -351,6 +389,7 @@
         assert!(verifier
             .process_movement(
                 DeviceId(1),
+                Source::Touchscreen,
                 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
                 &pointer_properties,
                 MotionFlags::empty(),
@@ -359,6 +398,7 @@
         assert!(verifier
             .process_movement(
                 DeviceId(1),
+                Source::Touchscreen,
                 input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
                 &pointer_properties,
                 MotionFlags::empty(), // forgot to set FLAG_CANCELED
@@ -373,6 +413,7 @@
         assert!(verifier
             .process_movement(
                 DeviceId(1),
+                Source::Touchscreen,
                 input_bindgen::AMOTION_EVENT_ACTION_UP,
                 &pointer_properties,
                 MotionFlags::empty(),
@@ -387,6 +428,7 @@
         assert!(verifier
             .process_movement(
                 DeviceId(1),
+                Source::Touchscreen,
                 input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
                 &pointer_properties,
                 MotionFlags::empty(),
@@ -396,6 +438,7 @@
         assert!(verifier
             .process_movement(
                 DeviceId(1),
+                Source::Touchscreen,
                 input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE,
                 &pointer_properties,
                 MotionFlags::empty(),
@@ -405,6 +448,7 @@
         assert!(verifier
             .process_movement(
                 DeviceId(1),
+                Source::Touchscreen,
                 input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT,
                 &pointer_properties,
                 MotionFlags::empty(),
@@ -414,6 +458,7 @@
         assert!(verifier
             .process_movement(
                 DeviceId(1),
+                Source::Touchscreen,
                 input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
                 &pointer_properties,
                 MotionFlags::empty(),
@@ -428,6 +473,7 @@
         assert!(verifier
             .process_movement(
                 DeviceId(1),
+                Source::Touchscreen,
                 input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
                 &pointer_properties,
                 MotionFlags::empty(),
@@ -437,10 +483,28 @@
         assert!(verifier
             .process_movement(
                 DeviceId(1),
+                Source::Touchscreen,
                 input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
                 &pointer_properties,
                 MotionFlags::empty(),
             )
             .is_err());
     }
+
+    // Send a MOVE without a preceding DOWN event. This is OK because it's from source
+    // MOUSE_RELATIVE, which is used during pointer capture. The verifier should allow such event.
+    #[test]
+    fn relative_mouse_move() {
+        let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+        let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+        assert!(verifier
+            .process_movement(
+                DeviceId(2),
+                Source::MouseRelative,
+                input_bindgen::AMOTION_EVENT_ACTION_MOVE,
+                &pointer_properties,
+                MotionFlags::empty(),
+            )
+            .is_ok());
+    }
 }
diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs
index 1d3c434..01d9599 100644
--- a/libs/input/rust/lib.rs
+++ b/libs/input/rust/lib.rs
@@ -19,7 +19,7 @@
 mod input;
 mod input_verifier;
 
-pub use input::{DeviceId, MotionAction, MotionFlags};
+pub use input::{DeviceId, MotionAction, MotionFlags, Source};
 pub use input_verifier::InputVerifier;
 
 #[cxx::bridge(namespace = "android::input")]
@@ -51,6 +51,7 @@
         fn process_movement(
             verifier: &mut InputVerifier,
             device_id: i32,
+            source: u32,
             action: u32,
             pointer_properties: &[RustPointerProperties],
             flags: u32,
@@ -73,12 +74,14 @@
 fn process_movement(
     verifier: &mut InputVerifier,
     device_id: i32,
+    source: u32,
     action: u32,
     pointer_properties: &[RustPointerProperties],
     flags: u32,
 ) -> String {
     let result = verifier.process_movement(
         DeviceId(device_id),
+        Source::from_bits(source).unwrap(),
         action,
         pointer_properties,
         MotionFlags::from_bits(flags).unwrap(),
diff --git a/libs/input/tests/TouchResampling_test.cpp b/libs/input/tests/TouchResampling_test.cpp
index 655de80..106d78a 100644
--- a/libs/input/tests/TouchResampling_test.cpp
+++ b/libs/input/tests/TouchResampling_test.cpp
@@ -31,6 +31,7 @@
     int32_t id;
     float x;
     float y;
+    ToolType toolType = ToolType::FINGER;
     bool isResampled = false;
 };
 
@@ -99,7 +100,7 @@
         properties.push_back({});
         properties.back().clear();
         properties.back().id = pointer.id;
-        properties.back().toolType = ToolType::FINGER;
+        properties.back().toolType = pointer.toolType;
 
         coords.push_back({});
         coords.back().clear();
@@ -292,6 +293,48 @@
 }
 
 /**
+ * Stylus pointer coordinates are not resampled, but an event is still generated for the batch with
+ * a resampled timestamp and should be marked as such.
+ */
+TEST_F(TouchResamplingTest, StylusCoordinatesNotResampledFor) {
+    std::chrono::nanoseconds frameTime;
+    std::vector<InputEventEntry> entries, expectedEntries;
+
+    // Initial ACTION_DOWN should be separate, because the first consume event will only return
+    // InputEvent with a single action.
+    entries = {
+            //      id  x   y
+            {0ms, {{0, 10, 20, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_DOWN},
+    };
+    publishInputEventEntries(entries);
+    frameTime = 5ms;
+    expectedEntries = {
+            //      id  x   y
+            {0ms, {{0, 10, 20, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_DOWN},
+    };
+    consumeInputEventEntries(expectedEntries, frameTime);
+
+    // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
+    entries = {
+            //      id  x   y
+            {10ms, {{0, 20, 30, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_MOVE},
+            {20ms, {{0, 30, 30, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_MOVE},
+    };
+    publishInputEventEntries(entries);
+    frameTime = 35ms;
+    expectedEntries = {
+            //      id  x   y
+            {10ms, {{0, 20, 30, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_MOVE},
+            {20ms, {{0, 30, 30, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_MOVE},
+            // A resampled event is generated, but the stylus coordinates are not resampled.
+            {25ms,
+             {{0, 30, 30, .toolType = ToolType::STYLUS, .isResampled = true}},
+             AMOTION_EVENT_ACTION_MOVE},
+    };
+    consumeInputEventEntries(expectedEntries, frameTime);
+}
+
+/**
  * Event should not be resampled when sample time is equal to event time.
  */
 TEST_F(TouchResamplingTest, SampleTimeEqualsEventTime) {
diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp
index 8d8a2bc..342f5de 100644
--- a/libs/nativedisplay/Android.bp
+++ b/libs/nativedisplay/Android.bp
@@ -73,6 +73,8 @@
         "libGLESv2",
     ],
 
+    static_libs: ["libguiflags"],
+
     export_header_lib_headers: ["jni_headers"],
 
     header_libs: [
diff --git a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
index 0f119f3..32fb350 100644
--- a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
+++ b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
@@ -19,6 +19,7 @@
 #include <android/hardware_buffer.h>
 #include <gui/BufferQueueDefs.h>
 #include <gui/ConsumerBase.h>
+#include <gui/Flags.h>
 #include <gui/IGraphicBufferProducer.h>
 #include <sys/cdefs.h>
 #include <system/graphics.h>
@@ -290,6 +291,20 @@
      */
     void releaseConsumerOwnership();
 
+    /**
+     * Interface for SurfaceTexture callback(s).
+     */
+    struct SurfaceTextureListener : public RefBase {
+        virtual void onFrameAvailable(const BufferItem& item) = 0;
+        virtual void onSetFrameRate(float frameRate, int8_t compatibility,
+                                    int8_t changeFrameRateStrategy) = 0;
+    };
+
+    /**
+     * setSurfaceTextureListener registers a SurfaceTextureListener.
+     */
+    void setSurfaceTextureListener(const sp<SurfaceTextureListener>&);
+
 protected:
     /**
      * abandonLocked overrides the ConsumerBase method to clear
@@ -335,6 +350,14 @@
     void computeCurrentTransformMatrixLocked();
 
     /**
+     * onSetFrameRate Notifies the consumer of a setFrameRate call from the producer side.
+     */
+#if FLAG_BQ_SET_FRAME_RATE
+    void onSetFrameRate(float frameRate, int8_t compatibility,
+                        int8_t changeFrameRateStrategy) override;
+#endif
+
+    /**
      * The default consumer usage flags that SurfaceTexture always sets on its
      * BufferQueue instance; these will be OR:d with any additional flags passed
      * from the SurfaceTexture user. In particular, SurfaceTexture will always
@@ -465,8 +488,30 @@
      */
     ImageConsumer mImageConsumer;
 
+    /**
+     * mSurfaceTextureListener holds the registered SurfaceTextureListener.
+     * Note that SurfaceTexture holds the lister with an sp<>, which means that the listener
+     * must only hold a wp<> to SurfaceTexture and not an sp<>.
+     */
+    sp<SurfaceTextureListener> mSurfaceTextureListener;
+
     friend class ImageConsumer;
     friend class EGLConsumer;
+
+private:
+    // Proxy listener to avoid having SurfaceTexture directly implement FrameAvailableListener as it
+    // is extending ConsumerBase which also implements FrameAvailableListener.
+    class FrameAvailableListenerProxy : public ConsumerBase::FrameAvailableListener {
+    public:
+        FrameAvailableListenerProxy(const wp<SurfaceTextureListener>& listener)
+              : mSurfaceTextureListener(listener) {}
+
+    private:
+        void onFrameAvailable(const BufferItem& item) override;
+
+        const wp<SurfaceTextureListener> mSurfaceTextureListener;
+    };
+    sp<FrameAvailableListenerProxy> mFrameAvailableListenerProxy;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
index 9f610e1..c2535e0 100644
--- a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
+++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
@@ -23,6 +23,8 @@
 #include <system/window.h>
 #include <utils/Trace.h>
 
+#include <com_android_graphics_libgui_flags.h>
+
 namespace android {
 
 // Macros for including the SurfaceTexture name in log messages
@@ -491,4 +493,42 @@
     return buffer;
 }
 
+void SurfaceTexture::setSurfaceTextureListener(
+        const sp<android::SurfaceTexture::SurfaceTextureListener>& listener) {
+    SFT_LOGV("setSurfaceTextureListener");
+
+    Mutex::Autolock _l(mMutex);
+    mSurfaceTextureListener = listener;
+    if (mSurfaceTextureListener != nullptr) {
+        mFrameAvailableListenerProxy =
+                sp<FrameAvailableListenerProxy>::make(mSurfaceTextureListener);
+        setFrameAvailableListener(mFrameAvailableListenerProxy);
+    } else {
+        mFrameAvailableListenerProxy.clear();
+    }
+}
+
+void SurfaceTexture::FrameAvailableListenerProxy::onFrameAvailable(const BufferItem& item) {
+    const auto listener = mSurfaceTextureListener.promote();
+    if (listener) {
+        listener->onFrameAvailable(item);
+    }
+}
+
+#if FLAG_BQ_SET_FRAME_RATE
+void SurfaceTexture::onSetFrameRate(float frameRate, int8_t compatibility,
+                                    int8_t changeFrameRateStrategy) {
+    SFT_LOGV("onSetFrameRate: %.2f", frameRate);
+
+    auto listener = [&] {
+        Mutex::Autolock _l(mMutex);
+        return mSurfaceTextureListener;
+    }();
+
+    if (listener) {
+        listener->onSetFrameRate(frameRate, compatibility, changeFrameRateStrategy);
+    }
+}
+#endif
+
 } // namespace android
diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp
index f889de5..0f62324 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.cpp
+++ b/services/inputflinger/UnwantedInteractionBlocker.cpp
@@ -18,6 +18,7 @@
 #include "UnwantedInteractionBlocker.h"
 
 #include <android-base/stringprintf.h>
+#include <com_android_input_flags.h>
 #include <ftl/enum.h>
 #include <input/PrintTools.h>
 #include <inttypes.h>
@@ -28,6 +29,8 @@
 #include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.h"
 #include "ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.h"
 
+namespace input_flags = com::android::input::flags;
+
 using android::base::StringPrintf;
 
 /**
@@ -344,10 +347,14 @@
     ALOGD_IF(DEBUG_INBOUND_MOTION, "%s: %s", __func__, args.dump().c_str());
     { // acquire lock
         std::scoped_lock lock(mLock);
-        const std::vector<NotifyMotionArgs> processedArgs =
-                mPreferStylusOverTouchBlocker.processMotion(args);
-        for (const NotifyMotionArgs& loopArgs : processedArgs) {
-            notifyMotionLocked(loopArgs);
+        if (input_flags::enable_multi_device_input()) {
+            notifyMotionLocked(args);
+        } else {
+            const std::vector<NotifyMotionArgs> processedArgs =
+                    mPreferStylusOverTouchBlocker.processMotion(args);
+            for (const NotifyMotionArgs& loopArgs : processedArgs) {
+                notifyMotionLocked(loopArgs);
+            }
         }
     } // release lock
 
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 9cd1e09..5158220 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -1293,7 +1293,7 @@
 }
 
 std::vector<InputTarget> InputDispatcher::findOutsideTargetsLocked(
-        int32_t displayId, const sp<WindowInfoHandle>& touchedWindow) const {
+        int32_t displayId, const sp<WindowInfoHandle>& touchedWindow, int32_t pointerId) const {
     if (touchedWindow == nullptr) {
         return {};
     }
@@ -1309,9 +1309,10 @@
 
         const WindowInfo& info = *windowHandle->getInfo();
         if (info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) {
-            addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_OUTSIDE,
-                                  /*pointerIds=*/{}, /*firstDownTimeInTarget=*/std::nullopt,
-                                  outsideTargets);
+            std::bitset<MAX_POINTER_ID + 1> pointerIds;
+            pointerIds.set(pointerId);
+            addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_OUTSIDE, pointerIds,
+                                  /*firstDownTimeInTarget=*/std::nullopt, outsideTargets);
         }
     }
     return outsideTargets;
@@ -2333,6 +2334,7 @@
         /* Case 1: New splittable pointer going down, or need target for hover or scroll. */
         const auto [x, y] = resolveTouchedPosition(entry);
         const int32_t pointerIndex = MotionEvent::getActionIndex(action);
+        const int32_t pointerId = entry.pointerProperties[pointerIndex].id;
         // Outside targets should be added upon first dispatched DOWN event. That means, this should
         // be a pointer that would generate ACTION_DOWN, *and* touch should not already be down.
         const bool isStylus = isPointerFromStylus(entry, pointerIndex);
@@ -2340,7 +2342,7 @@
                 findTouchedWindowAtLocked(displayId, x, y, isStylus);
 
         if (isDown) {
-            targets += findOutsideTargetsLocked(displayId, newTouchedWindowHandle);
+            targets += findOutsideTargetsLocked(displayId, newTouchedWindowHandle, pointerId);
         }
         // Handle the case where we did not find a window.
         if (newTouchedWindowHandle == nullptr) {
@@ -2398,7 +2400,6 @@
 
             if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
                 maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
-                const int32_t pointerId = entry.pointerProperties[0].id;
                 // The "windowHandle" is the target of this hovering pointer.
                 tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId, pointerId);
             }
@@ -2423,7 +2424,7 @@
             // Update the temporary touch state.
             std::bitset<MAX_POINTER_ID + 1> pointerIds;
             if (!isHoverAction) {
-                pointerIds.set(entry.pointerProperties[pointerIndex].id);
+                pointerIds.set(pointerId);
             }
 
             const bool isDownOrPointerDown = maskedAction == AMOTION_EVENT_ACTION_DOWN ||
@@ -2467,7 +2468,6 @@
         // If a window is already pilfering some pointers, give it this new pointer as well and
         // make it pilfering. This will prevent other non-spy windows from getting this pointer,
         // which is a specific behaviour that we want.
-        const int32_t pointerId = entry.pointerProperties[pointerIndex].id;
         for (TouchedWindow& touchedWindow : tempTouchState.windows) {
             if (touchedWindow.hasTouchingPointer(entry.deviceId, pointerId) &&
                 touchedWindow.hasPilferingPointers(entry.deviceId)) {
@@ -4324,9 +4324,9 @@
                 mVerifiersByDisplay.try_emplace(args.displayId,
                                                 StringPrintf("display %" PRId32, args.displayId));
         Result<void> result =
-                it->second.processMovement(args.deviceId, args.action, args.getPointerCount(),
-                                           args.pointerProperties.data(), args.pointerCoords.data(),
-                                           args.flags);
+                it->second.processMovement(args.deviceId, args.source, args.action,
+                                           args.getPointerCount(), args.pointerProperties.data(),
+                                           args.pointerCoords.data(), args.flags);
         if (!result.ok()) {
             LOG(FATAL) << "Bad stream: " << result.error() << " caused by " << args.dump();
         }
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 4d145c6..67705b9 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -242,8 +242,8 @@
             int32_t displayId, float x, float y, bool isStylus = false,
             bool ignoreDragWindow = false) const REQUIRES(mLock);
     std::vector<InputTarget> findOutsideTargetsLocked(
-            int32_t displayId, const sp<android::gui::WindowInfoHandle>& touchedWindow) const
-            REQUIRES(mLock);
+            int32_t displayId, const sp<android::gui::WindowInfoHandle>& touchedWindow,
+            int32_t pointerId) const REQUIRES(mLock);
 
     std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked(
             int32_t displayId, float x, float y, bool isStylus) const REQUIRES(mLock);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index a73c511..5890050 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -3183,8 +3183,7 @@
     } else {
         // release sideband stream if it exists and a non null buffer is being set
         if (mDrawingState.sidebandStream != nullptr) {
-            mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount();
-            mDrawingState.sidebandStream = nullptr;
+            setSidebandStream(nullptr, info, postTime);
         }
     }
 
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index c5870d4..dcef54d 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -944,6 +944,11 @@
             return VK_ERROR_SURFACE_LOST_KHR;
         }
 
+        // Additional buffer count over min_undequeued_buffers in vulkan came from 2 total
+        // being technically enough for fifo (although a poor experience) vs 3 being the
+        // absolute minimum for mailbox to be useful. So min_undequeued_buffers + 2 is sensible
+        static constexpr int default_additional_buffers = 2;
+
         if(pPresentMode != nullptr) {
             switch (pPresentMode->presentMode) {
                 case VK_PRESENT_MODE_IMMEDIATE_KHR:
@@ -951,8 +956,8 @@
                     break;
                 case VK_PRESENT_MODE_MAILBOX_KHR:
                 case VK_PRESENT_MODE_FIFO_KHR:
-                    capabilities->minImageCount =
-                        std::min(max_buffer_count, min_undequeued_buffers + 2);
+                    capabilities->minImageCount = std::min(max_buffer_count,
+                            min_undequeued_buffers + default_additional_buffers);
                     capabilities->maxImageCount = static_cast<uint32_t>(max_buffer_count);
                     break;
                 case VK_PRESENT_MODE_FIFO_RELAXED_KHR:
@@ -971,7 +976,8 @@
                     break;
             }
         } else {
-            capabilities->minImageCount = std::min(max_buffer_count, min_undequeued_buffers + 2);
+            capabilities->minImageCount = std::min(max_buffer_count,
+                    min_undequeued_buffers + default_additional_buffers);
             capabilities->maxImageCount = static_cast<uint32_t>(max_buffer_count);
         }
     }