[input] impl the fidl server to read the touchpad_mode switch

Bug: 99687
Change-Id: I63ddb2e8107bf9bca617bbb63215eefbb306f231
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/678925
Commit-Queue: Jianpeng Chao <chaopeng@google.com>
Reviewed-by: Alice Neels <neelsa@google.com>
Reviewed-by: Mukesh Agrawal <quiche@google.com>
diff --git a/src/ui/bin/scene_manager/BUILD.gn b/src/ui/bin/scene_manager/BUILD.gn
index 7b2db6b..a8a5864 100644
--- a/src/ui/bin/scene_manager/BUILD.gn
+++ b/src/ui/bin/scene_manager/BUILD.gn
@@ -28,6 +28,7 @@
     "//sdk/fidl/fuchsia.ui.app:fuchsia.ui.app-rustc",
     "//sdk/fidl/fuchsia.ui.composition:fuchsia.ui.composition-rustc",
     "//sdk/fidl/fuchsia.ui.input:fuchsia.ui.input-rustc",
+    "//sdk/fidl/fuchsia.ui.input.config:fuchsia.ui.input.config-rustc",
     "//sdk/fidl/fuchsia.ui.pointerinjector.configuration:fuchsia.ui.pointerinjector.configuration-rustc",
     "//sdk/fidl/fuchsia.ui.scenic:fuchsia.ui.scenic-rustc",
     "//sdk/fidl/fuchsia.ui.shortcut:fuchsia.ui.shortcut-rustc",
@@ -61,6 +62,7 @@
   ]
 
   sources = [
+    "src/input_config_server.rs",
     "src/input_device_registry_server.rs",
     "src/input_pipeline.rs",
     "src/main.rs",
diff --git a/src/ui/bin/scene_manager/meta/scene_manager.cml b/src/ui/bin/scene_manager/meta/scene_manager.cml
index 75f7f81..cd2d226 100644
--- a/src/ui/bin/scene_manager/meta/scene_manager.cml
+++ b/src/ui/bin/scene_manager/meta/scene_manager.cml
@@ -22,6 +22,7 @@
                 "fuchsia.input.injection.InputDeviceRegistry",
                 "fuchsia.session.scene.Manager",
                 "fuchsia.ui.accessibility.view.Registry",
+                "fuchsia.ui.input.config.Features",
             ],
         },
     ],
@@ -86,6 +87,7 @@
                 "fuchsia.input.injection.InputDeviceRegistry",
                 "fuchsia.session.scene.Manager",
                 "fuchsia.ui.accessibility.view.Registry",
+                "fuchsia.ui.input.config.Features",
             ],
             from: "self",
         },
