recovery_util/
regulatory.rs1use anyhow::{format_err, Context as _, Error};
6use fidl_fuchsia_hwinfo as hwinfo;
7use fidl_fuchsia_intl::RegulatoryDomain;
8use fidl_fuchsia_location_namedplace::{
9 RegulatoryRegionConfiguratorMarker, RegulatoryRegionConfiguratorProxy,
10};
11use fuchsia_component::client::connect_to_protocol;
12
13pub async fn set_region_code_from_factory() -> Result<(), Error> {
17 let hwinfo_proxy = connect_to_protocol::<hwinfo::ProductMarker>()
18 .context("Failed to connect to hwinfo protocol")?;
19
20 let configurator_proxy = connect_to_protocol::<RegulatoryRegionConfiguratorMarker>()
21 .context("Failed to connect to Configurator protocol")?;
22
23 let region_code = read_region_code_from_factory(&hwinfo_proxy).await?;
24 set_region_code(®ion_code, &configurator_proxy)
25}
26
27async fn read_region_code_from_factory(proxy: &hwinfo::ProductProxy) -> Result<String, Error> {
30 let product_info = proxy.get_info().await.context("Failed to get_info from ProductProxy")?;
31
32 if let Some(RegulatoryDomain { country_code: Some(country_code), .. }) =
33 product_info.regulatory_domain
34 {
35 return Ok(country_code);
36 }
37
38 Err(format_err!("No region code found, defaulting to worldwide mode (2.4GHz networks only)"))
39}
40
41fn set_region_code(
42 region_code: &str,
43 proxy: &RegulatoryRegionConfiguratorProxy,
44) -> Result<(), Error> {
45 validate_region_code(®ion_code).context("Failed to validate region code")?;
46
47 println!("Set region code: {:?}", region_code);
48 proxy.set_region(®ion_code).context("Set region code")?;
49 Ok(())
50}
51
52fn validate_region_code(region_code: &str) -> Result<(), Error> {
53 if region_code.len() != 2 {
55 return Err(format_err!(
56 "Invalid region code requested to set_region_code: {:?}",
57 region_code
58 ));
59 }
60
61 Ok(())
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67 use fuchsia_async::TimeoutExt;
68 use futures::channel::mpsc;
69 use futures::{StreamExt, TryStreamExt};
70 use zx::MonotonicDuration;
71 use {
72 fidl_fuchsia_hwinfo as hwinfo, fidl_fuchsia_location_namedplace as regulatory,
73 fuchsia_async as fasync,
74 };
75
76 fn create_mock_hwinfo_server(
77 mock_info: hwinfo::ProductInfo,
78 ) -> Result<hwinfo::ProductProxy, Error> {
79 let (proxy, mut request_stream) =
80 fidl::endpoints::create_proxy_and_stream::<hwinfo::ProductMarker>();
81
82 fasync::Task::local(async move {
83 while let Some(request) =
84 request_stream.try_next().await.expect("failed to read mock request")
85 {
86 match request {
87 hwinfo::ProductRequest::GetInfo { responder } => {
88 responder.send(&mock_info).ok();
89 }
90 }
91 }
92 })
93 .detach();
94
95 Ok(proxy)
96 }
97
98 fn create_mock_regulatory_configurator_server(
99 ) -> Result<(regulatory::RegulatoryRegionConfiguratorProxy, mpsc::Receiver<String>), Error>
100 {
101 let (mut sender, receiver) = mpsc::channel(1);
102 let (proxy, mut request_stream) = fidl::endpoints::create_proxy_and_stream::<
103 regulatory::RegulatoryRegionConfiguratorMarker,
104 >();
105
106 fasync::Task::local(async move {
107 while let Some(request) =
108 request_stream.try_next().await.expect("failed to read mock request")
109 {
110 match request {
111 regulatory::RegulatoryRegionConfiguratorRequest::SetRegion {
112 region,
113 control_handle: _,
114 } => {
115 sender.start_send(region).unwrap();
116 }
117 }
118 }
119 })
120 .detach();
121
122 Ok((proxy, receiver))
123 }
124
125 #[fuchsia::test]
126 async fn test_read_from_hwinfo_success() {
127 let expected_region_code = "AA".to_string();
129 let mut regulatory_domain = RegulatoryDomain::default();
130 regulatory_domain.country_code = Some(expected_region_code.clone());
131
132 let mut product_info = hwinfo::ProductInfo::default();
133 product_info.regulatory_domain = Some(regulatory_domain);
134
135 let proxy = create_mock_hwinfo_server(product_info).unwrap();
136
137 let region_code = read_region_code_from_factory(&proxy).await.unwrap();
138
139 assert_eq!(region_code, expected_region_code);
140 }
141
142 #[fuchsia::test]
143 async fn test_read_from_hwinfo_no_regulatory_domain_returns_error() {
144 let product_info = hwinfo::ProductInfo::default();
145 let proxy = create_mock_hwinfo_server(product_info).unwrap();
146
147 let result = read_region_code_from_factory(&proxy).await;
148
149 assert!(result.is_err());
150 assert_eq!(
151 format!("{}", result.unwrap_err()),
152 "No region code found, defaulting to worldwide mode (2.4GHz networks only)"
153 );
154 }
155
156 #[fuchsia::test]
157 async fn test_read_from_hwinfo_no_country_code_returns_error() {
158 let regulatory_domain = RegulatoryDomain::default();
159
160 let mut product_info = hwinfo::ProductInfo::default();
161 product_info.regulatory_domain = Some(regulatory_domain);
162 let proxy = create_mock_hwinfo_server(product_info).unwrap();
163
164 let result = read_region_code_from_factory(&proxy).await;
165
166 assert!(result.is_err());
167 assert_eq!(
168 format!("{}", result.unwrap_err()),
169 "No region code found, defaulting to worldwide mode (2.4GHz networks only)"
170 );
171 }
172
173 #[fuchsia::test]
174 async fn test_set_region_code_success() {
175 let valid_region_code = "AA".to_string();
176
177 let (proxy, mut receiver) = create_mock_regulatory_configurator_server().unwrap();
178
179 set_region_code(&valid_region_code, &proxy).unwrap();
180
181 let region_code_received =
182 receiver.next().on_timeout(MonotonicDuration::from_seconds(5), || None).await.unwrap();
183 assert_eq!(region_code_received, valid_region_code);
184 }
185
186 #[fuchsia::test]
187 async fn test_set_invalid_region_code_returns_error() {
188 let invalid_region_code = "a".to_string();
190
191 let (proxy, mut receiver) = create_mock_regulatory_configurator_server().unwrap();
192
193 let result = set_region_code(&invalid_region_code, &proxy);
194
195 assert!(result.is_err());
196 assert!(receiver.try_next().is_err());
198 }
199
200 #[fasync::run_singlethreaded(test)]
201 async fn test_valid_region_codes() {
202 let valid_codes = vec!["AA", "ZZ"];
203
204 for code in valid_codes {
205 validate_region_code(code).unwrap();
206 }
207 }
208
209 #[fasync::run_singlethreaded(test)]
210 async fn test_invalid_region_codes() {
211 let invalid_codes = vec!["", "a", "test"];
212
213 for code in invalid_codes {
214 validate_region_code(code).unwrap_err();
215 }
216 }
217}