1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2022 The ChromiumOS Authors 2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be 3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file. 4*bb4ee6a4SAndroid Build Coastguard Worker 5*bb4ee6a4SAndroid Build Coastguard Worker use std::num::ParseIntError; 6*bb4ee6a4SAndroid Build Coastguard Worker use std::str::ParseBoolError; 7*bb4ee6a4SAndroid Build Coastguard Worker 8*bb4ee6a4SAndroid Build Coastguard Worker use audio_streams::StreamEffect; 9*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(all(unix, feature = "audio_cras"))] 10*bb4ee6a4SAndroid Build Coastguard Worker use libcras::CrasClientType; 11*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(all(unix, feature = "audio_cras"))] 12*bb4ee6a4SAndroid Build Coastguard Worker use libcras::CrasSocketType; 13*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(all(unix, feature = "audio_cras"))] 14*bb4ee6a4SAndroid Build Coastguard Worker use libcras::CrasStreamType; 15*bb4ee6a4SAndroid Build Coastguard Worker use serde::Deserialize; 16*bb4ee6a4SAndroid Build Coastguard Worker use serde::Serialize; 17*bb4ee6a4SAndroid Build Coastguard Worker use serde_keyvalue::FromKeyValues; 18*bb4ee6a4SAndroid Build Coastguard Worker use thiserror::Error as ThisError; 19*bb4ee6a4SAndroid Build Coastguard Worker 20*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::snd::constants::*; 21*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::snd::layout::*; 22*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::snd::sys::StreamSourceBackend as SysStreamSourceBackend; 23*bb4ee6a4SAndroid Build Coastguard Worker 24*bb4ee6a4SAndroid Build Coastguard Worker #[derive(ThisError, Debug, PartialEq, Eq)] 25*bb4ee6a4SAndroid Build Coastguard Worker pub enum Error { 26*bb4ee6a4SAndroid Build Coastguard Worker /// Unknown snd parameter value. 27*bb4ee6a4SAndroid Build Coastguard Worker #[error("Invalid snd parameter value ({0}): {1}")] 28*bb4ee6a4SAndroid Build Coastguard Worker InvalidParameterValue(String, String), 29*bb4ee6a4SAndroid Build Coastguard Worker /// Failed to parse bool value. 30*bb4ee6a4SAndroid Build Coastguard Worker #[error("Invalid bool value: {0}")] 31*bb4ee6a4SAndroid Build Coastguard Worker InvalidBoolValue(ParseBoolError), 32*bb4ee6a4SAndroid Build Coastguard Worker /// Failed to parse int value. 33*bb4ee6a4SAndroid Build Coastguard Worker #[error("Invalid int value: {0}")] 34*bb4ee6a4SAndroid Build Coastguard Worker InvalidIntValue(ParseIntError), 35*bb4ee6a4SAndroid Build Coastguard Worker // Invalid backend. 36*bb4ee6a4SAndroid Build Coastguard Worker #[error("Backend is not implemented")] 37*bb4ee6a4SAndroid Build Coastguard Worker InvalidBackend, 38*bb4ee6a4SAndroid Build Coastguard Worker /// Failed to parse parameters. 39*bb4ee6a4SAndroid Build Coastguard Worker #[error("Invalid snd parameter: {0}")] 40*bb4ee6a4SAndroid Build Coastguard Worker UnknownParameter(String), 41*bb4ee6a4SAndroid Build Coastguard Worker /// Invalid PCM device config index. Happens when the length of PCM device config is less than 42*bb4ee6a4SAndroid Build Coastguard Worker /// the number of PCM devices. 43*bb4ee6a4SAndroid Build Coastguard Worker #[error("Invalid PCM device config index: {0}")] 44*bb4ee6a4SAndroid Build Coastguard Worker InvalidPCMDeviceConfigIndex(usize), 45*bb4ee6a4SAndroid Build Coastguard Worker /// Invalid PCM info direction (VIRTIO_SND_D_OUTPUT = 0, VIRTIO_SND_D_INPUT = 1) 46*bb4ee6a4SAndroid Build Coastguard Worker #[error("Invalid PCM Info direction: {0}")] 47*bb4ee6a4SAndroid Build Coastguard Worker InvalidPCMInfoDirection(u8), 48*bb4ee6a4SAndroid Build Coastguard Worker } 49*bb4ee6a4SAndroid Build Coastguard Worker 50*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)] 51*bb4ee6a4SAndroid Build Coastguard Worker #[serde(into = "String", try_from = "&str")] 52*bb4ee6a4SAndroid Build Coastguard Worker pub enum StreamSourceBackend { 53*bb4ee6a4SAndroid Build Coastguard Worker NULL, 54*bb4ee6a4SAndroid Build Coastguard Worker FILE, 55*bb4ee6a4SAndroid Build Coastguard Worker Sys(SysStreamSourceBackend), 56*bb4ee6a4SAndroid Build Coastguard Worker } 57*bb4ee6a4SAndroid Build Coastguard Worker 58*bb4ee6a4SAndroid Build Coastguard Worker // Implemented to make backend serialization possible, since we deserialize from str. 59*bb4ee6a4SAndroid Build Coastguard Worker impl From<StreamSourceBackend> for String { from(backend: StreamSourceBackend) -> Self60*bb4ee6a4SAndroid Build Coastguard Worker fn from(backend: StreamSourceBackend) -> Self { 61*bb4ee6a4SAndroid Build Coastguard Worker match backend { 62*bb4ee6a4SAndroid Build Coastguard Worker StreamSourceBackend::NULL => "null".to_owned(), 63*bb4ee6a4SAndroid Build Coastguard Worker StreamSourceBackend::FILE => "file".to_owned(), 64*bb4ee6a4SAndroid Build Coastguard Worker StreamSourceBackend::Sys(sys_backend) => sys_backend.into(), 65*bb4ee6a4SAndroid Build Coastguard Worker } 66*bb4ee6a4SAndroid Build Coastguard Worker } 67*bb4ee6a4SAndroid Build Coastguard Worker } 68*bb4ee6a4SAndroid Build Coastguard Worker 69*bb4ee6a4SAndroid Build Coastguard Worker impl TryFrom<&str> for StreamSourceBackend { 70*bb4ee6a4SAndroid Build Coastguard Worker type Error = Error; 71*bb4ee6a4SAndroid Build Coastguard Worker try_from(s: &str) -> Result<Self, Self::Error>72*bb4ee6a4SAndroid Build Coastguard Worker fn try_from(s: &str) -> Result<Self, Self::Error> { 73*bb4ee6a4SAndroid Build Coastguard Worker match s { 74*bb4ee6a4SAndroid Build Coastguard Worker "null" => Ok(StreamSourceBackend::NULL), 75*bb4ee6a4SAndroid Build Coastguard Worker "file" => Ok(StreamSourceBackend::FILE), 76*bb4ee6a4SAndroid Build Coastguard Worker _ => SysStreamSourceBackend::try_from(s).map(StreamSourceBackend::Sys), 77*bb4ee6a4SAndroid Build Coastguard Worker } 78*bb4ee6a4SAndroid Build Coastguard Worker } 79*bb4ee6a4SAndroid Build Coastguard Worker } 80*bb4ee6a4SAndroid Build Coastguard Worker 81*bb4ee6a4SAndroid Build Coastguard Worker /// Holds the parameters for each PCM device 82*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Debug, Clone, Default, Deserialize, Serialize, FromKeyValues, PartialEq, Eq)] 83*bb4ee6a4SAndroid Build Coastguard Worker #[serde(deny_unknown_fields, default)] 84*bb4ee6a4SAndroid Build Coastguard Worker pub struct PCMDeviceParameters { 85*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(all(unix, feature = "audio_cras"))] 86*bb4ee6a4SAndroid Build Coastguard Worker pub client_type: Option<CrasClientType>, 87*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(all(unix, feature = "audio_cras"))] 88*bb4ee6a4SAndroid Build Coastguard Worker pub stream_type: Option<CrasStreamType>, 89*bb4ee6a4SAndroid Build Coastguard Worker pub effects: Option<Vec<StreamEffect>>, 90*bb4ee6a4SAndroid Build Coastguard Worker } 91*bb4ee6a4SAndroid Build Coastguard Worker 92*bb4ee6a4SAndroid Build Coastguard Worker /// Holds the parameters for a cras sound device 93*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Debug, Clone, Deserialize, Serialize, FromKeyValues)] 94*bb4ee6a4SAndroid Build Coastguard Worker #[serde(deny_unknown_fields, default)] 95*bb4ee6a4SAndroid Build Coastguard Worker pub struct Parameters { 96*bb4ee6a4SAndroid Build Coastguard Worker pub capture: bool, 97*bb4ee6a4SAndroid Build Coastguard Worker pub num_output_devices: u32, 98*bb4ee6a4SAndroid Build Coastguard Worker pub num_input_devices: u32, 99*bb4ee6a4SAndroid Build Coastguard Worker pub backend: StreamSourceBackend, 100*bb4ee6a4SAndroid Build Coastguard Worker pub num_output_streams: u32, 101*bb4ee6a4SAndroid Build Coastguard Worker pub num_input_streams: u32, 102*bb4ee6a4SAndroid Build Coastguard Worker pub playback_path: String, 103*bb4ee6a4SAndroid Build Coastguard Worker pub playback_size: usize, 104*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(all(unix, feature = "audio_cras"))] 105*bb4ee6a4SAndroid Build Coastguard Worker #[serde(deserialize_with = "libcras::deserialize_cras_client_type")] 106*bb4ee6a4SAndroid Build Coastguard Worker pub client_type: CrasClientType, 107*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(all(unix, feature = "audio_cras"))] 108*bb4ee6a4SAndroid Build Coastguard Worker pub socket_type: CrasSocketType, 109*bb4ee6a4SAndroid Build Coastguard Worker pub output_device_config: Vec<PCMDeviceParameters>, 110*bb4ee6a4SAndroid Build Coastguard Worker pub input_device_config: Vec<PCMDeviceParameters>, 111*bb4ee6a4SAndroid Build Coastguard Worker pub card_index: usize, 112*bb4ee6a4SAndroid Build Coastguard Worker } 113*bb4ee6a4SAndroid Build Coastguard Worker 114*bb4ee6a4SAndroid Build Coastguard Worker impl Default for Parameters { default() -> Self115*bb4ee6a4SAndroid Build Coastguard Worker fn default() -> Self { 116*bb4ee6a4SAndroid Build Coastguard Worker Parameters { 117*bb4ee6a4SAndroid Build Coastguard Worker capture: false, 118*bb4ee6a4SAndroid Build Coastguard Worker num_output_devices: 1, 119*bb4ee6a4SAndroid Build Coastguard Worker num_input_devices: 1, 120*bb4ee6a4SAndroid Build Coastguard Worker backend: StreamSourceBackend::NULL, 121*bb4ee6a4SAndroid Build Coastguard Worker num_output_streams: 1, 122*bb4ee6a4SAndroid Build Coastguard Worker num_input_streams: 1, 123*bb4ee6a4SAndroid Build Coastguard Worker playback_path: "".to_string(), 124*bb4ee6a4SAndroid Build Coastguard Worker playback_size: 0, 125*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(all(unix, feature = "audio_cras"))] 126*bb4ee6a4SAndroid Build Coastguard Worker client_type: CrasClientType::CRAS_CLIENT_TYPE_CROSVM, 127*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(all(unix, feature = "audio_cras"))] 128*bb4ee6a4SAndroid Build Coastguard Worker socket_type: CrasSocketType::Unified, 129*bb4ee6a4SAndroid Build Coastguard Worker output_device_config: vec![], 130*bb4ee6a4SAndroid Build Coastguard Worker input_device_config: vec![], 131*bb4ee6a4SAndroid Build Coastguard Worker card_index: 0, 132*bb4ee6a4SAndroid Build Coastguard Worker } 133*bb4ee6a4SAndroid Build Coastguard Worker } 134*bb4ee6a4SAndroid Build Coastguard Worker } 135*bb4ee6a4SAndroid Build Coastguard Worker 136*bb4ee6a4SAndroid Build Coastguard Worker impl Parameters { get_total_output_streams(&self) -> u32137*bb4ee6a4SAndroid Build Coastguard Worker pub(crate) fn get_total_output_streams(&self) -> u32 { 138*bb4ee6a4SAndroid Build Coastguard Worker self.num_output_devices * self.num_output_streams 139*bb4ee6a4SAndroid Build Coastguard Worker } 140*bb4ee6a4SAndroid Build Coastguard Worker get_total_input_streams(&self) -> u32141*bb4ee6a4SAndroid Build Coastguard Worker pub(crate) fn get_total_input_streams(&self) -> u32 { 142*bb4ee6a4SAndroid Build Coastguard Worker self.num_input_devices * self.num_input_streams 143*bb4ee6a4SAndroid Build Coastguard Worker } 144*bb4ee6a4SAndroid Build Coastguard Worker get_total_streams(&self) -> u32145*bb4ee6a4SAndroid Build Coastguard Worker pub(crate) fn get_total_streams(&self) -> u32 { 146*bb4ee6a4SAndroid Build Coastguard Worker self.get_total_output_streams() + self.get_total_input_streams() 147*bb4ee6a4SAndroid Build Coastguard Worker } 148*bb4ee6a4SAndroid Build Coastguard Worker 149*bb4ee6a4SAndroid Build Coastguard Worker #[allow(dead_code)] get_device_params( &self, pcm_info: &virtio_snd_pcm_info, ) -> Result<PCMDeviceParameters, Error>150*bb4ee6a4SAndroid Build Coastguard Worker pub(crate) fn get_device_params( 151*bb4ee6a4SAndroid Build Coastguard Worker &self, 152*bb4ee6a4SAndroid Build Coastguard Worker pcm_info: &virtio_snd_pcm_info, 153*bb4ee6a4SAndroid Build Coastguard Worker ) -> Result<PCMDeviceParameters, Error> { 154*bb4ee6a4SAndroid Build Coastguard Worker let device_config = match pcm_info.direction { 155*bb4ee6a4SAndroid Build Coastguard Worker VIRTIO_SND_D_OUTPUT => &self.output_device_config, 156*bb4ee6a4SAndroid Build Coastguard Worker VIRTIO_SND_D_INPUT => &self.input_device_config, 157*bb4ee6a4SAndroid Build Coastguard Worker _ => return Err(Error::InvalidPCMInfoDirection(pcm_info.direction)), 158*bb4ee6a4SAndroid Build Coastguard Worker }; 159*bb4ee6a4SAndroid Build Coastguard Worker let device_idx = u32::from(pcm_info.hdr.hda_fn_nid) as usize; 160*bb4ee6a4SAndroid Build Coastguard Worker device_config 161*bb4ee6a4SAndroid Build Coastguard Worker .get(device_idx) 162*bb4ee6a4SAndroid Build Coastguard Worker .cloned() 163*bb4ee6a4SAndroid Build Coastguard Worker .ok_or(Error::InvalidPCMDeviceConfigIndex(device_idx)) 164*bb4ee6a4SAndroid Build Coastguard Worker } 165*bb4ee6a4SAndroid Build Coastguard Worker } 166*bb4ee6a4SAndroid Build Coastguard Worker 167*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(test)] 168*bb4ee6a4SAndroid Build Coastguard Worker #[allow(clippy::needless_update)] 169*bb4ee6a4SAndroid Build Coastguard Worker mod tests { 170*bb4ee6a4SAndroid Build Coastguard Worker use super::*; 171*bb4ee6a4SAndroid Build Coastguard Worker check_failure(s: &str)172*bb4ee6a4SAndroid Build Coastguard Worker fn check_failure(s: &str) { 173*bb4ee6a4SAndroid Build Coastguard Worker serde_keyvalue::from_key_values::<Parameters>(s).expect_err("parse should have failed"); 174*bb4ee6a4SAndroid Build Coastguard Worker } 175*bb4ee6a4SAndroid Build Coastguard Worker check_success( s: &str, capture: bool, backend: StreamSourceBackend, num_output_devices: u32, num_input_devices: u32, num_output_streams: u32, num_input_streams: u32, output_device_config: Vec<PCMDeviceParameters>, input_device_config: Vec<PCMDeviceParameters>, )176*bb4ee6a4SAndroid Build Coastguard Worker fn check_success( 177*bb4ee6a4SAndroid Build Coastguard Worker s: &str, 178*bb4ee6a4SAndroid Build Coastguard Worker capture: bool, 179*bb4ee6a4SAndroid Build Coastguard Worker backend: StreamSourceBackend, 180*bb4ee6a4SAndroid Build Coastguard Worker num_output_devices: u32, 181*bb4ee6a4SAndroid Build Coastguard Worker num_input_devices: u32, 182*bb4ee6a4SAndroid Build Coastguard Worker num_output_streams: u32, 183*bb4ee6a4SAndroid Build Coastguard Worker num_input_streams: u32, 184*bb4ee6a4SAndroid Build Coastguard Worker output_device_config: Vec<PCMDeviceParameters>, 185*bb4ee6a4SAndroid Build Coastguard Worker input_device_config: Vec<PCMDeviceParameters>, 186*bb4ee6a4SAndroid Build Coastguard Worker ) { 187*bb4ee6a4SAndroid Build Coastguard Worker let params: Parameters = 188*bb4ee6a4SAndroid Build Coastguard Worker serde_keyvalue::from_key_values(s).expect("parse should have succeded"); 189*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(params.capture, capture); 190*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(params.backend, backend); 191*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(params.num_output_devices, num_output_devices); 192*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(params.num_input_devices, num_input_devices); 193*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(params.num_output_streams, num_output_streams); 194*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(params.num_input_streams, num_input_streams); 195*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(params.output_device_config, output_device_config); 196*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(params.input_device_config, input_device_config); 197*bb4ee6a4SAndroid Build Coastguard Worker } 198*bb4ee6a4SAndroid Build Coastguard Worker 199*bb4ee6a4SAndroid Build Coastguard Worker #[test] parameters_fromstr()200*bb4ee6a4SAndroid Build Coastguard Worker fn parameters_fromstr() { 201*bb4ee6a4SAndroid Build Coastguard Worker check_failure("capture=none"); 202*bb4ee6a4SAndroid Build Coastguard Worker check_success( 203*bb4ee6a4SAndroid Build Coastguard Worker "capture=false", 204*bb4ee6a4SAndroid Build Coastguard Worker false, 205*bb4ee6a4SAndroid Build Coastguard Worker StreamSourceBackend::NULL, 206*bb4ee6a4SAndroid Build Coastguard Worker 1, 207*bb4ee6a4SAndroid Build Coastguard Worker 1, 208*bb4ee6a4SAndroid Build Coastguard Worker 1, 209*bb4ee6a4SAndroid Build Coastguard Worker 1, 210*bb4ee6a4SAndroid Build Coastguard Worker vec![], 211*bb4ee6a4SAndroid Build Coastguard Worker vec![], 212*bb4ee6a4SAndroid Build Coastguard Worker ); 213*bb4ee6a4SAndroid Build Coastguard Worker check_success( 214*bb4ee6a4SAndroid Build Coastguard Worker "capture=true,num_output_streams=2,num_input_streams=3", 215*bb4ee6a4SAndroid Build Coastguard Worker true, 216*bb4ee6a4SAndroid Build Coastguard Worker StreamSourceBackend::NULL, 217*bb4ee6a4SAndroid Build Coastguard Worker 1, 218*bb4ee6a4SAndroid Build Coastguard Worker 1, 219*bb4ee6a4SAndroid Build Coastguard Worker 2, 220*bb4ee6a4SAndroid Build Coastguard Worker 3, 221*bb4ee6a4SAndroid Build Coastguard Worker vec![], 222*bb4ee6a4SAndroid Build Coastguard Worker vec![], 223*bb4ee6a4SAndroid Build Coastguard Worker ); 224*bb4ee6a4SAndroid Build Coastguard Worker check_success( 225*bb4ee6a4SAndroid Build Coastguard Worker "capture=true,num_output_devices=3,num_input_devices=2", 226*bb4ee6a4SAndroid Build Coastguard Worker true, 227*bb4ee6a4SAndroid Build Coastguard Worker StreamSourceBackend::NULL, 228*bb4ee6a4SAndroid Build Coastguard Worker 3, 229*bb4ee6a4SAndroid Build Coastguard Worker 2, 230*bb4ee6a4SAndroid Build Coastguard Worker 1, 231*bb4ee6a4SAndroid Build Coastguard Worker 1, 232*bb4ee6a4SAndroid Build Coastguard Worker vec![], 233*bb4ee6a4SAndroid Build Coastguard Worker vec![], 234*bb4ee6a4SAndroid Build Coastguard Worker ); 235*bb4ee6a4SAndroid Build Coastguard Worker check_success( 236*bb4ee6a4SAndroid Build Coastguard Worker "capture=true,num_output_devices=2,num_input_devices=3,\ 237*bb4ee6a4SAndroid Build Coastguard Worker num_output_streams=3,num_input_streams=2", 238*bb4ee6a4SAndroid Build Coastguard Worker true, 239*bb4ee6a4SAndroid Build Coastguard Worker StreamSourceBackend::NULL, 240*bb4ee6a4SAndroid Build Coastguard Worker 2, 241*bb4ee6a4SAndroid Build Coastguard Worker 3, 242*bb4ee6a4SAndroid Build Coastguard Worker 3, 243*bb4ee6a4SAndroid Build Coastguard Worker 2, 244*bb4ee6a4SAndroid Build Coastguard Worker vec![], 245*bb4ee6a4SAndroid Build Coastguard Worker vec![], 246*bb4ee6a4SAndroid Build Coastguard Worker ); 247*bb4ee6a4SAndroid Build Coastguard Worker check_success( 248*bb4ee6a4SAndroid Build Coastguard Worker "capture=true,backend=null,num_output_devices=2,num_input_devices=3,\ 249*bb4ee6a4SAndroid Build Coastguard Worker num_output_streams=3,num_input_streams=2", 250*bb4ee6a4SAndroid Build Coastguard Worker true, 251*bb4ee6a4SAndroid Build Coastguard Worker StreamSourceBackend::NULL, 252*bb4ee6a4SAndroid Build Coastguard Worker 2, 253*bb4ee6a4SAndroid Build Coastguard Worker 3, 254*bb4ee6a4SAndroid Build Coastguard Worker 3, 255*bb4ee6a4SAndroid Build Coastguard Worker 2, 256*bb4ee6a4SAndroid Build Coastguard Worker vec![], 257*bb4ee6a4SAndroid Build Coastguard Worker vec![], 258*bb4ee6a4SAndroid Build Coastguard Worker ); 259*bb4ee6a4SAndroid Build Coastguard Worker check_success( 260*bb4ee6a4SAndroid Build Coastguard Worker "output_device_config=[[effects=[aec]],[]]", 261*bb4ee6a4SAndroid Build Coastguard Worker false, 262*bb4ee6a4SAndroid Build Coastguard Worker StreamSourceBackend::NULL, 263*bb4ee6a4SAndroid Build Coastguard Worker 1, 264*bb4ee6a4SAndroid Build Coastguard Worker 1, 265*bb4ee6a4SAndroid Build Coastguard Worker 1, 266*bb4ee6a4SAndroid Build Coastguard Worker 1, 267*bb4ee6a4SAndroid Build Coastguard Worker vec![ 268*bb4ee6a4SAndroid Build Coastguard Worker PCMDeviceParameters { 269*bb4ee6a4SAndroid Build Coastguard Worker effects: Some(vec![StreamEffect::EchoCancellation]), 270*bb4ee6a4SAndroid Build Coastguard Worker ..Default::default() 271*bb4ee6a4SAndroid Build Coastguard Worker }, 272*bb4ee6a4SAndroid Build Coastguard Worker Default::default(), 273*bb4ee6a4SAndroid Build Coastguard Worker ], 274*bb4ee6a4SAndroid Build Coastguard Worker vec![], 275*bb4ee6a4SAndroid Build Coastguard Worker ); 276*bb4ee6a4SAndroid Build Coastguard Worker check_success( 277*bb4ee6a4SAndroid Build Coastguard Worker "input_device_config=[[effects=[aec]],[]]", 278*bb4ee6a4SAndroid Build Coastguard Worker false, 279*bb4ee6a4SAndroid Build Coastguard Worker StreamSourceBackend::NULL, 280*bb4ee6a4SAndroid Build Coastguard Worker 1, 281*bb4ee6a4SAndroid Build Coastguard Worker 1, 282*bb4ee6a4SAndroid Build Coastguard Worker 1, 283*bb4ee6a4SAndroid Build Coastguard Worker 1, 284*bb4ee6a4SAndroid Build Coastguard Worker vec![], 285*bb4ee6a4SAndroid Build Coastguard Worker vec![ 286*bb4ee6a4SAndroid Build Coastguard Worker PCMDeviceParameters { 287*bb4ee6a4SAndroid Build Coastguard Worker effects: Some(vec![StreamEffect::EchoCancellation]), 288*bb4ee6a4SAndroid Build Coastguard Worker ..Default::default() 289*bb4ee6a4SAndroid Build Coastguard Worker }, 290*bb4ee6a4SAndroid Build Coastguard Worker Default::default(), 291*bb4ee6a4SAndroid Build Coastguard Worker ], 292*bb4ee6a4SAndroid Build Coastguard Worker ); 293*bb4ee6a4SAndroid Build Coastguard Worker 294*bb4ee6a4SAndroid Build Coastguard Worker // Invalid effect in device config 295*bb4ee6a4SAndroid Build Coastguard Worker check_failure("output_device_config=[[effects=[none]]]"); 296*bb4ee6a4SAndroid Build Coastguard Worker } 297*bb4ee6a4SAndroid Build Coastguard Worker 298*bb4ee6a4SAndroid Build Coastguard Worker #[test] 299*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(all(unix, feature = "audio_cras"))] cras_parameters_fromstr()300*bb4ee6a4SAndroid Build Coastguard Worker fn cras_parameters_fromstr() { 301*bb4ee6a4SAndroid Build Coastguard Worker fn cras_check_success( 302*bb4ee6a4SAndroid Build Coastguard Worker s: &str, 303*bb4ee6a4SAndroid Build Coastguard Worker backend: StreamSourceBackend, 304*bb4ee6a4SAndroid Build Coastguard Worker client_type: CrasClientType, 305*bb4ee6a4SAndroid Build Coastguard Worker socket_type: CrasSocketType, 306*bb4ee6a4SAndroid Build Coastguard Worker output_device_config: Vec<PCMDeviceParameters>, 307*bb4ee6a4SAndroid Build Coastguard Worker input_device_config: Vec<PCMDeviceParameters>, 308*bb4ee6a4SAndroid Build Coastguard Worker ) { 309*bb4ee6a4SAndroid Build Coastguard Worker let params: Parameters = 310*bb4ee6a4SAndroid Build Coastguard Worker serde_keyvalue::from_key_values(s).expect("parse should have succeded"); 311*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(params.backend, backend); 312*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(params.client_type, client_type); 313*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(params.socket_type, socket_type); 314*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(params.output_device_config, output_device_config); 315*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(params.input_device_config, input_device_config); 316*bb4ee6a4SAndroid Build Coastguard Worker } 317*bb4ee6a4SAndroid Build Coastguard Worker 318*bb4ee6a4SAndroid Build Coastguard Worker cras_check_success( 319*bb4ee6a4SAndroid Build Coastguard Worker "backend=cras", 320*bb4ee6a4SAndroid Build Coastguard Worker StreamSourceBackend::Sys(SysStreamSourceBackend::CRAS), 321*bb4ee6a4SAndroid Build Coastguard Worker CrasClientType::CRAS_CLIENT_TYPE_CROSVM, 322*bb4ee6a4SAndroid Build Coastguard Worker CrasSocketType::Unified, 323*bb4ee6a4SAndroid Build Coastguard Worker vec![], 324*bb4ee6a4SAndroid Build Coastguard Worker vec![], 325*bb4ee6a4SAndroid Build Coastguard Worker ); 326*bb4ee6a4SAndroid Build Coastguard Worker cras_check_success( 327*bb4ee6a4SAndroid Build Coastguard Worker "backend=cras,client_type=crosvm", 328*bb4ee6a4SAndroid Build Coastguard Worker StreamSourceBackend::Sys(SysStreamSourceBackend::CRAS), 329*bb4ee6a4SAndroid Build Coastguard Worker CrasClientType::CRAS_CLIENT_TYPE_CROSVM, 330*bb4ee6a4SAndroid Build Coastguard Worker CrasSocketType::Unified, 331*bb4ee6a4SAndroid Build Coastguard Worker vec![], 332*bb4ee6a4SAndroid Build Coastguard Worker vec![], 333*bb4ee6a4SAndroid Build Coastguard Worker ); 334*bb4ee6a4SAndroid Build Coastguard Worker cras_check_success( 335*bb4ee6a4SAndroid Build Coastguard Worker "backend=cras,client_type=arcvm", 336*bb4ee6a4SAndroid Build Coastguard Worker StreamSourceBackend::Sys(SysStreamSourceBackend::CRAS), 337*bb4ee6a4SAndroid Build Coastguard Worker CrasClientType::CRAS_CLIENT_TYPE_ARCVM, 338*bb4ee6a4SAndroid Build Coastguard Worker CrasSocketType::Unified, 339*bb4ee6a4SAndroid Build Coastguard Worker vec![], 340*bb4ee6a4SAndroid Build Coastguard Worker vec![], 341*bb4ee6a4SAndroid Build Coastguard Worker ); 342*bb4ee6a4SAndroid Build Coastguard Worker check_failure("backend=cras,client_type=none"); 343*bb4ee6a4SAndroid Build Coastguard Worker cras_check_success( 344*bb4ee6a4SAndroid Build Coastguard Worker "backend=cras,socket_type=legacy", 345*bb4ee6a4SAndroid Build Coastguard Worker StreamSourceBackend::Sys(SysStreamSourceBackend::CRAS), 346*bb4ee6a4SAndroid Build Coastguard Worker CrasClientType::CRAS_CLIENT_TYPE_CROSVM, 347*bb4ee6a4SAndroid Build Coastguard Worker CrasSocketType::Legacy, 348*bb4ee6a4SAndroid Build Coastguard Worker vec![], 349*bb4ee6a4SAndroid Build Coastguard Worker vec![], 350*bb4ee6a4SAndroid Build Coastguard Worker ); 351*bb4ee6a4SAndroid Build Coastguard Worker cras_check_success( 352*bb4ee6a4SAndroid Build Coastguard Worker "backend=cras,socket_type=unified", 353*bb4ee6a4SAndroid Build Coastguard Worker StreamSourceBackend::Sys(SysStreamSourceBackend::CRAS), 354*bb4ee6a4SAndroid Build Coastguard Worker CrasClientType::CRAS_CLIENT_TYPE_CROSVM, 355*bb4ee6a4SAndroid Build Coastguard Worker CrasSocketType::Unified, 356*bb4ee6a4SAndroid Build Coastguard Worker vec![], 357*bb4ee6a4SAndroid Build Coastguard Worker vec![], 358*bb4ee6a4SAndroid Build Coastguard Worker ); 359*bb4ee6a4SAndroid Build Coastguard Worker cras_check_success( 360*bb4ee6a4SAndroid Build Coastguard Worker "output_device_config=[[client_type=crosvm],[client_type=arcvm,stream_type=pro_audio],[]]", 361*bb4ee6a4SAndroid Build Coastguard Worker StreamSourceBackend::NULL, 362*bb4ee6a4SAndroid Build Coastguard Worker CrasClientType::CRAS_CLIENT_TYPE_CROSVM, 363*bb4ee6a4SAndroid Build Coastguard Worker CrasSocketType::Unified, 364*bb4ee6a4SAndroid Build Coastguard Worker vec![ 365*bb4ee6a4SAndroid Build Coastguard Worker PCMDeviceParameters{ 366*bb4ee6a4SAndroid Build Coastguard Worker client_type: Some(CrasClientType::CRAS_CLIENT_TYPE_CROSVM), 367*bb4ee6a4SAndroid Build Coastguard Worker stream_type: None, 368*bb4ee6a4SAndroid Build Coastguard Worker effects: None, 369*bb4ee6a4SAndroid Build Coastguard Worker }, 370*bb4ee6a4SAndroid Build Coastguard Worker PCMDeviceParameters{ 371*bb4ee6a4SAndroid Build Coastguard Worker client_type: Some(CrasClientType::CRAS_CLIENT_TYPE_ARCVM), 372*bb4ee6a4SAndroid Build Coastguard Worker stream_type: Some(CrasStreamType::CRAS_STREAM_TYPE_PRO_AUDIO), 373*bb4ee6a4SAndroid Build Coastguard Worker effects: None, 374*bb4ee6a4SAndroid Build Coastguard Worker }, 375*bb4ee6a4SAndroid Build Coastguard Worker Default::default(), 376*bb4ee6a4SAndroid Build Coastguard Worker ], 377*bb4ee6a4SAndroid Build Coastguard Worker vec![], 378*bb4ee6a4SAndroid Build Coastguard Worker ); 379*bb4ee6a4SAndroid Build Coastguard Worker cras_check_success( 380*bb4ee6a4SAndroid Build Coastguard Worker "input_device_config=[[client_type=crosvm],[client_type=arcvm,effects=[aec],stream_type=pro_audio],[effects=[EchoCancellation]],[]]", 381*bb4ee6a4SAndroid Build Coastguard Worker StreamSourceBackend::NULL, 382*bb4ee6a4SAndroid Build Coastguard Worker CrasClientType::CRAS_CLIENT_TYPE_CROSVM, 383*bb4ee6a4SAndroid Build Coastguard Worker CrasSocketType::Unified, 384*bb4ee6a4SAndroid Build Coastguard Worker vec![], 385*bb4ee6a4SAndroid Build Coastguard Worker vec![ 386*bb4ee6a4SAndroid Build Coastguard Worker PCMDeviceParameters{ 387*bb4ee6a4SAndroid Build Coastguard Worker client_type: Some(CrasClientType::CRAS_CLIENT_TYPE_CROSVM), 388*bb4ee6a4SAndroid Build Coastguard Worker stream_type: None, 389*bb4ee6a4SAndroid Build Coastguard Worker effects: None, 390*bb4ee6a4SAndroid Build Coastguard Worker }, 391*bb4ee6a4SAndroid Build Coastguard Worker PCMDeviceParameters{ 392*bb4ee6a4SAndroid Build Coastguard Worker client_type: Some(CrasClientType::CRAS_CLIENT_TYPE_ARCVM), 393*bb4ee6a4SAndroid Build Coastguard Worker stream_type: Some(CrasStreamType::CRAS_STREAM_TYPE_PRO_AUDIO), 394*bb4ee6a4SAndroid Build Coastguard Worker effects: Some(vec![StreamEffect::EchoCancellation]), 395*bb4ee6a4SAndroid Build Coastguard Worker }, 396*bb4ee6a4SAndroid Build Coastguard Worker PCMDeviceParameters{ 397*bb4ee6a4SAndroid Build Coastguard Worker client_type: None, 398*bb4ee6a4SAndroid Build Coastguard Worker stream_type: None, 399*bb4ee6a4SAndroid Build Coastguard Worker effects: Some(vec![StreamEffect::EchoCancellation]), 400*bb4ee6a4SAndroid Build Coastguard Worker }, 401*bb4ee6a4SAndroid Build Coastguard Worker Default::default(), 402*bb4ee6a4SAndroid Build Coastguard Worker ], 403*bb4ee6a4SAndroid Build Coastguard Worker ); 404*bb4ee6a4SAndroid Build Coastguard Worker 405*bb4ee6a4SAndroid Build Coastguard Worker // Invalid client_type in device config 406*bb4ee6a4SAndroid Build Coastguard Worker check_failure("output_device_config=[[client_type=none]]"); 407*bb4ee6a4SAndroid Build Coastguard Worker 408*bb4ee6a4SAndroid Build Coastguard Worker // Invalid stream type in device config 409*bb4ee6a4SAndroid Build Coastguard Worker check_failure("output_device_config=[[stream_type=none]]"); 410*bb4ee6a4SAndroid Build Coastguard Worker } 411*bb4ee6a4SAndroid Build Coastguard Worker 412*bb4ee6a4SAndroid Build Coastguard Worker #[test] get_device_params_output()413*bb4ee6a4SAndroid Build Coastguard Worker fn get_device_params_output() { 414*bb4ee6a4SAndroid Build Coastguard Worker let params = Parameters { 415*bb4ee6a4SAndroid Build Coastguard Worker output_device_config: vec![ 416*bb4ee6a4SAndroid Build Coastguard Worker PCMDeviceParameters { 417*bb4ee6a4SAndroid Build Coastguard Worker effects: Some(vec![StreamEffect::EchoCancellation]), 418*bb4ee6a4SAndroid Build Coastguard Worker ..Default::default() 419*bb4ee6a4SAndroid Build Coastguard Worker }, 420*bb4ee6a4SAndroid Build Coastguard Worker PCMDeviceParameters { 421*bb4ee6a4SAndroid Build Coastguard Worker effects: Some(vec![ 422*bb4ee6a4SAndroid Build Coastguard Worker StreamEffect::EchoCancellation, 423*bb4ee6a4SAndroid Build Coastguard Worker StreamEffect::EchoCancellation, 424*bb4ee6a4SAndroid Build Coastguard Worker ]), 425*bb4ee6a4SAndroid Build Coastguard Worker ..Default::default() 426*bb4ee6a4SAndroid Build Coastguard Worker }, 427*bb4ee6a4SAndroid Build Coastguard Worker ], 428*bb4ee6a4SAndroid Build Coastguard Worker ..Default::default() 429*bb4ee6a4SAndroid Build Coastguard Worker }; 430*bb4ee6a4SAndroid Build Coastguard Worker 431*bb4ee6a4SAndroid Build Coastguard Worker let default_pcm_info = virtio_snd_pcm_info { 432*bb4ee6a4SAndroid Build Coastguard Worker hdr: virtio_snd_info { 433*bb4ee6a4SAndroid Build Coastguard Worker hda_fn_nid: 0.into(), 434*bb4ee6a4SAndroid Build Coastguard Worker }, 435*bb4ee6a4SAndroid Build Coastguard Worker features: 0.into(), 436*bb4ee6a4SAndroid Build Coastguard Worker formats: 0.into(), 437*bb4ee6a4SAndroid Build Coastguard Worker rates: 0.into(), 438*bb4ee6a4SAndroid Build Coastguard Worker direction: VIRTIO_SND_D_OUTPUT, // Direction is OUTPUT 439*bb4ee6a4SAndroid Build Coastguard Worker channels_min: 1, 440*bb4ee6a4SAndroid Build Coastguard Worker channels_max: 6, 441*bb4ee6a4SAndroid Build Coastguard Worker padding: [0; 5], 442*bb4ee6a4SAndroid Build Coastguard Worker }; 443*bb4ee6a4SAndroid Build Coastguard Worker 444*bb4ee6a4SAndroid Build Coastguard Worker let mut pcm_info = default_pcm_info; 445*bb4ee6a4SAndroid Build Coastguard Worker pcm_info.hdr.hda_fn_nid = 0.into(); 446*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!( 447*bb4ee6a4SAndroid Build Coastguard Worker params.get_device_params(&pcm_info), 448*bb4ee6a4SAndroid Build Coastguard Worker Ok(params.output_device_config[0].clone()) 449*bb4ee6a4SAndroid Build Coastguard Worker ); 450*bb4ee6a4SAndroid Build Coastguard Worker 451*bb4ee6a4SAndroid Build Coastguard Worker let mut pcm_info = default_pcm_info; 452*bb4ee6a4SAndroid Build Coastguard Worker pcm_info.hdr.hda_fn_nid = 1.into(); 453*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!( 454*bb4ee6a4SAndroid Build Coastguard Worker params.get_device_params(&pcm_info), 455*bb4ee6a4SAndroid Build Coastguard Worker Ok(params.output_device_config[1].clone()) 456*bb4ee6a4SAndroid Build Coastguard Worker ); 457*bb4ee6a4SAndroid Build Coastguard Worker 458*bb4ee6a4SAndroid Build Coastguard Worker let mut pcm_info = default_pcm_info; 459*bb4ee6a4SAndroid Build Coastguard Worker pcm_info.hdr.hda_fn_nid = 2.into(); 460*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!( 461*bb4ee6a4SAndroid Build Coastguard Worker params.get_device_params(&pcm_info), 462*bb4ee6a4SAndroid Build Coastguard Worker Err(Error::InvalidPCMDeviceConfigIndex(2)) 463*bb4ee6a4SAndroid Build Coastguard Worker ); 464*bb4ee6a4SAndroid Build Coastguard Worker } 465*bb4ee6a4SAndroid Build Coastguard Worker 466*bb4ee6a4SAndroid Build Coastguard Worker #[test] get_device_params_input()467*bb4ee6a4SAndroid Build Coastguard Worker fn get_device_params_input() { 468*bb4ee6a4SAndroid Build Coastguard Worker let params = Parameters { 469*bb4ee6a4SAndroid Build Coastguard Worker input_device_config: vec![ 470*bb4ee6a4SAndroid Build Coastguard Worker PCMDeviceParameters { 471*bb4ee6a4SAndroid Build Coastguard Worker effects: Some(vec![ 472*bb4ee6a4SAndroid Build Coastguard Worker StreamEffect::EchoCancellation, 473*bb4ee6a4SAndroid Build Coastguard Worker StreamEffect::EchoCancellation, 474*bb4ee6a4SAndroid Build Coastguard Worker ]), 475*bb4ee6a4SAndroid Build Coastguard Worker ..Default::default() 476*bb4ee6a4SAndroid Build Coastguard Worker }, 477*bb4ee6a4SAndroid Build Coastguard Worker PCMDeviceParameters { 478*bb4ee6a4SAndroid Build Coastguard Worker effects: Some(vec![StreamEffect::EchoCancellation]), 479*bb4ee6a4SAndroid Build Coastguard Worker ..Default::default() 480*bb4ee6a4SAndroid Build Coastguard Worker }, 481*bb4ee6a4SAndroid Build Coastguard Worker ], 482*bb4ee6a4SAndroid Build Coastguard Worker ..Default::default() 483*bb4ee6a4SAndroid Build Coastguard Worker }; 484*bb4ee6a4SAndroid Build Coastguard Worker 485*bb4ee6a4SAndroid Build Coastguard Worker let default_pcm_info = virtio_snd_pcm_info { 486*bb4ee6a4SAndroid Build Coastguard Worker hdr: virtio_snd_info { 487*bb4ee6a4SAndroid Build Coastguard Worker hda_fn_nid: 0.into(), 488*bb4ee6a4SAndroid Build Coastguard Worker }, 489*bb4ee6a4SAndroid Build Coastguard Worker features: 0.into(), 490*bb4ee6a4SAndroid Build Coastguard Worker formats: 0.into(), 491*bb4ee6a4SAndroid Build Coastguard Worker rates: 0.into(), 492*bb4ee6a4SAndroid Build Coastguard Worker direction: VIRTIO_SND_D_INPUT, // Direction is INPUT 493*bb4ee6a4SAndroid Build Coastguard Worker channels_min: 1, 494*bb4ee6a4SAndroid Build Coastguard Worker channels_max: 6, 495*bb4ee6a4SAndroid Build Coastguard Worker padding: [0; 5], 496*bb4ee6a4SAndroid Build Coastguard Worker }; 497*bb4ee6a4SAndroid Build Coastguard Worker 498*bb4ee6a4SAndroid Build Coastguard Worker let mut pcm_info = default_pcm_info; 499*bb4ee6a4SAndroid Build Coastguard Worker pcm_info.hdr.hda_fn_nid = 0.into(); 500*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!( 501*bb4ee6a4SAndroid Build Coastguard Worker params.get_device_params(&pcm_info), 502*bb4ee6a4SAndroid Build Coastguard Worker Ok(params.input_device_config[0].clone()) 503*bb4ee6a4SAndroid Build Coastguard Worker ); 504*bb4ee6a4SAndroid Build Coastguard Worker 505*bb4ee6a4SAndroid Build Coastguard Worker let mut pcm_info = default_pcm_info; 506*bb4ee6a4SAndroid Build Coastguard Worker pcm_info.hdr.hda_fn_nid = 1.into(); 507*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!( 508*bb4ee6a4SAndroid Build Coastguard Worker params.get_device_params(&pcm_info), 509*bb4ee6a4SAndroid Build Coastguard Worker Ok(params.input_device_config[1].clone()) 510*bb4ee6a4SAndroid Build Coastguard Worker ); 511*bb4ee6a4SAndroid Build Coastguard Worker 512*bb4ee6a4SAndroid Build Coastguard Worker let mut pcm_info = default_pcm_info; 513*bb4ee6a4SAndroid Build Coastguard Worker pcm_info.hdr.hda_fn_nid = 2.into(); 514*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!( 515*bb4ee6a4SAndroid Build Coastguard Worker params.get_device_params(&pcm_info), 516*bb4ee6a4SAndroid Build Coastguard Worker Err(Error::InvalidPCMDeviceConfigIndex(2)) 517*bb4ee6a4SAndroid Build Coastguard Worker ); 518*bb4ee6a4SAndroid Build Coastguard Worker } 519*bb4ee6a4SAndroid Build Coastguard Worker 520*bb4ee6a4SAndroid Build Coastguard Worker #[test] get_device_params_invalid_direction()521*bb4ee6a4SAndroid Build Coastguard Worker fn get_device_params_invalid_direction() { 522*bb4ee6a4SAndroid Build Coastguard Worker let params = Parameters::default(); 523*bb4ee6a4SAndroid Build Coastguard Worker 524*bb4ee6a4SAndroid Build Coastguard Worker let pcm_info = virtio_snd_pcm_info { 525*bb4ee6a4SAndroid Build Coastguard Worker hdr: virtio_snd_info { 526*bb4ee6a4SAndroid Build Coastguard Worker hda_fn_nid: 0.into(), 527*bb4ee6a4SAndroid Build Coastguard Worker }, 528*bb4ee6a4SAndroid Build Coastguard Worker features: 0.into(), 529*bb4ee6a4SAndroid Build Coastguard Worker formats: 0.into(), 530*bb4ee6a4SAndroid Build Coastguard Worker rates: 0.into(), 531*bb4ee6a4SAndroid Build Coastguard Worker direction: 2, // Invalid direction 532*bb4ee6a4SAndroid Build Coastguard Worker channels_min: 1, 533*bb4ee6a4SAndroid Build Coastguard Worker channels_max: 6, 534*bb4ee6a4SAndroid Build Coastguard Worker padding: [0; 5], 535*bb4ee6a4SAndroid Build Coastguard Worker }; 536*bb4ee6a4SAndroid Build Coastguard Worker 537*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!( 538*bb4ee6a4SAndroid Build Coastguard Worker params.get_device_params(&pcm_info), 539*bb4ee6a4SAndroid Build Coastguard Worker Err(Error::InvalidPCMInfoDirection(2)) 540*bb4ee6a4SAndroid Build Coastguard Worker ); 541*bb4ee6a4SAndroid Build Coastguard Worker } 542*bb4ee6a4SAndroid Build Coastguard Worker } 543