diff --git a/src/ui/bin/scene_manager/src/input_config_server.rs b/src/ui/bin/scene_manager/src/input_config_server.rs
new file mode 100644
index 0000000..87bbbc3
--- /dev/null
+++ b/src/ui/bin/scene_manager/src/input_config_server.rs
@@ -0,0 +1,65 @@
+// Copyright 2022 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use {
+    anyhow::Error, fidl_fuchsia_ui_input_config::FeaturesRequestStream,
+    futures::channel::mpsc::UnboundedSender,
+};
+
+/// A struct which forwards `FeaturesRequestStream`s over an
+/// `mpsc::UnboundedSender`.
+pub(crate) struct InputConfigFeaturesServer {
+    sender: UnboundedSender<FeaturesRequestStream>,
+}
+
+/// Creates an `InputConfigFeaturesServer`.
+///
+/// Returns both the server, and the `mpsc::UnboundedReceiver` which can be
+/// used to receive `FeaturesRequest`'s forwarded by the server.
+pub(crate) fn make_server_and_receiver(
+) -> (InputConfigFeaturesServer, futures::channel::mpsc::UnboundedReceiver<FeaturesRequestStream>) {
+    let (sender, receiver) = futures::channel::mpsc::unbounded::<FeaturesRequestStream>();
+    (InputConfigFeaturesServer { sender }, receiver)
+}
+
+impl InputConfigFeaturesServer {
+    /// Handles the incoming `FeaturesRequest`.
+    ///
+    /// Simply forwards the request over the `mpsc::UnboundedSender`.
+    pub async fn handle_request(&self, stream: FeaturesRequestStream) -> Result<(), Error> {
+        self.sender.unbounded_send(stream).map_err(anyhow::Error::from)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use {
+        super::*, assert_matches::assert_matches, fidl::endpoints::create_proxy_and_stream,
+        fidl_fuchsia_ui_input_config::FeaturesMarker, fuchsia_async as fasync,
+    };
+
+    #[fasync::run_singlethreaded(test)]
+    async fn test_handle_request_forwards_stream_and_returns_ok() {
+        let (server, mut receiver) = make_server_and_receiver();
+        let (_proxy, stream) =
+            create_proxy_and_stream::<FeaturesMarker>().expect("should make proxy/stream");
+        assert_matches!(server.handle_request(stream).await, Ok(()));
+
+        // Note: can't use `assert_matches!()` here, because `FeaturesRequest`
+        // does not implement `Debug`.
+        match receiver.try_next() {
+            Ok(opt) => assert!(opt.is_some()),
+            Err(e) => panic!("reading failed with {:#?}", e),
+        }
+    }
+
+    #[fasync::run_singlethreaded(test)]
+    async fn test_handle_request_returns_error_on_disconnected_receiver() {
+        let (server, receiver) = make_server_and_receiver();
+        let (_proxy, stream) =
+            create_proxy_and_stream::<FeaturesMarker>().expect("should make proxy/stream");
+        std::mem::drop(receiver);
+        assert_matches!(server.handle_request(stream).await, Err(_));
+    }
+}
diff --git a/src/ui/bin/scene_manager/src/input_pipeline.rs b/src/ui/bin/scene_manager/src/input_pipeline.rs
index f4d64ef..64dd932 100644
--- a/src/ui/bin/scene_manager/src/input_pipeline.rs
+++ b/src/ui/bin/scene_manager/src/input_pipeline.rs
@@ -7,6 +7,7 @@
     anyhow::{Context, Error},
     fidl_fuchsia_input_injection::InputDeviceRegistryRequestStream,
     fidl_fuchsia_settings as fsettings,
+    fidl_fuchsia_ui_input_config::FeaturesRequestStream as InputConfigFeaturesRequestStream,
     fidl_fuchsia_ui_pointerinjector_configuration::SetupProxy,
     fidl_fuchsia_ui_shortcut as ui_shortcut, fuchsia_async as fasync,
     fuchsia_component::client::connect_to_protocol,
@@ -35,6 +36,8 @@
 ///
 /// # Parameters
 /// - `scene_manager`: The scene manager used by the session.
+/// - `input_config_request_stream_receiver`:  A receiving end of a MPSC channel for
+///   `InputConfig` messages.
 /// - `input_device_registry_request_stream_receiver`: A receiving end of a MPSC channel for
 ///   `InputDeviceRegistry` messages.
 /// - `node`: The inspect node to insert individual inspect handler nodes into.
@@ -43,6 +46,9 @@
     // new Flatland API.
     use_flatland: bool,
     scene_manager: Arc<Mutex<Box<dyn SceneManager>>>,
+    input_config_request_stream_receiver: futures::channel::mpsc::UnboundedReceiver<
+        InputConfigFeaturesRequestStream,
+    >,
     input_device_registry_request_stream_receiver: futures::channel::mpsc::UnboundedReceiver<
         InputDeviceRegistryRequestStream,
     >,
@@ -76,6 +82,13 @@
 
     fasync::Task::local(input_device_registry_fut).detach();
 
+    let input_config_fut = handle_input_config_request_streams(
+        input_config_request_stream_receiver,
+        input_pipeline.input_device_bindings().clone(),
+    );
+
+    fasync::Task::local(input_config_fut).detach();
+
     Ok(input_pipeline)
 }
 
@@ -323,6 +336,28 @@
     assembly.add_handler(ImmersiveModeShortcutHandler::new())
 }
 
+pub async fn handle_input_config_request_streams(
+    mut stream_receiver: futures::channel::mpsc::UnboundedReceiver<
+        InputConfigFeaturesRequestStream,
+    >,
+    input_device_bindings: InputDeviceBindingHashMap,
+) {
+    while let Some(stream) = stream_receiver.next().await {
+        match InputPipeline::handle_input_config_request_stream(stream, &input_device_bindings)
+            .await
+        {
+            Ok(()) => (),
+            Err(e) => {
+                fx_log_warn!(
+                    "failure while serving InputConfig.Features: {}; \
+                     will continue serving other clients",
+                    e
+                );
+            }
+        }
+    }
+}
+
 pub async fn handle_input_device_registry_request_streams(
     mut stream_receiver: futures::channel::mpsc::UnboundedReceiver<
         InputDeviceRegistryRequestStream,
diff --git a/src/ui/bin/scene_manager/src/main.rs b/src/ui/bin/scene_manager/src/main.rs
index 0a3cf66..99875aa 100644
--- a/src/ui/bin/scene_manager/src/main.rs
+++ b/src/ui/bin/scene_manager/src/main.rs
@@ -16,6 +16,7 @@
         RegistryRequestStream as A11yViewRegistryRequestStream,
     },
     fidl_fuchsia_ui_app as ui_app, fidl_fuchsia_ui_composition as fland,
+    fidl_fuchsia_ui_input_config::FeaturesRequestStream as InputConfigFeaturesRequestStream,
     fidl_fuchsia_ui_scenic::ScenicMarker,
     fidl_fuchsia_ui_views as ui_views, fuchsia_async as fasync,
     fuchsia_component::{client::connect_to_protocol, server::ServiceFs},
@@ -29,6 +30,7 @@
     std::sync::Arc,
 };
 
+mod input_config_server;
 mod input_device_registry_server;
 mod input_pipeline;
 
@@ -36,6 +38,7 @@
     AccessibilityViewRegistry(A11yViewRegistryRequestStream),
     SceneManager(SceneManagerRequestStream),
     InputDeviceRegistry(InputDeviceRegistryRequestStream),
+    InputConfigFeatures(InputConfigFeaturesRequestStream),
 }
 
 #[fuchsia::main(logging_tags = [ "scene_manager" ])]
