recovery_util/
regulatory.rs1use anyhow::{Context as _, Error, format_err};
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 fidl_fuchsia_hwinfo as hwinfo;
68 use fidl_fuchsia_location_namedplace as regulatory;
69 use fuchsia_async as fasync;
70 use fuchsia_async::TimeoutExt;
71 use futures::channel::mpsc;
72 use futures::{StreamExt, TryStreamExt};
73 use zx::MonotonicDuration;
74
75 fn create_mock_hwinfo_server(
76 mock_info: hwinfo::ProductInfo,
77 ) -> Result<hwinfo::ProductProxy, Error> {
78 let (proxy, mut request_stream) =
79 fidl::endpoints::create_proxy_and_stream::<hwinfo::ProductMarker>();
80
81 fasync::Task::local(async move {
82 while let Some(request) =
83 request_stream.try_next().await.expect("failed to read mock request")
84 {
85 match request {
86 hwinfo::ProductRequest::GetInfo { responder } => {
87 responder.send(&mock_info).ok();
88 }
89 }
90 }
91 })
92 .detach();
93
94 Ok(proxy)
95 }
96
97 fn create_mock_regulatory_configurator_server()
98 -> Result<(regulatory::RegulatoryRegionConfiguratorProxy, mpsc::Receiver<String>), Error> {
99 let (mut sender, receiver) = mpsc::channel(1);
100 let (proxy, mut request_stream) = fidl::endpoints::create_proxy_and_stream::<
101 regulatory::RegulatoryRegionConfiguratorMarker,
102 >();
103
104 fasync::Task::local(async move {
105 while let Some(request) =
106 request_stream.try_next().await.expect("failed to read mock request")
107 {
108 match request {
109 regulatory::RegulatoryRegionConfiguratorRequest::SetRegion {
110 region,
111 control_handle: _,
112 } => {
113 sender.start_send(region).unwrap();
114 }
115 }
116 }
117 })
118 .detach();
119
120 Ok((proxy, receiver))
121 }
122
123 #[fuchsia::test]
124 async fn test_read_from_hwinfo_success() {
125 let expected_region_code = "AA".to_string();
127 let mut regulatory_domain = RegulatoryDomain::default();
128 regulatory_domain.country_code = Some(expected_region_code.clone());
129
130 let mut product_info = hwinfo::ProductInfo::default();
131 product_info.regulatory_domain = Some(regulatory_domain);
132
133 let proxy = create_mock_hwinfo_server(product_info).unwrap();
134
135 let region_code = read_region_code_from_factory(&proxy).await.unwrap();
136
137 assert_eq!(region_code, expected_region_code);
138 }
139
140 #[fuchsia::test]
141 async fn test_read_from_hwinfo_no_regulatory_domain_returns_error() {
142 let product_info = hwinfo::ProductInfo::default();
143 let proxy = create_mock_hwinfo_server(product_info).unwrap();
144
145 let result = read_region_code_from_factory(&proxy).await;
146
147 assert!(result.is_err());
148 assert_eq!(
149 format!("{}", result.unwrap_err()),
150 "No region code found, defaulting to worldwide mode (2.4GHz networks only)"
151 );
152 }
153
154 #[fuchsia::test]
155 async fn test_read_from_hwinfo_no_country_code_returns_error() {
156 let regulatory_domain = RegulatoryDomain::default();
157
158 let mut product_info = hwinfo::ProductInfo::default();
159 product_info.regulatory_domain = Some(regulatory_domain);
160 let proxy = create_mock_hwinfo_server(product_info).unwrap();
161
162 let result = read_region_code_from_factory(&proxy).await;
163
164 assert!(result.is_err());
165 assert_eq!(
166 format!("{}", result.unwrap_err()),
167 "No region code found, defaulting to worldwide mode (2.4GHz networks only)"
168 );
169 }
170
171 #[fuchsia::test]
172 async fn test_set_region_code_success() {
173 let valid_region_code = "AA".to_string();
174
175 let (proxy, mut receiver) = create_mock_regulatory_configurator_server().unwrap();
176
177 set_region_code(&valid_region_code, &proxy).unwrap();
178
179 let region_code_received =
180 receiver.next().on_timeout(MonotonicDuration::from_seconds(5), || None).await.unwrap();
181 assert_eq!(region_code_received, valid_region_code);
182 }
183
184 #[fuchsia::test]
185 async fn test_set_invalid_region_code_returns_error() {
186 let invalid_region_code = "a".to_string();
188
189 let (proxy, mut receiver) = create_mock_regulatory_configurator_server().unwrap();
190
191 let result = set_region_code(&invalid_region_code, &proxy);
192
193 assert!(result.is_err());
194 assert!(receiver.try_next().is_err());
196 }
197
198 #[fuchsia::test]
199 async fn test_valid_region_codes() {
200 let valid_codes = vec!["AA", "ZZ"];
201
202 for code in valid_codes {
203 validate_region_code(code).unwrap();
204 }
205 }
206
207 #[fuchsia::test]
208 async fn test_invalid_region_codes() {
209 let invalid_codes = vec!["", "a", "test"];
210
211 for code in invalid_codes {
212 validate_region_code(code).unwrap_err();
213 }
214 }
215}