1# Copyrigh The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5"""Get speaker/microphone status from cras_client_test, /proc/asound and 6 atrus.log. 7""" 8 9from __future__ import print_function 10 11import logging 12import re 13 14 15NUM_AUDIO_STREAM_IN_MEETING = 3 16 17def get_soundcard_by_name(dut, name): 18 """ 19 Returns the soundcard number of specified soundcard by name. 20 @param dut: The handle of the device under test. 21 @param name: The name of Speaker 22 For example: 'Hangouts Meet speakermic' 23 @returns the soundcard, if no device found returns None. 24 """ 25 soundcard = None 26 cmd = "cat /proc/asound/cards | grep \"{}\" | grep USB".format(name) 27 logging.info('---cmd: %s', cmd) 28 try: 29 soundcard = dut.run(cmd, ignore_status=True).stdout.strip().split()[0] 30 except Exception as e: 31 soundcard = None 32 logging.exception('Fail to execute %s.', cmd) 33 if soundcard: 34 soundcard = "card{}".format(soundcard) 35 logging.info('---audio card %s', soundcard) 36 else: 37 logging.exception('Fail to get sound card, cli=%s.', cmd) 38 return soundcard 39 40def check_soundcard_by_name(dut, name): 41 """ 42 check soundcard by name exists 43 @param dut: The handle of the device under test. 44 @param name: The name of Speaker 45 For example: 'Hangouts Meet speakermic' 46 @returns: True, None if test passes, 47 False, errMsg if test fails 48 """ 49 if get_soundcard_by_name(dut, name): 50 return True, None 51 else: 52 return False, 'Soundcard is not found under /proc/asound/cards.' 53 54def check_audio_stream(dut, is_in_meeting): 55 """ 56 Verify speaker is streaming or not streaming as expected. 57 @param dut: The handle of the device under test. 58 @is_in_meeting: True if CfM is in meeting, False, if not 59 @returns: True, None if test passes, 60 False, errMsg if test fails 61 """ 62 number_stream = get_number_of_active_streams(dut) 63 if is_in_meeting: 64 if number_stream >= NUM_AUDIO_STREAM_IN_MEETING: 65 return True, None 66 else: 67 return False, 'Number of Audio streams is not expected.' 68 else: 69 if number_stream <= NUM_AUDIO_STREAM_IN_MEETING: 70 return True, None 71 else: 72 return False, 'Number of Audio streams is not expected.' 73 74def get_audio_stream_state(dut, soundcard): 75 """ 76 Returns the state of stream0 for specified soundcard. 77 78 @param dut: The handle of the device under test. Should be initialized in 79 autotest. 80 @param soundcard: soundcard 81 For example: 'card0' 82 83 @returns the list of state of steam0, "Running" or "Stop" 84 85 """ 86 stream_state = [] 87 try: 88 cmd = ("cat /proc/asound/%s/stream0 | grep Status | " 89 "awk -v N=2 '{print $N}'" % soundcard) 90 stream_state = dut.run(cmd, ignore_status=True).stdout.split() 91 except Exception as e: 92 logging.exception('Fail to run cli: %s.', cmd) 93 return stream_state 94 95 96def check_default_speaker_volume(dut, cfm_facade): 97 """Check volume of speaker is the same as expected. 98 @param dut: The handle of the device under test. 99 @param cfm_facade: the handle of cfm facade 100 @returns True, None if default speakers have same volume as one read 101 from hotrod, 102 False, errMsg, otherwise 103 """ 104 try: 105 expected_volume = int(cfm_facade.get_speaker_volume()) 106 except Exception as e: 107 errmsg = 'Fail to run telemetry api to get speaker volume.' 108 logging.exception(errmsg) 109 return False, errmsg 110 if expected_volume < 1: 111 return False, 'Fail to get speaker volume from Hotrod.' 112 nodes = get_nodes_for_default_speakers_cras(dut) 113 if not nodes: 114 logging.info('---Fail to get node for default speaker.') 115 return False, 'Fail to get node for default speaker.' 116 for node in nodes: 117 cras_volume = get_speaker_volume_cras(dut, node) 118 logging.info('---Volume for default speaker are sync for ' 119 'node %s? cfm: %d, cras: %d.' 120 'format(node, expected_volume, cras_volume)') 121 if not expected_volume == cras_volume: 122 logging.info('---Volume Check Fail for default speaker: ' 123 'expected_volume:%d, actual_volume:%d.' 124 'format(expected_volume, cras_volume)') 125 return False, ('Volume Check fails for default speaker: ' 126 'expected_volume:%d, actual_volume:%d', 127 '% expected_volume, cras_volume') 128 logging.info('---Expected volume: %d, actual: %d', 129 expected_volume, cras_volume) 130 return True, None 131 132def get_number_of_active_streams(dut): 133 """ 134 Returns the number of active stream. 135 @param dut: The handle of the device under test. Should be initialized in 136 autotest. 137 @returns the number of active streams. 138 """ 139 cmd = ("cras_test_client --dump_server_info " 140 "| grep 'Num active streams:' " 141 "| awk -v N=4 '{print $N}'") 142 143 try: 144 number_of_streams = int(dut.run(cmd, ignore_status=True).stdout.strip()) 145 except Exception as e: 146 logging.exception('Fail to execute cli to get number of streams: %s.', 147 cmd) 148 return None 149 logging.info('---number of audio streaming: %d', number_of_streams) 150 return number_of_streams 151 152 153def get_nodes_for_default_speakers_cras(dut): 154 """get node for default speakers from cras_test_client. 155 @param dut: The handle of the device under test. Should be initialized in 156 autotest. 157 @returns the list of nodes for default speakers. If device not found, 158 returns []. 159 """ 160 nodes = [] 161 cmd = ("cras_test_client --dump_server_info | awk '/Output Nodes:/," 162 "/Input Devices:/'") 163 try: 164 lines = dut.run(cmd, ignore_status=True).stdout.splitlines() 165 except Exception as e: 166 logging.exception('Fail to execute cli to get nodes for default' 167 'speaker: %s', cmd) 168 return nodes 169 for _line in lines: 170 match = re.findall(r"(\d+):\d+.*USB\s+\*.*", _line) 171 if match: 172 nodes.append(match[0]) 173 logging.info('---found nodes for default speaker %s', nodes) 174 return nodes 175 176 177def get_speaker_for_node_cras(dut, node): 178 """get node for default speakers from cras_test_client. 179 @param dut: The handle of the device under test. Should be initialized in 180 autotest. 181 182 @returns the list of nodes for default speakers. If device not found, 183 returns []. 184 """ 185 cmd = ("cras_test_client --dump_server_info | awk '/Output Devices:/," 186 "/Output Nodes:/' | grep '%s'" % node) 187 188 try: 189 line = dut.run(cmd, ignore_status=True).stdout.stripe() 190 speaker = re.findall(r"^[0-9]+\s*(.*):\s+USB\s+Audio:", line)[0] 191 except Exception as e: 192 logging.exception('Fail to execute cli to get nodes for default' 193 'speaker: %s.', cmd) 194 195 logging.info('---speaker for %s is %s', node, speaker) 196 return speaker 197 198 199def get_nodes_for_default_microphone_cras(dut): 200 """get node for default microphones from cras_test_client. 201 @param dut: The handle of the device under test. Should be initialized in 202 autotest. 203 204 @returns the list of nodes for default microphone. If device not found, 205 returns []. 206 """ 207 nodes = None 208 cmd = ("cras_test_client --dump_server_info | awk '/Input Nodes:/," 209 "/Attached clients:/'") 210 try: 211 lines = dut.run(cmd, ignore_status=True).stdout.splitlines() 212 for _line in lines: 213 nodes.append(re.findall(r"(\d+):\d+.*USB\s+\*.*", _line)[0]) 214 except Exception as e: 215 logging.exception('Fail to execute cli to get nodes for default' 216 ' speaker: %s.', cmd) 217 return nodes 218 219 220def get_microphone_for_node_cras(dut, node): 221 """get node for default speakers from cras_test_client. 222 @param dut: The handle of the device under test. Should be initialized in 223 autotest. 224 225 @returns the list of nodes for default speakers. If device not found, 226 returns []. 227 """ 228 cmd = ("cras_test_client --dump_server_info | awk '/Input Devices:/," 229 "/Input Nodes:/' | grep '%s' " % node) 230 231 try: 232 line = dut.run(cmd, ignore_status=True).stdout 233 microphone = re.findall(r"10\t(.*):\s+USB\s+Audio:", line)[0] 234 except Exception as e: 235 logging.exception('Fail to execute cli to get nodes for default' 236 ' speaker: %s.', cmd) 237 logging.info('---mic for %s is %s', node, microphone) 238 return microphone 239 240 241def get_speaker_volume_cras(dut, node): 242 """get volume for speaker from cras_test_client based on node 243 @param dut: The handle of the device under test. Should be initialized in 244 autotest. 245 @param node: The node of Speaker 246 Example cli: 247 cras_test_client --dump_server_info | awk 248 '/Output Nodes:/,/Input Devices:/' | grep 9:0 | 249 awk -v N=3 '{print $N}' 250 251 @returns the volume of speaker. If device not found, returns None. 252 """ 253 cmd = ("cras_test_client --dump_server_info | awk '/Output Nodes:/," 254 "/Input Devices:/' | grep -E 'USB' | grep '%s':0 " 255 "| awk -v N=3 '{print $N}'" % node) 256 try: 257 volume = int(dut.run(cmd, ignore_status=True).stdout.strip()) 258 except Exception as e: 259 logging.exception('Fail to execute cli %s to get volume for node %d', 260 cmd, node) 261 return None 262 return volume 263 264 265def check_cras_mic_mute(dut, cfm_facade): 266 """ 267 check microphone is muted or unmuted as expected/. 268 @param dut: The handle of the device under test. 269 @param cfm_facade: facade of CfM 270 @param is_mic_muted: True if muted, False otherwise 271 @returns True, none if test passes 272 False, errMsg if test fails 273 """ 274 try: 275 is_mic_muted = cfm_facade.is_mic_muted() 276 except Exception as e: 277 errmsg = 'Fail to run telemetry api to check mute state for mic.' 278 logging.exception(errmsg) 279 return False, errmsg 280 actual_muted = get_mic_muted_cras(dut) 281 if is_mic_muted == actual_muted: 282 return True, None 283 else: 284 if is_mic_muted: 285 logging.info('Hotrod setting: microphone is muted') 286 else: 287 logging.info('Hotrod setting: microphone is not muted') 288 if actual_muted: 289 logging.info('cras setting: microphone is muted') 290 else: 291 logging.info('cras setting: microphone is not muted') 292 return False, 'Microphone is not muted/unmuted as shown in Hotrod.' 293 294def check_is_preferred_speaker(dut, name): 295 """ 296 check preferred speaker is speaker to be tested. 297 @param dut: The handle of the device under test. 298 @param cfm_facade: facade of CfM 299 @param name: name of speaker 300 @returns True, none if test passes 301 False, errMsg if test fails 302 """ 303 node = None 304 cmd = ("cras_test_client --dump_server_info | awk " 305 "'/Output Devices:/,/Output Nodes:/' " 306 "| grep '%s' " % name) 307 try: 308 output = dut.run(cmd, ignore_status=True).stdout.strip() 309 except Exception as e: 310 logging.exception('Fail to run cli %s to find %s in cras_test_client.', 311 cmd, node) 312 return False, 'Fail to run cli {}:, reason: {}'.format(cmd, str(e)) 313 logging.info('---output = %s', output) 314 if output: 315 node = output.split()[0] 316 logging.info('---found node for %s is %s', name, node) 317 else: 318 return False, 'Fail in get node for speaker in cras.' 319 default_nodes = get_nodes_for_default_speakers_cras(dut) 320 logging.info('---default speaker node is %s', default_nodes) 321 if node in default_nodes: 322 return True, None 323 return False, '{} is not set to preferred speaker.'.format(name) 324 325 326def check_is_preferred_mic(dut, name): 327 """check preferred mic is set to speaker to be tested.""" 328 cmd = ("cras_test_client --dump_server_info | " 329 "awk '/Input Devices/,/Input Nodes/' | grep '%s' | " 330 "awk -v N=1 '{print $N}'" % name) 331 logging.info('---cmd = %s',cmd) 332 try: 333 mic_node = dut.run(cmd, ignore_status=True).stdout.strip() 334 logging.info('---mic_node : %s', mic_node) 335 except Exception as e: 336 logging.exception('Fail to execute: %s to check preferred mic.', cmd) 337 return False, 'Fail to run cli.' 338 try: 339 cmd = ("cras_test_client --dump_server_info | awk '/Input Nodes:/," 340 "/Attached clients:/' | grep default " 341 "| awk -v N=2 '{print $N}'") 342 mic_node_default = dut.run(cmd, ignore_status=True).stdout.strip() 343 if not mic_node_default: 344 cmd = ("cras_test_client --dump_server_info | awk '/Input Nodes:/," 345 "/Attached clients:/' | grep '%s' " 346 "| awk -v N=2 '{print $N}'" %name) 347 mic_node_default = dut.run(cmd,ignore_status=True).stdout.strip() 348 logging.info('---%s',cmd) 349 logging.info('---%s', mic_node_default) 350 except Exception as e: 351 msg = 'Fail to execute: {} to check preferred mic'.format(cmd) 352 logging.exception(msg) 353 return False, msg 354 logging.info('---mic node:%s, default node:%s', 355 mic_node, mic_node_default) 356 if mic_node == mic_node_default.split(':')[0]: 357 return True, None 358 return False, '{} is not preferred microphone.'.format(name) 359 360 361def get_mic_muted_cras(dut): 362 """ 363 Get the status of mute or unmute for microphone 364 @param dut: the handle of CfM under test 365 @returns True if mic is muted 366 False if mic not not muted 367 """ 368 cmd = 'cras_test_client --dump_server_info | grep "Capture Gain"' 369 try: 370 microphone_muted = dut.run(cmd, ignore_status=True).stdout.strip() 371 except Exception as e: 372 logging.exception('Fail to execute: %s.', cmd) 373 return False, 'Fail to execute: {}.'.format(cmd) 374 logging.info('---%s', microphone_muted) 375 if "Muted" in microphone_muted: 376 return True 377 else: 378 return False 379 380 381def check_speaker_exist_cras(dut, name): 382 """ 383 Check speaker exists in cras. 384 @param dut: The handle of the device under test. 385 @param name: name of speaker 386 @returns: True, None if test passes, 387 False, errMsg if test fails 388 """ 389 cmd = ("cras_test_client --dump_server_info | awk " 390 "'/Output Devices:/, /Output Nodes:/' " 391 "| grep '%s'" % name) 392 try: 393 speaker = dut.run(cmd, ignore_status=True).stdout.splitlines()[0] 394 except Exception as e: 395 logging.exception('Fail to find %s in cras_test_client running %s.', 396 name, cmd) 397 speaker = None 398 logging.info('---cmd: %s\n---output = %s', cmd, speaker) 399 if speaker: 400 return True, None 401 return False, 'Fail to execute cli {}: Reason: {}'.format(cmd, str(e)) 402 403 404def check_microphone_exist_cras(dut, name): 405 """ 406 Check microphone exists in cras. 407 @param dut: The handle of the device under test. 408 @param name: name of speaker 409 @returns: True, None if test passes, 410 False, errMsg if test fails 411 """ 412 microphone = None 413 cmd = ("cras_test_client --dump_server_info | awk " 414 "'/Input Devices:/, /Input Nodes:/' " 415 "| grep '%s'" % name ) 416 try: 417 microphone = dut.run(cmd, ignore_status=True).stdout.splitlines()[0] 418 except Exception as e: 419 logging.exception('Fail to execute cli %s.', cmd) 420 logging.info('---cmd: %s\n---output = %s', cmd, microphone) 421 if microphone: 422 return True, None 423 return False, 'Fail to find microphone {}.'.format(name) 424 425def check_audio_stream(dut, is_in_meet): 426 """ 427 Verify speaker is streaming or not streaming as expected. 428 @param dut: The handle of the device under test. 429 @is_in_meeting: True if CfM is in meeting, False, if not 430 @returns: True, None if test passes, 431 False, errMsg if test fails 432 """ 433 number_stream = get_number_of_active_streams(dut) 434 if is_in_meet: 435 if number_stream >= NUM_AUDIO_STREAM_IN_MEETING: 436 return True, None 437 else: 438 return False, 'Number of Audio streams is not expected.' 439 else: 440 if number_stream <= NUM_AUDIO_STREAM_IN_MEETING: 441 return True, None 442 else: 443 return False, 'Number of Audio streams is not expected.' 444 445