@@ -47,11 +50,15 @@
     fs.dir("svc").add_fidl_service(ExposedServices::AccessibilityViewRegistry);
     fs.dir("svc").add_fidl_service(ExposedServices::SceneManager);
     fs.dir("svc").add_fidl_service(ExposedServices::InputDeviceRegistry);
+    fs.dir("svc").add_fidl_service(ExposedServices::InputConfigFeatures);
     fs.take_and_serve_directory_handle()?;
 
     let (input_device_registry_server, input_device_registry_request_stream_receiver) =
         input_device_registry_server::make_server_and_receiver();
 
+    let (input_config_server, input_config_receiver) =
+        input_config_server::make_server_and_receiver();
+
     // This call should normally never fail. The ICU data loader must be kept alive to ensure
     // Unicode data is kept in memory.
     let icu_data_loader = icu_data::Loader::new().unwrap();
@@ -103,6 +110,7 @@
     if let Ok(input_pipeline) = input_pipeline::handle_input(
         use_flatland,
         scene_manager.clone(),
+        input_config_receiver,
         input_device_registry_request_stream_receiver,
         icu_data_loader,
         &inspect_node.clone(),
@@ -155,6 +163,14 @@
                     }
                 }
             }
+            ExposedServices::InputConfigFeatures(request_stream) => {
+                match &input_config_server.handle_request(request_stream).await {
+                    Ok(()) => (),
+                    Err(e) => {
+                        fx_log_warn!("failed to forward InputConfigFeaturesRequestStream: {:?}", e)
+                    }
+                }
+            }
         }
     }
 
diff --git a/src/ui/lib/input_pipeline/src/consumer_controls_binding.rs b/src/ui/lib/input_pipeline/src/consumer_controls_binding.rs
index 8b90510..94f9bd2 100644
--- a/src/ui/lib/input_pipeline/src/consumer_controls_binding.rs
+++ b/src/ui/lib/input_pipeline/src/consumer_controls_binding.rs
@@ -8,6 +8,7 @@
     async_trait::async_trait,
     fidl_fuchsia_input_report as fidl_input_report,
     fidl_fuchsia_input_report::{InputDeviceProxy, InputReport},
+    fidl_fuchsia_ui_input_config::FeaturesRequest as InputConfigFeaturesRequest,
     fuchsia_syslog::fx_log_err,
     fuchsia_zircon as zx,
     futures::channel::mpsc::Sender,
@@ -68,6 +69,13 @@
     fn get_device_descriptor(&self) -> input_device::InputDeviceDescriptor {
         input_device::InputDeviceDescriptor::ConsumerControls(self.device_descriptor.clone())
     }
+
+    async fn handle_input_config_request(
+        &self,
+        _request: &InputConfigFeaturesRequest,
+    ) -> Result<(), Error> {
+        Ok(())
+    }
 }
 
 impl ConsumerControlsBinding {
diff --git a/src/ui/lib/input_pipeline/src/fake_input_device_binding.rs b/src/ui/lib/input_pipeline/src/fake_input_device_binding.rs
index c7a5d87..65ccb09 100644
--- a/src/ui/lib/input_pipeline/src/fake_input_device_binding.rs
+++ b/src/ui/lib/input_pipeline/src/fake_input_device_binding.rs
@@ -3,7 +3,8 @@
 // found in the LICENSE file.
 
 use {
-    crate::input_device, crate::keyboard_binding, async_trait::async_trait,
+    crate::input_device, crate::keyboard_binding, anyhow::Error, async_trait::async_trait,
+    fidl_fuchsia_ui_input_config::FeaturesRequest as InputConfigFeaturesRequest,
     futures::channel::mpsc::Sender,
 };
 
@@ -11,12 +12,18 @@
 pub struct FakeInputDeviceBinding {
     /// The channel to stream InputEvents to.
     event_sender: Sender<input_device::InputEvent>,
+
+    /// The channel to stream received input config requests.
+    input_config_requests_sender: Sender<InputConfigFeaturesRequest>,
 }
 
 #[allow(dead_code)]
 impl FakeInputDeviceBinding {
-    pub fn new(input_event_sender: Sender<input_device::InputEvent>) -> Self {
-        FakeInputDeviceBinding { event_sender: input_event_sender }
+    pub fn new(
+        input_event_sender: Sender<input_device::InputEvent>,
+        input_config_requests_sender: Sender<InputConfigFeaturesRequest>,
+    ) -> Self {
+        FakeInputDeviceBinding { event_sender: input_event_sender, input_config_requests_sender }
     }
 }
 
@@ -31,4 +38,24 @@
     fn input_event_sender(&self) -> Sender<input_device::InputEvent> {
         self.event_sender.clone()
     }
+
+    async fn handle_input_config_request(
+        &self,
+        request: &InputConfigFeaturesRequest,
+    ) -> Result<(), Error> {
+        let copied = match request {
+            InputConfigFeaturesRequest::SetTouchpadMode { enable, control_handle } => {
+                InputConfigFeaturesRequest::SetTouchpadMode {
+                    enable: *enable,
+                    control_handle: control_handle.to_owned(),
+                }
+            }
+        };
+
+        self.input_config_requests_sender
+            .clone()
+            .try_send(copied)
+            .expect("send input_config_request");
+        Ok(())
+    }
 }
diff --git a/src/ui/lib/input_pipeline/src/input_device.rs b/src/ui/lib/input_pipeline/src/input_device.rs
index 9929286..3a76a99 100644
--- a/src/ui/lib/input_pipeline/src/input_device.rs
+++ b/src/ui/lib/input_pipeline/src/input_device.rs
@@ -13,7 +13,9 @@
     fidl::endpoints::Proxy,
     fidl_fuchsia_input_report as fidl_input_report,
     fidl_fuchsia_input_report::{InputDeviceMarker, InputReport},
-    fidl_fuchsia_io as fio, fuchsia_async as fasync, fuchsia_zircon as zx,
+    fidl_fuchsia_io as fio,
+    fidl_fuchsia_ui_input_config::FeaturesRequest as InputConfigFeaturesRequest,
+    fuchsia_async as fasync, fuchsia_zircon as zx,
     futures::{channel::mpsc::Sender, stream::StreamExt},
     std::path::PathBuf,
 };
