1use anyhow::{format_err, Error};
6
7use serde_json::{from_value, to_value, Value};
8
9use crate::setui::types::{IntlInfo, MicStates, NetworkType, SetUiResult};
10use fidl_fuchsia_media::AudioRenderUsage2;
11use fidl_fuchsia_settings::{
12 self as fsettings, AudioMarker, AudioStreamSettingSource, AudioStreamSettings2,
13 ConfigurationInterfaces, DeviceState, DisplayMarker, DisplaySettings, InputMarker, InputState,
14 IntlMarker, SetupMarker, SetupSettings, Volume,
15};
16use fuchsia_component::client::connect_to_protocol;
17use log::info;
18
19#[derive(Debug)]
21pub struct SetUiFacade {
22 audio_proxy: Option<fsettings::AudioProxy>,
25
26 display_proxy: Option<fsettings::DisplayProxy>,
28
29 input_proxy: Option<fsettings::InputProxy>,
31}
32
33impl SetUiFacade {
34 pub fn new() -> SetUiFacade {
35 SetUiFacade { audio_proxy: None, display_proxy: None, input_proxy: None }
36 }
37
38 pub async fn set_network(&self, args: Value) -> Result<Value, Error> {
43 let network_type: NetworkType = from_value(args)?;
44 info!("set_network input {:?}", network_type);
45 let setup_service_proxy = match connect_to_protocol::<SetupMarker>() {
46 Ok(proxy) => proxy,
47 Err(e) => bail!("Failed to connect to Setup service {:?}.", e),
48 };
49
50 let mut settings = SetupSettings::default();
51
52 match network_type {
53 NetworkType::Ethernet => {
54 settings.enabled_configuration_interfaces = Some(ConfigurationInterfaces::ETHERNET);
55 }
56 NetworkType::Wifi => {
57 settings.enabled_configuration_interfaces = Some(ConfigurationInterfaces::WIFI);
58 }
59 _ => return Err(format_err!("Network type must either be ethernet or wifi.")),
60 }
61 match setup_service_proxy.set(&settings, false).await? {
64 Ok(_) => Ok(to_value(SetUiResult::Success)?),
65 Err(err) => Err(format_err!("Update network settings failed with err {:?}", err)),
66 }
67 }
68
69 pub async fn get_network_setting(&self) -> Result<Value, Error> {
73 let setup_service_proxy = match connect_to_protocol::<SetupMarker>() {
74 Ok(proxy) => proxy,
75 Err(e) => bail!("Failed to connect to Setup service {:?}.", e),
76 };
77 let setting = setup_service_proxy.watch().await?;
78 match setting.enabled_configuration_interfaces {
79 Some(ConfigurationInterfaces::ETHERNET) => Ok(to_value(NetworkType::Ethernet)?),
80 Some(ConfigurationInterfaces::WIFI) => Ok(to_value(NetworkType::Wifi)?),
81 _ => Ok(to_value(NetworkType::Unknown)?),
82 }
83 }
84
85 pub async fn set_intl_setting(&self, args: Value) -> Result<Value, Error> {
96 let intl_info: IntlInfo = from_value(args)?;
97 info!("Received Intl Settings Request {:?}", intl_info);
98
99 let intl_service_proxy = match connect_to_protocol::<IntlMarker>() {
100 Ok(proxy) => proxy,
101 Err(e) => bail!("Failed to connect to Intl service {:?}.", e),
102 };
103 match intl_service_proxy.set(&intl_info.into()).await? {
104 Ok(_) => Ok(to_value(SetUiResult::Success)?),
105 Err(err) => Err(format_err!("Update intl settings failed with err {:?}", err)),
106 }
107 }
108
109 pub async fn get_intl_setting(&self) -> Result<Value, Error> {
113 let intl_service_proxy = match connect_to_protocol::<IntlMarker>() {
114 Ok(proxy) => proxy,
115 Err(e) => bail!("Failed to connect to Intl service {:?}.", e),
116 };
117 let intl_info: IntlInfo = intl_service_proxy.watch().await?.into();
118 return Ok(to_value(&intl_info)?);
119 }
120
121 pub async fn is_mic_muted(&self) -> Result<Value, Error> {
125 let input_proxy = match self.input_proxy.as_ref() {
126 Some(proxy) => proxy.clone(),
127 None => match connect_to_protocol::<InputMarker>() {
128 Ok(proxy) => proxy,
129 Err(e) => bail!("isMicMuted - failed to connect to Input service {:?}.", e),
130 },
131 };
132
133 match input_proxy.watch().await?.devices {
134 Some(input_device) => {
135 let mut muted = false;
136 if let Some(device) = input_device
137 .into_iter()
138 .find(|device| device.device_type == Some(fsettings::DeviceType::Microphone))
139 {
140 match device.state {
141 Some(state) => {
142 muted = state.toggle_flags == Some(fsettings::ToggleStateFlags::MUTED);
143 }
144 _ => (),
145 }
146 return Ok(to_value(muted)?);
147 } else {
148 return Ok(to_value(false)?);
150 }
151 }
152 _ => Err(format_err!("isMicMuted - cannot read input state.")),
153 }
154 }
155
156 pub async fn set_brightness(&self, args: Value) -> Result<Value, Error> {
161 let brightness: f32 = from_value(args)?;
162
163 let display_proxy = match self.display_proxy.as_ref() {
166 Some(proxy) => proxy.clone(),
167 None => match connect_to_protocol::<DisplayMarker>() {
168 Ok(proxy) => proxy,
169 Err(e) => bail!("Failed to connect to Display service {:?}.", e),
170 },
171 };
172
173 let settings = DisplaySettings {
174 auto_brightness: Some(false),
175 brightness_value: Some(brightness),
176 ..Default::default()
177 };
178 match display_proxy.set(&settings).await? {
179 Ok(_) => Ok(to_value(SetUiResult::Success)?),
180 Err(e) => Err(format_err!("SetBrightness failed with err {:?}", e)),
181 }
182 }
183
184 pub async fn set_media_volume(&self, args: Value) -> Result<Value, Error> {
189 let volume: f32 = from_value(args)?;
190
191 let audio_proxy = match self.audio_proxy.as_ref() {
194 Some(proxy) => proxy.clone(),
195 None => match connect_to_protocol::<AudioMarker>() {
196 Ok(proxy) => proxy,
197 Err(e) => bail!("Failed to connect to Display service {:?}.", e),
198 },
199 };
200
201 let stream_settings = AudioStreamSettings2 {
202 stream: Some(AudioRenderUsage2::Media),
203 source: Some(AudioStreamSettingSource::User),
204 user_volume: Some(Volume {
205 level: Some(volume),
206 muted: Some(false),
207 ..Default::default()
208 }),
209 ..Default::default()
210 };
211 let settings = fsettings::AudioSettings2 {
212 streams: Some(vec![stream_settings]),
213 ..Default::default()
214 };
215
216 info!("Setting audio settings {:?}", settings);
217 match audio_proxy.set2(&settings).await? {
218 Ok(_) => Ok(to_value(SetUiResult::Success)?),
219 Err(e) => Err(format_err!("SetVolume failed with err {:?}", e)),
220 }
221 }
222
223 pub async fn set_mic_mute(&self, args: Value) -> Result<Value, Error> {
228 let mic_state: MicStates = from_value(args)?;
229
230 let is_muted = self.is_mic_muted().await?.as_bool().unwrap_or(false);
232 let mut mute_mic: bool = false;
233 match mic_state {
234 MicStates::Muted => {
235 if is_muted {
236 return Ok(to_value(SetUiResult::Success)?);
237 }
238 mute_mic = true;
239 }
240 MicStates::Available => {
241 if !is_muted {
242 return Ok(to_value(SetUiResult::Success)?);
243 }
244 }
245 _ => return Err(format_err!("Mic state must either be muted or available.")),
246 }
247
248 let input_proxy = match self.input_proxy.as_ref() {
250 Some(proxy) => proxy.clone(),
251 None => match connect_to_protocol::<InputMarker>() {
252 Ok(proxy) => proxy,
253 Err(e) => bail!("Failed to connect to Microphone {:?}.", e),
254 },
255 };
256
257 let mic_device_name = "microphone";
259 let mut input_states = InputState {
260 name: Some(mic_device_name.to_string()),
261 device_type: Some(fsettings::DeviceType::Microphone),
262 state: Some(DeviceState {
263 toggle_flags: Some(fsettings::ToggleStateFlags::AVAILABLE),
264 ..Default::default()
265 }),
266 ..Default::default()
267 };
268
269 if mute_mic {
271 input_states.state = Some(DeviceState {
272 toggle_flags: Some(fsettings::ToggleStateFlags::MUTED),
273 ..Default::default()
274 });
275 }
276
277 info!("SetMicMute: setting input state {:?}", input_states);
278 match input_proxy.set(&[input_states]).await? {
279 Ok(_) => Ok(to_value(SetUiResult::Success)?),
280 Err(e) => Err(format_err!("SetMicMute failed with err {:?}", e)),
281 }
282 }
283}
284
285#[cfg(test)]
286mod tests {
287 use super::*;
288 use crate::common_utils::test::assert_value_round_trips_as;
289 use crate::setui::types::MicStates::Muted;
290 use crate::setui::types::{HourCycle, LocaleId, TemperatureUnit};
291 use fidl::endpoints::create_proxy_and_stream;
292 use fidl_fuchsia_settings::InputDevice;
293 use fuchsia_async as fasync;
294 use futures::TryStreamExt;
295 use serde_json::json;
296
297 fn make_intl_info() -> IntlInfo {
298 return IntlInfo {
299 locales: Some(vec![LocaleId { id: "en-US".into() }]),
300 temperature_unit: Some(TemperatureUnit::Celsius),
301 time_zone_id: Some("UTC".into()),
302 hour_cycle: Some(HourCycle::H12),
303 };
304 }
305
306 #[test]
307 fn serde_intl_set() {
308 let intl_request = make_intl_info();
309 assert_value_round_trips_as(
310 intl_request,
311 json!(
312 {
313 "locales": [{"id": "en-US"}],
314 "temperature_unit":"Celsius",
315 "time_zone_id": "UTC",
316 "hour_cycle": "H12",
317 }),
318 );
319 }
320
321 #[fasync::run_singlethreaded(test)]
323 async fn test_set_brightness() {
324 let brightness = 0.5f32;
325 let (proxy, mut stream) = create_proxy_and_stream::<DisplayMarker>();
326
327 let facade =
329 SetUiFacade { audio_proxy: None, display_proxy: Some(proxy), input_proxy: None };
330 let facade_fut = async move {
331 assert_eq!(
332 facade.set_brightness(to_value(brightness).unwrap()).await.unwrap(),
333 to_value(SetUiResult::Success).unwrap()
334 );
335 };
336
337 let stream_fut = async move {
339 match stream.try_next().await {
340 Ok(Some(fsettings::DisplayRequest::Set { settings, responder })) => {
341 assert_eq!(
342 settings,
343 DisplaySettings {
344 auto_brightness: Some(false),
345 brightness_value: Some(brightness),
346 ..Default::default()
347 }
348 );
349 responder.send(Ok(())).unwrap();
350 }
351 other => panic!("Unexpected stream item: {:?}", other),
352 }
353 };
354
355 futures::future::join(facade_fut, stream_fut).await;
356 }
357
358 #[fasync::run_singlethreaded(test)]
360 async fn test_set_media_volume() {
361 let volume = 0.5f32;
362 let (proxy, mut stream) = create_proxy_and_stream::<AudioMarker>();
363
364 let facade =
366 SetUiFacade { audio_proxy: Some(proxy), display_proxy: None, input_proxy: None };
367 let facade_fut = async move {
368 assert_eq!(
369 facade.set_media_volume(to_value(volume).unwrap()).await.unwrap(),
370 to_value(SetUiResult::Success).unwrap()
371 );
372 };
373
374 let stream_fut = async move {
376 match stream.try_next().await {
377 Ok(Some(fsettings::AudioRequest::Set2 { settings, responder })) => {
378 let mut streams = settings.streams.unwrap();
379 assert_eq!(1, streams.len());
380 assert_eq!(
381 streams.pop().unwrap(),
382 AudioStreamSettings2 {
383 stream: Some(AudioRenderUsage2::Media),
384 source: Some(AudioStreamSettingSource::User),
385 user_volume: Some(Volume {
386 level: Some(volume),
387 muted: Some(false),
388 ..Default::default()
389 }),
390 ..Default::default()
391 }
392 );
393 responder.send(Ok(())).unwrap();
394 }
395 other => panic!("Unexpected stream item: {:?}", other),
396 }
397 };
398
399 futures::future::join(facade_fut, stream_fut).await;
400 }
401
402 #[fasync::run_singlethreaded(test)]
405 async fn test_set_mic_mute() {
406 let mic_state: MicStates = Muted;
407 let (proxy, mut stream) = create_proxy_and_stream::<InputMarker>();
408
409 let facade =
411 SetUiFacade { audio_proxy: None, display_proxy: None, input_proxy: Some(proxy) };
412 let facade_fut = async move {
413 assert_eq!(
414 facade.set_mic_mute(to_value(mic_state).unwrap()).await.unwrap(),
415 to_value(SetUiResult::Success).unwrap()
416 );
417 };
418
419 let input_stream_fut = async move {
421 match stream.try_next().await {
422 Ok(Some(fsettings::InputRequest::Watch { responder })) => {
423 let device = InputDevice {
424 device_name: None,
425 device_type: Some(fsettings::DeviceType::Microphone),
426 source_states: None,
427 mutable_toggle_state: None,
428 state: Some(DeviceState {
429 toggle_flags: Some(fsettings::ToggleStateFlags::AVAILABLE),
430 ..Default::default()
431 }),
432 ..Default::default()
433 };
434 let settings = fsettings::InputSettings {
435 devices: Some(vec![device]),
436 ..Default::default()
437 };
438 responder.send(&settings).unwrap();
439 }
440 other => panic!("Unexpected Watch request: {:?}", other),
441 }
442 match stream.try_next().await {
443 Ok(Some(fsettings::InputRequest::Set { input_states, responder })) => {
444 assert_eq!(
445 input_states[0],
446 InputState {
447 name: Some("microphone".to_string()),
448 device_type: Some(fsettings::DeviceType::Microphone),
449 state: Some(DeviceState {
450 toggle_flags: Some(fsettings::ToggleStateFlags::MUTED),
451 ..Default::default()
452 }),
453 ..Default::default()
454 }
455 );
456 responder.send(Ok(())).unwrap();
457 }
458 other => panic!("Unexpected stream item: {:?}", other),
459 }
460 };
461
462 futures::future::join(facade_fut, input_stream_fut).await;
463 }
464
465 #[fasync::run_singlethreaded(test)]
467 async fn test_set_mic_mute_in_desired_state() {
468 let mic_state: MicStates = Muted;
469 let (proxy, mut stream) = create_proxy_and_stream::<InputMarker>();
470
471 let facade =
473 SetUiFacade { audio_proxy: None, display_proxy: None, input_proxy: Some(proxy) };
474 let facade_fut = async move {
475 assert_eq!(
476 facade.set_mic_mute(to_value(mic_state).unwrap()).await.unwrap(),
477 to_value(SetUiResult::Success).unwrap()
478 );
479 };
480
481 let input_stream_fut = async move {
483 match stream.try_next().await {
484 Ok(Some(fsettings::InputRequest::Watch { responder })) => {
485 let device = InputDevice {
486 device_name: None,
487 device_type: Some(fsettings::DeviceType::Microphone),
488 source_states: None,
489 mutable_toggle_state: None,
490 state: Some(DeviceState {
491 toggle_flags: Some(fsettings::ToggleStateFlags::MUTED),
492 ..Default::default()
493 }),
494 ..Default::default()
495 };
496 let settings = fsettings::InputSettings {
497 devices: Some(vec![device]),
498 ..Default::default()
499 };
500 responder.send(&settings).unwrap();
501 }
502 other => panic!("Unexpected Watch request: {:?}", other),
503 }
504 match stream.try_next().await {
505 Ok(Some(fsettings::InputRequest::Set { input_states, responder: _ })) => {
506 panic!("Unexpected stream item: {:?}", input_states[0]);
507 }
508 _ => (),
509 }
510 };
511
512 futures::future::join(facade_fut, input_stream_fut).await;
513 }
514
515 #[fasync::run_singlethreaded(test)]
517 async fn test_is_mic_muted() {
518 let is_muted = true;
519 let (proxy, mut stream) = create_proxy_and_stream::<InputMarker>();
520
521 let facade =
523 SetUiFacade { audio_proxy: None, display_proxy: None, input_proxy: Some(proxy) };
524 let facade_fut = async move {
525 assert_eq!(facade.is_mic_muted().await.unwrap(), to_value(is_muted).unwrap());
526 };
527
528 let input_stream_fut = async move {
530 match stream.try_next().await {
531 Ok(Some(fsettings::InputRequest::Watch { responder })) => {
532 let device = InputDevice {
533 device_name: None,
534 device_type: Some(fsettings::DeviceType::Microphone),
535 source_states: None,
536 mutable_toggle_state: None,
537 state: Some(DeviceState {
538 toggle_flags: Some(fsettings::ToggleStateFlags::MUTED),
539 ..Default::default()
540 }),
541 ..Default::default()
542 };
543 let settings = fsettings::InputSettings {
544 devices: Some(vec![device]),
545 ..Default::default()
546 };
547 responder.send(&settings).unwrap();
548 }
549 other => panic!("Unexpected Watch request: {:?}", other),
550 }
551 };
552
553 futures::future::join(facade_fut, input_stream_fut).await;
554 }
555}