blob: 96a4a3f900ac2ea1bab0632aebdfeef61c6cf8a5 [file] [log] [blame]
// Copyright 2020 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.
#![recursion_limit = "512"]
use {
anyhow::Error,
fidl::endpoints::{create_proxy, Proxy, RequestStream, ServiceMarker},
fidl_fuchsia_hardware_input as finput,
fidl_fuchsia_hardware_power_statecontrol as statecontrol,
fidl_fuchsia_io::{
DirectoryProxy, MODE_TYPE_DIRECTORY, OPEN_RIGHT_READABLE, OPEN_RIGHT_WRITABLE,
},
fidl_fuchsia_test_pwrbtn as test_pwrbtn, fuchsia_async as fasync,
fuchsia_component::server as fserver,
fuchsia_syslog::{self as syslog, macros::*},
fuchsia_zircon::{self as zx, AsHandleRef, HandleBased},
futures::{channel::oneshot, StreamExt, TryFutureExt, TryStreamExt},
vfs::{
directory::entry::DirectoryEntry, execution_scope::ExecutionScope, path::Path as pfsPath,
pseudo_directory, service,
},
};
fn event_handle_rights() -> zx::Rights {
zx::Rights::BASIC | zx::Rights::SIGNAL
}
#[fasync::run_singlethreaded()]
async fn main() -> Result<(), Error> {
syslog::init_with_tags(&["pwrbtn-monitor-mock-services"])?;
fx_log_info!("started");
let (send_test_result, recv_test_result) = oneshot::channel();
let mut recv_test_result = Some(recv_test_result);
let mut send_test_result = Some(send_test_result);
let event = zx::Event::create()?;
let event_for_test_protocol = event.duplicate_handle(event_handle_rights())?;
let mut fs = fserver::ServiceFs::new();
fs.dir("svc").add_fidl_service(move |mut stream: test_pwrbtn::TestsRequestStream| {
let recv_test_result = recv_test_result.take();
fasync::Task::spawn(
async move {
fx_log_info!("new connection to {}", test_pwrbtn::TestsMarker::NAME);
match stream.try_next().await? {
Some(test_pwrbtn::TestsRequest::Run { responder }) => {
if let Some(recv_test_result) = recv_test_result {
let _ = recv_test_result.await;
responder.send()?;
} else {
panic!("Get already called");
}
}
_ => (),
}
Ok(())
}
.unwrap_or_else(|e: anyhow::Error| {
panic!("couldn't run fuchsia.test.pwrbtn.Tests: {:?}", e);
}),
)
.detach();
});
fs.dir("svc").add_fidl_service(move |mut stream: statecontrol::AdminRequestStream| {
let send_test_result = send_test_result.take();
let event_for_test_protocol = event_for_test_protocol
.duplicate_handle(event_handle_rights())
.expect("failed to clone event");
fasync::Task::spawn(
async move {
fx_log_info!("new connection to {}", statecontrol::AdminMarker::NAME);
match stream.try_next().await? {
Some(statecontrol::AdminRequest::Poweroff { responder }) => {
// If we respond to pwrbtn-monitor it will go back to check the signals
// on the event we gave it, see that ZX_USER_SIGNAL_0 is still set, and
// call this again, and thus be stuck in a loop until the test is torn
// down. This isn't useful, and generates quite a bit of log noise at
// the end of the test. To avoid this, we need to clear the signal on
// the event.
event_for_test_protocol
.signal_handle(zx::Signals::USER_0, zx::Signals::NONE)?;
responder.send(&mut Ok(()))?;
if let Some(send_test_result) = send_test_result {
send_test_result.send(()).expect("failed to send test completion");
}
}
_ => {
panic!("only expecting calls to Poweroff");
}
}
Ok(())
}
.unwrap_or_else(|e: anyhow::Error| {
panic!("couldn't run fuchsia.power.hardware.statecontrol.Admin: {:?}", e);
}),
)
.detach();
});
// A pseudo_directory must be used here because a ServiceFs does not support portions of
// fuchsia.io required by `fdio_watch_directory`, which pwrbtn-monitor uses on this directory.
let input_dir = pseudo_directory! {
"mock_input_device" => service::endpoint(move |_, channel| {
let event = event.duplicate_handle(event_handle_rights()).expect("failed to clone event");
fasync::Task::spawn(
async move {
fx_log_info!("new connection to the mock input device");
let mut stream = finput::DeviceRequestStream::from_channel(channel);
while let Some(req) = stream.try_next().await? {
match req {
finput::DeviceRequest::GetReportDesc { responder } => {
fx_log_info!("sending report desc");
// The following sequence of numbers is a HID report containing a
// power button press. This is used to convince pwrbtn-monitor that
// the power button has been pressed, and that it should begin a
// system power off.
responder.send(&[
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x80, // Usage (Sys Control)
0xA1, 0x01, // Collection (Application)
0x09, 0x81, // Usage (Sys Power Down)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x08, // Report Size (8)
0x95, 0x01, // Report Count (1)
0x81,
0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
])?;
}
finput::DeviceRequest::GetReportsEvent { responder } => {
fx_log_info!("sending reports event and signalling the event");
let event_dup = event.duplicate_handle(event_handle_rights())?;
responder.send(zx::Status::OK.into_raw(), event_dup)?;
event.signal_handle(zx::Signals::NONE, zx::Signals::USER_0)?;
}
finput::DeviceRequest::ReadReport { responder } => {
fx_log_info!("sending report");
let msg = &[1]; // 1 means "power off", 0 would mean "don't power off"
responder.send(
zx::Status::OK.into_raw(),
msg,
zx::Time::get_monotonic().into_nanos(),
)?;
}
_ => panic!("unexpected call to fuchsia.hardware.input.Device"),
}
}
Ok(())
}
.unwrap_or_else(|e: anyhow::Error| {
panic!("couldn't run fuchsia.hardware.input.Device: {:?}", e);
})
).detach()
}),
};
let (proxy, server_end) = create_proxy()?;
input_dir.clone().open(
ExecutionScope::new(),
OPEN_RIGHT_READABLE | OPEN_RIGHT_WRITABLE,
MODE_TYPE_DIRECTORY,
pfsPath::empty(),
server_end,
);
fs.add_remote("input", DirectoryProxy::from_channel(proxy.into_channel().unwrap()));
fs.take_and_serve_directory_handle()?;
fs.collect::<()>().await;
Ok(())
}