@@ -134,6 +136,12 @@
 
     /// Returns the input event stream's sender.
     fn input_event_sender(&self) -> Sender<InputEvent>;
+
+    /// Handles input config changes.
+    async fn handle_input_config_request(
+        &self,
+        request: &InputConfigFeaturesRequest,
+    ) -> Result<(), Error>;
 }
 
 /// Initializes the input report stream for the device bound to `device_proxy`.
diff --git a/src/ui/lib/input_pipeline/src/input_pipeline.rs b/src/ui/lib/input_pipeline/src/input_pipeline.rs
index dec82d6..fcf4419 100644
--- a/src/ui/lib/input_pipeline/src/input_pipeline.rs
+++ b/src/ui/lib/input_pipeline/src/input_pipeline.rs
@@ -8,7 +8,9 @@
         focus_listener::FocusListener, input_device, input_handler,
     },
     anyhow::{format_err, Context, Error},
-    fidl_fuchsia_input_injection, fidl_fuchsia_io as fio, fuchsia_async as fasync,
+    fidl_fuchsia_input_injection, fidl_fuchsia_io as fio,
+    fidl_fuchsia_ui_input_config::FeaturesRequestStream as InputConfigFeaturesRequestStream,
+    fuchsia_async as fasync,
     fuchsia_fs::open_directory_in_namespace,
     fuchsia_syslog::{fx_log_err, fx_log_warn},
     fuchsia_vfs_watcher::{WatchEvent, Watcher},
@@ -454,6 +456,35 @@
         Ok(())
     }
 
