[tel] AT Command transport
Add support for a simple queue for AT messages to the
modem.
Test:
$ fx set core.x64 --with-base bundles:tools,//src/connectivity/telephony,//src/connectivity/telephony:tests
$ fx qemu -kN
$ runtests -t tel_fake_at_query
Change-Id: I441551e78327ffd6df183acabb3af15573076be9
diff --git a/garnet/packages/prod/BUILD.gn b/garnet/packages/prod/BUILD.gn
index 09734a9..3309368 100644
--- a/garnet/packages/prod/BUILD.gn
+++ b/garnet/packages/prod/BUILD.gn
@@ -303,6 +303,8 @@
public_deps = [
"//src/connectivity/telephony/drivers/qmi-fake-transport",
"//src/connectivity/telephony/drivers/qmi-usb-transport",
+ "//src/connectivity/telephony/ril-at",
+ "//src/connectivity/telephony/ril-at:ril-at-tests",
"//src/connectivity/telephony/ril-qmi",
"//src/connectivity/telephony/ril-qmi:ril-qmi-tests",
"//src/connectivity/telephony/telephony",
diff --git a/sdk/fidl/fuchsia.telephony.ril/ril.fidl b/sdk/fidl/fuchsia.telephony.ril/ril.fidl
index 3d2488e..5aab3ac 100644
--- a/sdk/fidl/fuchsia.telephony.ril/ril.fidl
+++ b/sdk/fidl/fuchsia.telephony.ril/ril.fidl
@@ -71,4 +71,7 @@
/// Signal Strength (dBm) for LTE networks (total received wideband power).
GetSignalStrength() -> (float32 dbm) error RilError;
+
+ /// Raw string to send to modem for development.
+ RawCommand(string command) -> (string result) error RilError;
};
diff --git a/src/connectivity/telephony/BUILD.gn b/src/connectivity/telephony/BUILD.gn
index 2456bb9..e801eec 100644
--- a/src/connectivity/telephony/BUILD.gn
+++ b/src/connectivity/telephony/BUILD.gn
@@ -7,6 +7,7 @@
public_deps = [
":tests",
"//src/connectivity/telephony/config",
+ "//src/connectivity/telephony/ril-at",
"//src/connectivity/telephony/ril-qmi",
"//src/connectivity/telephony/telephony",
"//src/connectivity/telephony/tools",
diff --git a/src/connectivity/telephony/ril-at/BUILD.gn b/src/connectivity/telephony/ril-at/BUILD.gn
new file mode 100644
index 0000000..81898b3
--- /dev/null
+++ b/src/connectivity/telephony/ril-at/BUILD.gn
@@ -0,0 +1,56 @@
+# Copyright 2018 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.
+
+import("//build/package.gni")
+import("//build/rust/rustc_binary.gni")
+import("//build/test/test_package.gni")
+import("//build/testing/environments.gni")
+
+rustc_binary("bin") {
+ name = "ril_at"
+ edition = "2018"
+ with_unit_tests = true
+
+ deps = [
+ "//sdk/fidl/fuchsia.telephony.ril:fuchsia.telephony.ril-rustc",
+ "//src/connectivity/telephony/lib/qmi-protocol",
+ "//src/lib/fidl/rust/fidl",
+ "//src/lib/fuchsia-async",
+ "//src/lib/fuchsia-component",
+ "//src/lib/syslog/rust:syslog",
+ "//src/lib/zircon/rust:fuchsia-zircon",
+ "//third_party/rust_crates:anyhow",
+ "//third_party/rust_crates:bytes",
+ "//third_party/rust_crates:futures",
+ "//third_party/rust_crates:log",
+ "//third_party/rust_crates:parking_lot",
+ ]
+}
+
+package("ril-at") {
+ deps = [ ":bin" ]
+
+ binary = "ril_at"
+
+ meta = [
+ {
+ path = rebase_path("meta/ril-at.cmx")
+ dest = "ril-at.cmx"
+ },
+ ]
+}
+
+unittest_package("ril-at-tests") {
+ package_name = "ril-at-tests"
+
+ deps = [ ":bin_test" ]
+
+ tests = [
+ {
+ name = "ril_at_bin_test"
+ dest = "ril-at-tests"
+ environments = basic_envs
+ },
+ ]
+}
diff --git a/src/connectivity/telephony/ril-at/meta/ril-at.cmx b/src/connectivity/telephony/ril-at/meta/ril-at.cmx
new file mode 100644
index 0000000..2015a8d
--- /dev/null
+++ b/src/connectivity/telephony/ril-at/meta/ril-at.cmx
@@ -0,0 +1,10 @@
+{
+ "program": {
+ "binary": "bin/app"
+ },
+ "sandbox": {
+ "services": [
+ "fuchsia.logger.LogSink"
+ ]
+ }
+}
diff --git a/src/connectivity/telephony/ril-at/src/main.rs b/src/connectivity/telephony/ril-at/src/main.rs
new file mode 100644
index 0000000..5e6a5e0
--- /dev/null
+++ b/src/connectivity/telephony/ril-at/src/main.rs
@@ -0,0 +1,162 @@
+// 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.
+
+use crate::transport::AtTransport;
+use anyhow::Error;
+use fidl_fuchsia_telephony_ril::*;
+use fuchsia_async as fasync;
+use fuchsia_component::server::ServiceFs;
+use fuchsia_syslog::{self as syslog, macros::*};
+use fuchsia_zircon as zx;
+use futures::{
+ future::Future,
+ lock::Mutex,
+ task::{AtomicWaker, Context},
+ StreamExt, TryFutureExt, TryStreamExt,
+};
+use std::{pin::Pin, sync::Arc, task::Poll};
+
+mod transport;
+
+pub struct AtModem {
+ transport: Option<AtTransport>,
+ waker: AtomicWaker,
+}
+
+impl AtModem {
+ pub fn new() -> Self {
+ AtModem { transport: None, waker: AtomicWaker::new() }
+ }
+
+ async fn fidl_service_async(modem: Arc<Mutex<AtModem>>, mut stream: SetupRequestStream) -> () {
+ let mut modem = modem.lock().await;
+ let res = stream.next().await;
+ if let Some(Ok(SetupRequest::ConnectTransport { channel, responder })) = res {
+ let status = modem.connect_transport(channel);
+ fx_log_info!("Connecting the service to the transport driver: {}", status);
+ if status {
+ let _result = responder.send(&mut Ok(()));
+ } else {
+ let _result = responder.send(&mut Err(RilError::TransportError));
+ }
+ }
+ }
+
+ pub fn fidl_service(modem: Arc<Mutex<AtModem>>) -> impl Fn(SetupRequestStream) -> () {
+ move |stream| fasync::spawn(AtModem::fidl_service_async(modem.clone(), stream))
+ }
+
+ fn connect_transport(&mut self, chan: zx::Channel) -> bool {
+ fx_log_info!("Connecting the transport");
+ match fasync::Channel::from_channel(chan) {
+ Ok(chan) => {
+ if chan.is_closed() {
+ fx_log_err!("The transport channel is not open");
+ return false;
+ }
+ self.set_transport(AtTransport::new(chan));
+ true
+ }
+ Err(_) => {
+ fx_log_err!("Failed to convert a zircon channel to a fasync one");
+ false
+ }
+ }
+ }
+
+ fn set_transport(&mut self, transport: AtTransport) {
+ self.transport = Some(transport);
+ self.waker.wake();
+ }
+}
+
+//Wait for transport setup.
+impl<'modem> Future for &AtModem {
+ type Output = ();
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ match self.transport {
+ Some(_) => Poll::Ready(()),
+ None => {
+ self.waker.register(cx.waker());
+ Poll::Pending
+ }
+ }
+ }
+}
+
+/// Craft a AT Query given a handle and a client connection. Handles common error paths.
+/// For more specialized interactions with the modem, prefer to call `client.send_msg()` directly.
+macro_rules! at_query {
+ ($responder:expr, $transport:expr, $query:expr) => {{
+ match $transport.send_msg($query).await {
+ Ok(at) => at,
+ Err(e) => {
+ fx_log_err!("Transport Error: {:?}", e);
+ return $responder.send(&mut Err(RilError::TransportError));
+ }
+ }
+ }};
+}
+
+struct FrilService;
+impl FrilService {
+ pub fn fidl_service(
+ modem: Arc<Mutex<AtModem>>,
+ ) -> impl Fn(RadioInterfaceLayerRequestStream) -> () {
+ move |stream: RadioInterfaceLayerRequestStream| {
+ fx_log_info!("New client connecting to the Fuchsia RIL");
+ fasync::spawn(FrilService::fidl_service_async(modem.clone(), stream))
+ }
+ }
+
+ async fn fidl_service_async(
+ modem: Arc<Mutex<AtModem>>,
+ stream: RadioInterfaceLayerRequestStream,
+ ) {
+ stream
+ .try_for_each(move |req| Self::handle_request(modem.clone(), req))
+ .unwrap_or_else(|e| fx_log_err!("Error running {:?}", e))
+ .await
+ }
+
+ async fn handle_request(
+ modem: Arc<Mutex<AtModem>>,
+ request: RadioInterfaceLayerRequest,
+ ) -> Result<(), fidl::Error> {
+ let mut modem = modem.lock().await;
+ (&(*modem)).await; // Await transport setup.
+ // This await is ok since the transport has been set up.
+ let transport = (&mut modem.transport).as_mut().unwrap();
+ match request {
+ RadioInterfaceLayerRequest::RawCommand { command, responder } => {
+ let resp = at_query!(responder, transport, command);
+
+ responder.send(&mut Ok(resp))?;
+ }
+ // Translate requests here
+ _ => (),
+ }
+ Ok(())
+ }
+}
+
+#[fasync::run_singlethreaded]
+async fn main() -> Result<(), Error> {
+ syslog::init_with_tags(&["ril-at"]).expect("Can't init logger");
+ fx_log_info!("Starting ril-at...");
+
+ let modem = Arc::new(Mutex::new(AtModem::new()));
+
+ let mut fs = ServiceFs::new_local();
+ let mut dir = fs.dir("svc");
+
+ // Add connection to upstream clients.
+ dir.add_fidl_service(FrilService::fidl_service(modem.clone()));
+ // Add connection to downstream driver.
+ dir.add_fidl_service(AtModem::fidl_service(modem.clone()));
+
+ fs.take_and_serve_directory_handle()?;
+ Ok(fs.collect::<()>().await)
+}
diff --git a/src/connectivity/telephony/ril-at/src/transport.rs b/src/connectivity/telephony/ril-at/src/transport.rs
new file mode 100644
index 0000000..824b76e
--- /dev/null
+++ b/src/connectivity/telephony/ril-at/src/transport.rs
@@ -0,0 +1,123 @@
+// Copyright 2018 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 fuchsia_async as fasync;
+use fuchsia_zircon as zx;
+use futures::lock::Mutex;
+use std::{collections::VecDeque, str::Utf8Error, sync::Arc};
+
+#[derive(Clone, Debug)]
+pub enum AtTransportError {
+ ChannelClosed,
+ ClientRead { error: zx::Status },
+ ClientWrite { error: zx::Status },
+ Deserialize { bytes: Vec<u8>, error: Utf8Error },
+}
+
+struct AtMessage {
+ request: String,
+ response: Option<Result<String, AtTransportError>>,
+}
+
+impl AtMessage {
+ fn new(request: String) -> Arc<Mutex<Self>> {
+ Arc::new(Mutex::new(AtMessage { request, response: None }))
+ }
+}
+
+pub struct AtTransport {
+ messages: Mutex<VecDeque<Arc<Mutex<AtMessage>>>>,
+ transport_channel: Mutex<fasync::Channel>,
+}
+
+impl AtTransport {
+ pub fn new(chan: fasync::Channel) -> Self {
+ AtTransport { transport_channel: Mutex::new(chan), messages: Mutex::new(VecDeque::new()) }
+ }
+
+ pub async fn send_msg(&mut self, string_msg: String) -> Result<String, AtTransportError> {
+ let msg = self.enqueue_message(string_msg).await;
+ self.do_sends_and_recvs().await;
+
+ let msg = msg.lock().await;
+ msg.response.clone().unwrap() // Unwraps the option set in do_sends_and_recvs
+ }
+
+ async fn enqueue_message(&self, string_msg: String) -> Arc<Mutex<AtMessage>> {
+ let msg = AtMessage::new(string_msg);
+
+ let messages = &mut self.messages.lock().await;
+ messages.push_back(msg.clone());
+
+ msg
+ }
+
+ async fn do_sends_and_recvs(&mut self) -> () {
+ // Serialize message send/recvs.
+ let transport_channel = self.transport_channel.lock().await;
+
+ 'messages: loop {
+ let message = {
+ let mut messages = self.messages.lock().await;
+ match messages.pop_front() {
+ None => break 'messages,
+ Some(m) => m,
+ }
+ };
+ let mut message = message.lock().await;
+
+ let response = self.send_one(&transport_channel, &message.request).await;
+ message.response = Some(response);
+ }
+ }
+
+ async fn send_one(
+ &self,
+ transport: &fasync::Channel,
+ request: &String,
+ ) -> Result<String, AtTransportError> {
+ let request_bytes = Self::serialize_request(request);
+
+ transport
+ .write(&request_bytes, /* no handles */ &mut vec![])
+ .map_err(|error| AtTransportError::ClientWrite { error })?;
+
+ // Loop over unsolicited events until we get a response or an error.
+ loop {
+ let response_buf = Self::recv(transport).await?;
+ let response = Self::deserialize_response(response_buf.bytes().to_vec())?;
+
+ if Self::is_unsolicited_event(&response) {
+ // TODO Handle these
+ } else {
+ return Ok(response);
+ }
+ }
+ }
+
+ fn serialize_request(request: &String) -> Vec<u8> {
+ request.as_bytes().to_vec()
+ }
+
+ fn deserialize_response(response_bytes: Vec<u8>) -> Result<String, AtTransportError> {
+ String::from_utf8(response_bytes).map_err(|error| AtTransportError::Deserialize {
+ bytes: error.as_bytes().to_vec(),
+ error: error.utf8_error(),
+ })
+ }
+
+ fn is_unsolicited_event(_response: &String) -> bool {
+ // TODO Fix this.
+ false
+ }
+
+ async fn recv(transport_channel: &fasync::Channel) -> Result<zx::MessageBuf, AtTransportError> {
+ let mut response_buf = zx::MessageBuf::new();
+ match transport_channel.recv_msg(&mut response_buf).await {
+ Ok(()) => return Ok(response_buf),
+ Err(zx::Status::PEER_CLOSED) => return Err(AtTransportError::ChannelClosed),
+ Err(error) => return Err(AtTransportError::ClientRead { error }),
+ }
+ }
+}
diff --git a/src/connectivity/telephony/ril-qmi/src/main.rs b/src/connectivity/telephony/ril-qmi/src/main.rs
index 874f724..2ad62a7 100644
--- a/src/connectivity/telephony/ril-qmi/src/main.rs
+++ b/src/connectivity/telephony/ril-qmi/src/main.rs
@@ -187,6 +187,9 @@
responder.send(&mut Ok(RadioPowerState::Off))?
}
}
+ RadioInterfaceLayerRequest::RawCommand { command: _command, responder } => {
+ responder.send(&mut Err(RilError::UnknownError))?
+ }
}
Ok(())
}
diff --git a/src/connectivity/telephony/tests/BUILD.gn b/src/connectivity/telephony/tests/BUILD.gn
index 5bd2a3c..6bdd3f3 100644
--- a/src/connectivity/telephony/tests/BUILD.gn
+++ b/src/connectivity/telephony/tests/BUILD.gn
@@ -15,6 +15,7 @@
public_deps = [
":devmgr-manifest",
"component-integration:fake-at-driver-test",
+ "component-integration:fake-at-query",
"component-integration:fake-qmi-query",
"component-integration:snooper",
"driver-integration:telephony-qmi-usb-integration-test",
@@ -37,6 +38,10 @@
environments = basic_envs
},
{
+ name = "tel_fake_at_query"
+ environments = basic_envs
+ },
+ {
name = "tel_fake_at_driver_test"
environments = basic_envs
},
diff --git a/src/connectivity/telephony/tests/component-integration/BUILD.gn b/src/connectivity/telephony/tests/component-integration/BUILD.gn
index a5564bd..47e19f4 100644
--- a/src/connectivity/telephony/tests/component-integration/BUILD.gn
+++ b/src/connectivity/telephony/tests/component-integration/BUILD.gn
@@ -12,6 +12,10 @@
public_deps = [ "fake-qmi-query" ]
}
+group("fake-at-query") {
+ testonly = true
+ public_deps = [ "fake-at-query" ]
+}
group("snooper") {
testonly = true
public_deps = [ "snooper:multi-clients" ]
diff --git a/src/connectivity/telephony/tests/component-integration/fake-at-query/BUILD.gn b/src/connectivity/telephony/tests/component-integration/fake-at-query/BUILD.gn
new file mode 100644
index 0000000..2bd48f7
--- /dev/null
+++ b/src/connectivity/telephony/tests/component-integration/fake-at-query/BUILD.gn
@@ -0,0 +1,31 @@
+# Copyright 2019 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.
+
+import("//build/rust/rustc_test.gni")
+
+rustc_test("fake-at-query") {
+ name = "tel_fake_at_query"
+ edition = "2018"
+ deps = [
+ "//garnet/lib/rust/files_async",
+ "//sdk/fidl/fuchsia.telephony.ril:fuchsia.telephony.ril-rustc",
+ "//src/connectivity/telephony/lib/qmi",
+ "//src/connectivity/telephony/lib/tel-devmgr/fidl:devmgr-rustc",
+ "//src/connectivity/telephony/tests/tel-dev:tel_dev",
+ "//src/lib/fdio/rust:fdio",
+ "//src/lib/fidl/rust/fidl",
+ "//src/lib/fuchsia-async",
+ "//src/lib/fuchsia-component",
+ "//src/lib/storage/fuchsia-vfs-watcher",
+ "//src/lib/syslog/rust:syslog",
+ "//src/lib/zircon/rust:fuchsia-zircon",
+ "//third_party/rust_crates:anyhow",
+ "//third_party/rust_crates:futures",
+ "//third_party/rust_crates:pin-utils",
+ "//third_party/rust_crates:thiserror",
+ "//zircon/system/fidl/fuchsia-device:fuchsia-device-rustc",
+ "//zircon/system/fidl/fuchsia-io:fuchsia-io-rustc",
+ ]
+ non_rust_deps = [ "//zircon/public/lib/syslog" ]
+}
diff --git a/src/connectivity/telephony/tests/component-integration/fake-at-query/src/lib.rs b/src/connectivity/telephony/tests/component-integration/fake-at-query/src/lib.rs
new file mode 100644
index 0000000..1e9b8bf
--- /dev/null
+++ b/src/connectivity/telephony/tests/component-integration/fake-at-query/src/lib.rs
@@ -0,0 +1,87 @@
+// Copyright 2019 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::{Context as _, Error},
+ fidl_fuchsia_telephony_ril::{RadioInterfaceLayerMarker, SetupMarker},
+ fuchsia_component::{
+ client::{launch, launcher},
+ fuchsia_single_component_package_url,
+ },
+ fuchsia_syslog::{self as syslog, macros::*},
+ futures::future::{join_all, Future},
+ futures::lock::Mutex,
+ qmi as tel_ctl, //Todo: rename qmi module
+ std::pin::Pin,
+ std::sync::Arc,
+ tel_dev::component_test::*,
+};
+
+// Send this command to attempt to make a call on the test driver and fail.
+const AT_CMD_REQ_ATD_STR: &str = "ATD\r";
+const AT_CMD_RESP_NO_CARRIER_STR: &str = "NO CARRIER\r";
+
+// Send this string to request the test driver sends back an invalid text string.
+const AT_CMD_REQ_INVALID_UNICODE_STR: &str = "INVALID UNICODE";
+
+const RIL_URL: &str = fuchsia_single_component_package_url!("ril-at");
+
+// TODO(kehrt) Split this into multiple tests. Unfortunately, test setup and teardown multiple
+// times causes a variety of errors I don't understand yet.
+#[fuchsia_async::run_singlethreaded(test)]
+async fn at_one_query_test() -> Result<(), Error> {
+ syslog::init_with_tags(&["at-query-test"]).expect("Can't init logger");
+
+ //Setup
+ const TEL_PATH: &str = "class/at-transport";
+ let found_device =
+ get_fake_device_in_isolated_devmgr(TEL_PATH).await.expect("getting fake device");
+ let launcher = launcher().context("Failed to open launcher service")?;
+ let chan = tel_ctl::connect_transport_device(&found_device).await?;
+ let app =
+ launch(&launcher, RIL_URL.to_string(), None).context("Failed to launch ril-at service")?;
+ let ril_modem_setup = app.connect_to_service::<SetupMarker>()?;
+ ril_modem_setup.connect_transport(chan).await?.expect("make sure telephony svc is running");
+ let ril_modem = app.connect_to_service::<RadioInterfaceLayerMarker>()?;
+
+ // Test sending and receiving one message.
+ fx_log_err!("sending ATD");
+ let resp = ril_modem.raw_command(&AT_CMD_REQ_ATD_STR).await?.expect("error sending get info");
+ assert_eq!(resp, AT_CMD_RESP_NO_CARRIER_STR);
+ fx_log_err!("received and verified responses");
+
+ // Test sending a message that causes a parse error for the response.
+ fx_log_err!("sending a request which expects an error response");
+ let resp = ril_modem
+ .raw_command(&AT_CMD_REQ_INVALID_UNICODE_STR)
+ .await
+ .expect("error sending get info");
+ assert!(resp.is_err());
+ fx_log_err!("received and verified responses");
+
+ // Test interleaving a bunch of sends and receives. let ril_modem_arc = Arc::new(Mutex::new(ril_modem));
+ let ril_modem_arc = Arc::new(Mutex::new(ril_modem));
+ let mut vec: Vec<Pin<Box<dyn Future<Output = (i32, String)>>>> = Vec::new();
+ for x in 0..100 {
+ let ril_modem_arc = ril_modem_arc.clone();
+ vec.push(Box::pin(async move {
+ let send_string = format!("RACE{:}", x);
+ let ril_modem_arc = ril_modem_arc.lock().await;
+ let recv_result = ril_modem_arc.raw_command(&send_string).await;
+ (x, recv_result.unwrap().unwrap())
+ }))
+ }
+
+ let joined = join_all(vec).await;
+
+ for (x, recv_result) in joined.into_iter() {
+ let recv_string = format!("ECAR{:}", x);
+ assert!(recv_string == recv_result);
+ }
+ // Tear down
+ unbind_fake_device(&found_device)?;
+ fx_log_err!("unbinded device");
+ validate_removal_of_fake_device(TEL_PATH).await.expect("validate removal of device");
+ Ok(())
+}
diff --git a/src/connectivity/telephony/tests/component-integration/fake-qmi-query/src/lib.rs b/src/connectivity/telephony/tests/component-integration/fake-qmi-query/src/lib.rs
index 3dfc73f..04bb78b 100644
--- a/src/connectivity/telephony/tests/component-integration/fake-qmi-query/src/lib.rs
+++ b/src/connectivity/telephony/tests/component-integration/fake-qmi-query/src/lib.rs
@@ -4,7 +4,6 @@
use {
anyhow::{Context as _, Error},
- fidl_fuchsia_io,
fidl_fuchsia_telephony_ril::{RadioInterfaceLayerMarker, SetupMarker},
fuchsia_component::{
client::{launch, launcher},
@@ -12,8 +11,7 @@
},
fuchsia_syslog::{self as syslog, macros::*},
qmi,
- std::path::Path,
- tel_dev::{component_test::*, isolated_devmgr},
+ tel_dev::component_test::*,
};
const RIL_URL: &str = fuchsia_single_component_package_url!("ril-qmi");
@@ -23,17 +21,8 @@
async fn qmi_query_test() -> Result<(), Error> {
syslog::init_with_tags(&["qmi-query-test"]).expect("Can't init logger");
const TEL_PATH: &str = "class/qmi-transport";
- let tel_dir =
- isolated_devmgr::open_dir_in_isolated_devmgr(TEL_PATH).expect("opening qmi-transport dir");
- let directory_proxy = fidl_fuchsia_io::DirectoryProxy::new(
- fuchsia_async::Channel::from_channel(fdio::clone_channel(&tel_dir)?)?,
- );
- let tel_devices = files_async::readdir(&directory_proxy).await?;
- let last_device: &files_async::DirEntry = tel_devices.last().expect("no device found");
- let found_device_path = Box::new(Path::new(TEL_PATH).join(last_device.name.clone()));
- assert_eq!(tel_devices.len(), 1);
- fx_log_info!("connecting to {:?}", &*found_device_path);
- let found_device = isolated_devmgr::open_file_in_isolated_devmgr(*found_device_path).unwrap();
+ let found_device =
+ get_fake_device_in_isolated_devmgr(TEL_PATH).await.expect("getting fake device");
let launcher = launcher().context("Failed to open launcher service")?;
let chan = qmi::connect_transport_device(&found_device).await?;
let app =
@@ -41,6 +30,7 @@
let ril_modem_setup = app.connect_to_service::<SetupMarker>()?;
ril_modem_setup.connect_transport(chan).await?.expect("make sure telephony svc is running");
let ril_modem = app.connect_to_service::<RadioInterfaceLayerMarker>()?;
+
fx_log_info!("sending a IMEI request");
let imei = ril_modem.get_device_identity().await?.expect("error sending IMEI request");
assert_eq!(imei, "359260080168351");
diff --git a/src/connectivity/telephony/tests/fake-drivers/at-fake-transport/fake-device.cc b/src/connectivity/telephony/tests/fake-drivers/at-fake-transport/fake-device.cc
index 6f67d39..d3f8e1ee 100644
--- a/src/connectivity/telephony/tests/fake-drivers/at-fake-transport/fake-device.cc
+++ b/src/connectivity/telephony/tests/fake-drivers/at-fake-transport/fake-device.cc
@@ -13,6 +13,7 @@
#include <cstdio>
#include <future>
#include <string>
+
#include <ddktl/fidl.h>
namespace fidl_tel_snoop = ::llcpp::fuchsia::telephony::snoop;
@@ -24,6 +25,14 @@
static const std::string kAtCmdRespManuId = "Sierra Wireless Incorporated\r\rOK\r";
static const std::string kAtCmdRespErr = "ERROR\r";
+static const size_t kRaceDetectionPrefixSize = 4;
+static const std::string kRaceDetectionRequestPrefix = "RACE";
+static const std::string kRaceDetectionResponsePrefix = "ECAR";
+
+static const std::string kInvalidUnicodeRequest = "INVALID UNICODE";
+static const size_t kInvalidUnicodeResponseSize = 2;
+static const char* kInvalidUnicodeResponse = "\x80\x81";
+
constexpr uint32_t kTelCtrlPlanePktMax = 2048;
AtDevice::AtDevice(zx_device_t* device) : Device(device) {}
@@ -52,7 +61,7 @@
}
void AtDevice::SnoopCtrlMsg(uint8_t* snoop_data, uint32_t snoop_data_len,
- fidl_tel_snoop::Direction direction) {
+ fidl_tel_snoop::Direction direction) {
if (GetCtrlSnoopChannel()) {
fidl_tel_snoop::Message snoop_msg;
fidl_tel_snoop::QmiMessage msg;
@@ -64,26 +73,44 @@
memcpy(msg.opaque_bytes.data_, snoop_data, current_length);
snoop_msg.set_qmi_message(&msg);
zxlogf(INFO, "at-fake-transport: snoop msg %u %u %u %u sent\n", msg.opaque_bytes.data_[0],
- msg.opaque_bytes.data_[1], msg.opaque_bytes.data_[2],
- msg.opaque_bytes.data_[3]);
+ msg.opaque_bytes.data_[1], msg.opaque_bytes.data_[2], msg.opaque_bytes.data_[3]);
fidl_tel_snoop::Publisher::Call::SendMessage(zx::unowned_channel(GetCtrlSnoopChannel().get()),
std::move(snoop_msg));
}
}
void AtDevice::ReplyCtrlMsg(uint8_t* req, uint32_t req_size, uint8_t* resp, uint32_t resp_size) {
- zxlogf(INFO, "at-fake-driver: req %u %u %u %u with len %u\n", req[0], req[1], req[2], req[3], req_size);
+ zxlogf(INFO, "at-fake-driver: req %u %u %u %u with len %u\n", req[0], req[1], req[2], req[3],
+ req_size);
if (0 == memcmp(req, kAtCmdReqAtdStr.c_str(), kAtCmdReqAtdStr.size())) {
resp_size = std::min(kAtCmdRespNoCarrier.size(), static_cast<std::size_t>(resp_size));
memcpy(resp, kAtCmdRespNoCarrier.c_str(), resp_size);
- zxlogf(INFO, "at-fake-driver: resp %u %u %u %u with len %u\n", resp[0], resp[1], resp[2], resp[3], resp_size);
+ zxlogf(INFO, "at-fake-driver: resp %u %u %u %u with len %u\n", resp[0], resp[1], resp[2],
+ resp[3], resp_size);
sent_fake_at_msg(GetCtrlChannel(), resp, resp_size);
SnoopCtrlMsg(resp, resp_size, fidl_tel_snoop::Direction::FROM_MODEM);
+
} else if (0 == memcmp(req, kAtCmdReqAtCgmi.c_str(), kAtCmdReqAtCgmi.size())) {
resp_size = std::min(kAtCmdRespManuId.size(), static_cast<std::size_t>(resp_size));
memcpy(resp, kAtCmdRespManuId.c_str(), resp_size);
sent_fake_at_msg(GetCtrlChannel(), resp, resp_size);
SnoopCtrlMsg(resp, resp_size, fidl_tel_snoop::Direction::FROM_MODEM);
+
+ } else if (0 == memcmp(req, kInvalidUnicodeRequest.c_str(), kInvalidUnicodeRequest.size())) {
+ resp_size = std::min(kInvalidUnicodeResponseSize, static_cast<std::size_t>(resp_size));
+ memcpy(resp, kInvalidUnicodeResponse, resp_size);
+ sent_fake_at_msg(GetCtrlChannel(), resp, resp_size);
+ SnoopCtrlMsg(resp, resp_size, fidl_tel_snoop::Direction::FROM_MODEM);
+
+ } else if (req_size >= kRaceDetectionPrefixSize &&
+ 0 == memcmp(req, kRaceDetectionRequestPrefix.c_str(), kRaceDetectionPrefixSize)) {
+ resp_size = std::min(resp_size, req_size);
+ memcpy(resp, kRaceDetectionResponsePrefix.c_str(), kRaceDetectionPrefixSize);
+ memcpy(resp + kRaceDetectionPrefixSize, req + kRaceDetectionPrefixSize,
+ req_size - kRaceDetectionPrefixSize);
+ sent_fake_at_msg(GetCtrlChannel(), resp, resp_size);
+ SnoopCtrlMsg(resp, resp_size, fidl_tel_snoop::Direction::FROM_MODEM);
+
} else {
zxlogf(ERROR, "at-fake-driver: unexpected at msg received\n");
resp_size = std::min(kAtCmdRespErr.size(), static_cast<std::size_t>(resp_size));
@@ -149,8 +176,7 @@
// create a port to watch at messages
zx_status_t status = zx::port::create(0, &GetCtrlChannelPort());
if (status != ZX_OK) {
- zxlogf(ERROR, "at-fake-transport: failed to create a port: %s\n",
- zx_status_get_string(status));
+ zxlogf(ERROR, "at-fake-transport: failed to create a port: %s\n", zx_status_get_string(status));
return status;
}
diff --git a/src/connectivity/telephony/tests/meta/tel_fake_at_query.cmx b/src/connectivity/telephony/tests/meta/tel_fake_at_query.cmx
new file mode 100644
index 0000000..a1e54ea
--- /dev/null
+++ b/src/connectivity/telephony/tests/meta/tel_fake_at_query.cmx
@@ -0,0 +1,32 @@
+{
+ "facets": {
+ "fuchsia.test": {
+ "injected-services": {
+ "fuchsia.tel.devmgr.IsolatedDevmgr": [
+ "fuchsia-pkg://fuchsia.com/tel_devmgr_at_component_test#meta/tel_devmgr_at_component_test.cmx"
+ ]
+ }
+ }
+ },
+ "program": {
+ "binary": "test/tel_fake_at_query"
+ },
+ "sandbox": {
+ "boot": [
+ "bin",
+ "driver",
+ "lib"
+ ],
+ "dev": [
+ "class/at-transport",
+ "test/test"
+ ],
+ "services": [
+ "fuchsia.sys.Launcher",
+ "fuchsia.tel.devmgr.IsolatedDevmgr"
+ ],
+ "system": [
+ "driver/at-fake-transport.so"
+ ]
+ }
+}