+    /// Handles the incoming InputConfigFeaturesRequestStream.
+    ///
+    /// This method will end when the request stream is closed. If the stream closes with an
+    /// error the error will be returned in the Result.
+    ///
+    /// # Parameters
+    /// - `stream`: The stream of InputConfigFeaturesRequests.
+    /// - `bindings`: Holds all the InputDeviceBindings associated with the InputPipeline.
+    pub async fn handle_input_config_request_stream(
+        mut stream: InputConfigFeaturesRequestStream,
+        bindings: &InputDeviceBindingHashMap,
+    ) -> Result<(), Error> {
+        while let Some(request) =
+            stream.try_next().await.context("Error handling input config request stream")?
+        {
+            let bindings = bindings.lock().await;
+            for v in bindings.values() {
+                for binding in v.iter() {
+                    match binding.handle_input_config_request(&request).await {
+                        Ok(()) => (),
+                        Err(e) => fx_log_err!("Error handling input config request {:?}", e),
+                    }
+                }
+            }
+        }
+
+        Ok(())
+    }
+
     /// Starts all tasks in an asynchronous executor.
     fn run(tasks: Vec<fuchsia_async::Task<()>>) {
         fasync::Task::local(async move {
@@ -556,7 +587,12 @@
         crate::input_device::{self, InputDeviceBinding},
         crate::mouse_binding,
         crate::utils::Position,
+        assert_matches::assert_matches,
         fidl::endpoints::{create_proxy, create_proxy_and_stream, create_request_stream},
+        fidl_fuchsia_ui_input_config::{
+            FeaturesMarker as InputConfigFeaturesMarker,
+            FeaturesRequest as InputConfigFeaturesRequest,
+        },
         fuchsia_async as fasync, fuchsia_zircon as zx,
         futures::channel::mpsc::Sender,
         futures::FutureExt,
@@ -652,10 +688,16 @@
         // Create two fake device bindings.
         let (device_event_sender, device_event_receiver) =
             futures::channel::mpsc::channel(input_device::INPUT_EVENT_BUFFER_SIZE);
-        let first_device_binding =
-            fake_input_device_binding::FakeInputDeviceBinding::new(device_event_sender.clone());
-        let second_device_binding =
-            fake_input_device_binding::FakeInputDeviceBinding::new(device_event_sender.clone());
+        let (input_config_features_sender, _input_config_features_receiver) =
+            futures::channel::mpsc::channel(1);
+        let first_device_binding = fake_input_device_binding::FakeInputDeviceBinding::new(
+            device_event_sender.clone(),
+            input_config_features_sender.clone(),
+        );
+        let second_device_binding = fake_input_device_binding::FakeInputDeviceBinding::new(
+            device_event_sender.clone(),
+            input_config_features_sender.clone(),
+        );
 
         // Create a fake input handler.
         let (handler_event_sender, mut handler_event_receiver) =
@@ -699,8 +741,12 @@
         // Create two fake device bindings.
         let (device_event_sender, device_event_receiver) =
             futures::channel::mpsc::channel(input_device::INPUT_EVENT_BUFFER_SIZE);
-        let input_device_binding =
-            fake_input_device_binding::FakeInputDeviceBinding::new(device_event_sender.clone());
+        let (input_config_features_sender, _input_config_features_receiver) =
+            futures::channel::mpsc::channel(1);
+        let input_device_binding = fake_input_device_binding::FakeInputDeviceBinding::new(
+            device_event_sender.clone(),
+            input_config_features_sender.clone(),
+        );
 
         // Create two fake input handlers.
         let (first_handler_event_sender, mut first_handler_event_receiver) =
@@ -953,4 +999,38 @@
         let bindings = bindings.lock().await;
         assert_eq!(bindings.len(), 1);
     }
+
+    /// Tests that config changes are forwarded to device bindings.
+    #[fasync::run_singlethreaded(test)]
+    async fn handle_input_config_request_stream() {
+        let (device_event_sender, _device_event_receiver) =
+            futures::channel::mpsc::channel(input_device::INPUT_EVENT_BUFFER_SIZE);
+        let (input_config_features_sender, mut input_config_features_receiver) =
+            futures::channel::mpsc::channel(1);
+        let fake_device_binding = fake_input_device_binding::FakeInputDeviceBinding::new(
+            device_event_sender,
+            input_config_features_sender,
+        );
+        let bindings: InputDeviceBindingHashMap = Arc::new(Mutex::new(HashMap::new()));
+        bindings.lock().await.insert(1, vec![Box::new(fake_device_binding)]);
+
+        let bindings_clone = bindings.clone();
+
+        let (input_config_features_proxy, input_config_features_request_stream) =
+            create_proxy_and_stream::<InputConfigFeaturesMarker>().unwrap();
+        input_config_features_proxy.set_touchpad_mode(true).expect("set_touchpad_mode");
+        // Drop proxy to terminate request stream.
+        std::mem::drop(input_config_features_proxy);
+        InputPipeline::handle_input_config_request_stream(
+            input_config_features_request_stream,
+            &bindings_clone,
+        )
+        .await
+        .expect("handle_input_config_request_stream");
+
+        assert_matches!(
+            input_config_features_receiver.next().await.unwrap(),
+            InputConfigFeaturesRequest::SetTouchpadMode { enable: true, .. }
+        );
+    }
 }
diff --git a/src/ui/lib/input_pipeline/src/keyboard_binding.rs b/src/ui/lib/input_pipeline/src/keyboard_binding.rs
index 90b13c2..96aa735 100644
--- a/src/ui/lib/input_pipeline/src/keyboard_binding.rs
+++ b/src/ui/lib/input_pipeline/src/keyboard_binding.rs
@@ -11,6 +11,7 @@
     fidl_fuchsia_input_report::{InputDeviceProxy, InputReport},
     fidl_fuchsia_ui_input3 as fidl_ui_input3,
     fidl_fuchsia_ui_input3::KeyEventType,
+    fidl_fuchsia_ui_input_config::FeaturesRequest as InputConfigFeaturesRequest,
     fuchsia_async as fasync,
     fuchsia_syslog::fx_log_err,
     fuchsia_zircon as zx,
@@ -230,6 +231,13 @@
     fn get_device_descriptor(&self) -> input_device::InputDeviceDescriptor {
         input_device::InputDeviceDescriptor::Keyboard(self.device_descriptor.clone())
     }
+
+    async fn handle_input_config_request(
+        &self,
+        _request: &InputConfigFeaturesRequest,
+    ) -> Result<(), Error> {
+        Ok(())
+    }
 }
 
 impl KeyboardBinding {
diff --git a/src/ui/lib/input_pipeline/src/mouse_binding.rs b/src/ui/lib/input_pipeline/src/mouse_binding.rs
index 03d317a..2a7be61 100644
--- a/src/ui/lib/input_pipeline/src/mouse_binding.rs
+++ b/src/ui/lib/input_pipeline/src/mouse_binding.rs
@@ -9,6 +9,7 @@
     async_trait::async_trait,
     fidl_fuchsia_input_report as fidl_input_report,
     fidl_fuchsia_input_report::{InputDeviceProxy, InputReport},
+    fidl_fuchsia_ui_input_config::FeaturesRequest as InputConfigFeaturesRequest,
     fuchsia_syslog::fx_log_err,
     fuchsia_zircon as zx,
     futures::channel::mpsc::Sender,
@@ -161,6 +162,13 @@
     fn get_device_descriptor(&self) -> input_device::InputDeviceDescriptor {
         input_device::InputDeviceDescriptor::Mouse(self.device_descriptor.clone())
     }
+
+    async fn handle_input_config_request(
+        &self,
+        _request: &InputConfigFeaturesRequest,
+    ) -> Result<(), Error> {
+        Ok(())
+    }
 }
 
 impl MouseBinding {
diff --git a/src/ui/lib/input_pipeline/src/touch_binding.rs b/src/ui/lib/input_pipeline/src/touch_binding.rs
index 7922a17..014e873 100644
--- a/src/ui/lib/input_pipeline/src/touch_binding.rs
+++ b/src/ui/lib/input_pipeline/src/touch_binding.rs
@@ -9,7 +9,9 @@
     async_trait::async_trait,
     fidl_fuchsia_input_report as fidl_input_report,
     fidl_fuchsia_input_report::{InputDeviceProxy, InputReport},
-    fidl_fuchsia_ui_input as fidl_ui_input, fidl_fuchsia_ui_pointerinjector as pointerinjector,
+    fidl_fuchsia_ui_input as fidl_ui_input,
+    fidl_fuchsia_ui_input_config::FeaturesRequest as InputConfigFeaturesRequest,
+    fidl_fuchsia_ui_pointerinjector as pointerinjector,
     fuchsia_syslog::fx_log_err,
     fuchsia_zircon as zx,
     futures::channel::mpsc::Sender,
@@ -157,8 +159,10 @@
     device_descriptor: TouchDeviceDescriptor,
 
     /// Touch device type of the touch device.
-    /// TODO(fxb/99687): following CL will begin use this field and remove the underscore.
-    _touch_device_type: TouchDeviceType,
+    touch_device_type: TouchDeviceType,
+
+    /// Proxy to the device.
+    device_proxy: InputDeviceProxy,
 }
 
 #[async_trait]
@@ -170,6 +174,38 @@
     fn get_device_descriptor(&self) -> input_device::InputDeviceDescriptor {
         input_device::InputDeviceDescriptor::Touch(self.device_descriptor.clone())
     }
+
+    async fn handle_input_config_request(
+        &self,
+        request: &InputConfigFeaturesRequest,
+    ) -> Result<(), Error> {
+        match request {
+            InputConfigFeaturesRequest::SetTouchpadMode { enable, .. } => {
+                match self.touch_device_type {
+                    TouchDeviceType::TouchScreen => Ok(()),
+                    TouchDeviceType::WindowsPrecisionTouchpad => {
+                        // `get_feature_report` to only modify the input_mode and
+                        // keep other feature as is.
+                        let mut report = match self.device_proxy.get_feature_report().await? {
+                            Ok(report) => report,
+                            Err(e) => return Err(format_err!("get_feature_report failed: {}", e)),
+                        };
+                        let mut touch =
+                            report.touch.unwrap_or(fidl_input_report::TouchFeatureReport::EMPTY);
+                        touch.input_mode = match enable {
+                            true => Some(fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection),
+                            false => Some(fidl_input_report::TouchConfigurationInputMode::MouseCollection),
+                        };
+                        report.touch = Some(touch);
+                        self.device_proxy
+                            .set_feature_report(report)
+                            .await?
+                            .map_err(|e| format_err!("set_feature_report failed: {}", e))
+                    }
+                }
+            }
+        }
+    }
 }
 
 impl TouchBinding {
@@ -191,7 +227,7 @@
         input_event_sender: Sender<input_device::InputEvent>,
     ) -> Result<Self, Error> {
         let device_binding =
-            Self::bind_device(&device_proxy, device_id, input_event_sender).await?;
+            Self::bind_device(device_proxy.clone(), device_id, input_event_sender).await?;
         input_device::initialize_report_stream(
             device_proxy,
             device_binding.get_device_descriptor(),
@@ -213,12 +249,12 @@
     /// If the device descriptor could not be retrieved, or the descriptor could not be parsed
     /// correctly.
     async fn bind_device(
-        device: &InputDeviceProxy,
+        device_proxy: InputDeviceProxy,
         device_id: u32,
         input_event_sender: Sender<input_device::InputEvent>,
     ) -> Result<Self, Error> {
         let device_descriptor: fidl_fuchsia_input_report::DeviceDescriptor =
-            device.get_descriptor().await?;
+            device_proxy.get_descriptor().await?;
 
         match device_descriptor.touch {
             Some(fidl_fuchsia_input_report::TouchDescriptor {
@@ -241,7 +277,8 @@
                         .filter_map(Result::ok)
                         .collect(),
                 },
-                _touch_device_type: get_device_type(device).await,
+                touch_device_type: get_device_type(&device_proxy).await,
+                device_proxy,
             }),
             descriptor => Err(format_err!("Touch Descriptor failed to parse: \n {:?}", descriptor)),
         }
@@ -448,15 +485,21 @@
 mod tests {
     use {
         super::*,
+        crate::input_pipeline::InputDeviceBindingHashMap,
+        crate::input_pipeline::InputPipeline,
         crate::testing_utilities::{
             self, create_touch_contact, create_touch_event, create_touch_input_report,
         },
         crate::utils::Position,
         assert_matches::assert_matches,
+        fidl::endpoints::create_proxy_and_stream,
         fidl::endpoints::spawn_stream_handler,
+        fidl_fuchsia_ui_input_config::FeaturesMarker as InputConfigFeaturesMarker,
         fuchsia_async as fasync,
+        futures::lock::Mutex,
         futures::StreamExt,
         pretty_assertions::assert_eq,
+        std::sync::Arc,
         test_case::test_case,
     };
 
@@ -690,70 +733,7 @@
         let input_device_proxy = spawn_stream_handler(move |input_device_request| async move {
             match input_device_request {
                 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
-                    let _ = responder.send(fidl_input_report::DeviceDescriptor {
-                        mouse: match has_mouse_descriptor {
-                            true => Some(fidl_input_report::MouseDescriptor {
-                                input: Some(fidl_input_report::MouseInputDescriptor {
-                                    movement_x: None,
-                                    movement_y: None,
-                                    position_x: None,
-                                    position_y: None,
-                                    scroll_v: None,
-                                    scroll_h: None,
-                                    buttons: None,
-                                    ..fidl_input_report::MouseInputDescriptor::EMPTY
-                                }),
-                                ..fidl_input_report::MouseDescriptor::EMPTY
-                            }),
-                            false => None,
-                        },
-                        touch: Some(fidl_input_report::TouchDescriptor {
-                            input: Some(fidl_input_report::TouchInputDescriptor {
-                                contacts: Some(vec![fidl_input_report::ContactInputDescriptor {
-                                    position_x: Some(fidl_input_report::Axis {
-                                        range: fidl_input_report::Range { min: 1, max: 2 },
-                                        unit: fidl_input_report::Unit {
-                                            type_: fidl_input_report::UnitType::None,
-                                            exponent: 0,
-                                        },
-                                    }),
-                                    position_y: Some(fidl_input_report::Axis {
-                                        range: fidl_input_report::Range { min: 2, max: 3 },
-                                        unit: fidl_input_report::Unit {
-                                            type_: fidl_input_report::UnitType::Other,
-                                            exponent: 100000,
-                                        },
-                                    }),
-                                    pressure: Some(fidl_input_report::Axis {
-                                        range: fidl_input_report::Range { min: 3, max: 4 },
-                                        unit: fidl_input_report::Unit {
-                                            type_: fidl_input_report::UnitType::Grams,
-                                            exponent: -991,
-                                        },
-                                    }),
-                                    contact_width: Some(fidl_input_report::Axis {
-                                        range: fidl_input_report::Range { min: 5, max: 6 },
-                                        unit: fidl_input_report::Unit {
-                                            type_:
-                                                fidl_input_report::UnitType::EnglishAngularVelocity,
-                                            exponent: 123,
-                                        },
-                                    }),
-                                    contact_height: Some(fidl_input_report::Axis {
-                                        range: fidl_input_report::Range { min: 7, max: 8 },
-                                        unit: fidl_input_report::Unit {
-                                            type_: fidl_input_report::UnitType::Pascals,
-                                            exponent: 100,
-                                        },
-                                    }),
-                                    ..fidl_input_report::ContactInputDescriptor::EMPTY
-                                }]),
-                                ..fidl_input_report::TouchInputDescriptor::EMPTY
-                            }),
-                            ..fidl_input_report::TouchDescriptor::EMPTY
-                        }),
-                        ..fidl_input_report::DeviceDescriptor::EMPTY
-                    });
+                    let _ = responder.send(get_touchpad_device_descriptor(has_mouse_descriptor));
                 }
                 fidl_input_report::InputDeviceRequest::GetFeatureReport { responder } => {
                     let _ = responder.send(&mut Ok(fidl_input_report::FeatureReport {
@@ -773,6 +753,152 @@
             futures::channel::mpsc::channel(input_device::INPUT_EVENT_BUFFER_SIZE);
 
         let binding = TouchBinding::new(input_device_proxy, 0, device_event_sender).await.unwrap();
-        pretty_assertions::assert_eq!(binding._touch_device_type, expect_touch_device_type);
+        pretty_assertions::assert_eq!(binding.touch_device_type, expect_touch_device_type);
+    }
+
+    #[test_case(false, fidl_input_report::TouchConfigurationInputMode::MouseCollection; "mouse mode")]
+    #[test_case(true, fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection; "touchpad mode")]
+    #[fasync::run_singlethreaded(test)]
+    async fn handle_input_config_request(
+        enable_touchpad_mode: bool,
+        want_touch_configuration_input_mode: fidl_input_report::TouchConfigurationInputMode,
+    ) {
+        let (set_feature_report_sender, mut set_feature_report_receiver) =
+            futures::channel::mpsc::channel::<fidl_input_report::FeatureReport>(1);
+        let input_device_proxy = spawn_stream_handler(move |input_device_request| {
+            let mut set_feature_report_sender = set_feature_report_sender.clone();
+            async move {
+                match input_device_request {
+                    fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
+                        let _ = responder.send(get_touchpad_device_descriptor(true));
+                    }
+                    fidl_input_report::InputDeviceRequest::GetFeatureReport { responder } => {
+                        let _ = responder.send(&mut Ok(fidl_input_report::FeatureReport {
+                            touch: Some(fidl_input_report::TouchFeatureReport {
+                                input_mode: Some(
+                                    fidl_input_report::TouchConfigurationInputMode::MouseCollection,
+                                ),
+                                ..fidl_input_report::TouchFeatureReport::EMPTY
+                            }),
+                            ..fidl_input_report::FeatureReport::EMPTY
+                        }));
+                    }
+                    fidl_input_report::InputDeviceRequest::SetFeatureReport {
+                        report,
+                        responder,
+                    } => {
+                        match set_feature_report_sender.try_send(report) {
+                            Ok(_) => {
+                                let _ = responder.send(&mut Ok(()));
+                            }
+                            Err(e) => {
+                                panic!(
+                                    "try_send set_feature_report_request failed: {}",
+                                    e.to_string()
+                                );
+                            }
+                        };
+                    }
+                    fidl_input_report::InputDeviceRequest::GetInputReportsReader { .. } => {
+                        // Do not panic as `initialize_report_stream()` will call this protocol.
+                    }
+                    _ => panic!("InputDevice handler received an unexpected request"),
+                }
+            }
+        })
+        .unwrap();
+
+        let (device_event_sender, _device_event_receiver) =
+            futures::channel::mpsc::channel(input_device::INPUT_EVENT_BUFFER_SIZE);
+
+        let binding = TouchBinding::new(input_device_proxy, 0, device_event_sender).await.unwrap();
+        let bindings: InputDeviceBindingHashMap = Arc::new(Mutex::new(HashMap::new()));
+        bindings.lock().await.insert(1, vec![Box::new(binding)]);
+
+        let bindings_clone = bindings.clone();
+
+        let (input_config_features_proxy, input_config_features_request_stream) =
+            create_proxy_and_stream::<InputConfigFeaturesMarker>().unwrap();
+        input_config_features_proxy
+            .set_touchpad_mode(enable_touchpad_mode)
+            .expect("set_touchpad_mode");
+
+        // Drop proxy to terminate request stream.
+        std::mem::drop(input_config_features_proxy);
+
+        InputPipeline::handle_input_config_request_stream(
+            input_config_features_request_stream,
+            &bindings_clone,
+        )
+        .await
+        .expect("handle_input_config_request_stream");
+
+        let got_feature_report = set_feature_report_receiver.next().await;
+        let want_feature_report = Some(fidl_input_report::FeatureReport {
+            touch: Some(fidl_input_report::TouchFeatureReport {
+                input_mode: Some(want_touch_configuration_input_mode),
+                ..fidl_input_report::TouchFeatureReport::EMPTY
+            }),
+            ..fidl_input_report::FeatureReport::EMPTY
+        });
+        pretty_assertions::assert_eq!(want_feature_report, got_feature_report);
+    }
+
+    /// Returns an |fidl_fuchsia_input_report::DeviceDescriptor| for
+    /// touchpad related tests.
+    fn get_touchpad_device_descriptor(
+        has_mouse_descriptor: bool,
+    ) -> fidl_fuchsia_input_report::DeviceDescriptor {
+        fidl_input_report::DeviceDescriptor {
+            mouse: match has_mouse_descriptor {
+                true => Some(fidl_input_report::MouseDescriptor::EMPTY),
+                false => None,
+            },
+            touch: Some(fidl_input_report::TouchDescriptor {
+                input: Some(fidl_input_report::TouchInputDescriptor {
+                    contacts: Some(vec![fidl_input_report::ContactInputDescriptor {
+                        position_x: Some(fidl_input_report::Axis {
+                            range: fidl_input_report::Range { min: 1, max: 2 },
+                            unit: fidl_input_report::Unit {
+                                type_: fidl_input_report::UnitType::None,
+                                exponent: 0,
+                            },
+                        }),
+                        position_y: Some(fidl_input_report::Axis {
+                            range: fidl_input_report::Range { min: 2, max: 3 },
+                            unit: fidl_input_report::Unit {
+                                type_: fidl_input_report::UnitType::Other,
+                                exponent: 100000,
+                            },
+                        }),
+                        pressure: Some(fidl_input_report::Axis {
+                            range: fidl_input_report::Range { min: 3, max: 4 },
+                            unit: fidl_input_report::Unit {
+                                type_: fidl_input_report::UnitType::Grams,
+                                exponent: -991,
+                            },
+                        }),
+                        contact_width: Some(fidl_input_report::Axis {
+                            range: fidl_input_report::Range { min: 5, max: 6 },
+                            unit: fidl_input_report::Unit {
+                                type_: fidl_input_report::UnitType::EnglishAngularVelocity,
+                                exponent: 123,
+                            },
+                        }),
+                        contact_height: Some(fidl_input_report::Axis {
+                            range: fidl_input_report::Range { min: 7, max: 8 },
+                            unit: fidl_input_report::Unit {
+                                type_: fidl_input_report::UnitType::Pascals,
+                                exponent: 100,
+                            },
+                        }),
+                        ..fidl_input_report::ContactInputDescriptor::EMPTY
+                    }]),
+                    ..fidl_input_report::TouchInputDescriptor::EMPTY
+                }),
+                ..fidl_input_report::TouchDescriptor::EMPTY
+            }),
+            ..fidl_input_report::DeviceDescriptor::EMPTY
+        }
     }
 }