1 #!/usr/bin/env python3.4 2 # 3 # Copyright 2016 - Google 4 # 5 # Licensed under the Apache License, Version 2.0 (the "License"); 6 # you may not use this file except in compliance with the License. 7 # You may obtain a copy of the License at 8 # 9 # http://www.apache.org/licenses/LICENSE-2.0 10 # 11 # Unless required by applicable law or agreed to in writing, software 12 # distributed under the License is distributed on an "AS IS" BASIS, 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 # See the License for the specific language governing permissions and 15 # limitations under the License. 16 17 # Defines utilities that can be used for making calls indenpendent of 18 # subscription IDs. This can be useful when making calls over mediums not SIM 19 # based. 20 21 # Make a phone call to the specified URI. It is assumed that we are making the 22 # call to the user selected default account. 23 # 24 # We usually want to make sure that the call has ended up in a good state. 25 # 26 # NOTE: This util is applicable to only non-conference type calls. It is best 27 # suited to test cases where only one call is in action at any point of time. 28 29 import queue 30 import time 31 32 from acts import logger 33 from acts.test_utils.tel import tel_defines 34 35 def dial_number(log, ad, uri): 36 """Dial a number 37 38 Args: 39 log: log object 40 ad: android device object 41 uri: Tel number to dial 42 43 Returns: 44 True if success, False if fail. 45 """ 46 log.info("Dialing up droid {} call uri {}".format( 47 ad.serial, uri)) 48 49 # First check that we are not in call. 50 if ad.droid.telecomIsInCall(): 51 log.info("We're still in call {}".format(ad.serial)) 52 return False 53 54 # Start tracking updates. 55 ad.droid.telecomStartListeningForCallAdded() 56 #If a phone number is passed in 57 if "tel:" not in uri: 58 uri = "tel:" + uri 59 ad.droid.telecomCallTelUri(uri) 60 61 event = None 62 try: 63 event = ad.ed.pop_event( 64 tel_defines.EventTelecomCallAdded, 65 tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE) 66 except queue.Empty: 67 log.info( 68 "Did not get {} event!".format(tel_defines.EventTelecomCallAdded)) 69 # Return failure. 70 return False 71 finally: 72 ad.droid.telecomStopListeningForCallAdded() 73 74 call_id = event['data']['CallId'] 75 log.info("Call ID: {} dev {}".format(call_id, ad.serial)) 76 77 if not call_id: 78 log.info("CallId is empty!") 79 return False 80 if not wait_for_dialing(log, ad): 81 return False 82 83 return call_id 84 85 def wait_for_call_state(log, ad, call_id, state): 86 """Wait for the given call id to transition to the given call state. 87 88 Args: 89 log: log object 90 ad: android device object 91 call_id: ID of the call that we're waiting for the call state to 92 transition into. 93 state: desired final state. 94 95 Returns: 96 True if success, False if fail. 97 """ 98 # Lets track the call now. 99 # NOTE: Disable this everywhere we return. 100 ad.droid.telecomCallStartListeningForEvent( 101 call_id, tel_defines.EVENT_CALL_STATE_CHANGED) 102 103 # We may have missed the update so do a quick check. 104 if ad.droid.telecomCallGetCallState(call_id) == state: 105 log.info("Call ID {} already in {} dev {}!".format( 106 call_id, state, ad.serial)) 107 ad.droid.telecomCallStopListeningForEvent(call_id, 108 tel_defines.EventTelecomCallStateChanged) 109 return True 110 111 # If not then we need to poll for the event. 112 # We return if we have found a match or we timeout for further updates of 113 # the call 114 end_time = time.time() + 10 115 while True and time.time() < end_time: 116 try: 117 event = ad.ed.pop_event( 118 tel_defines.EventTelecomCallStateChanged, 119 tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE) 120 call_state = event['data']['Event'] 121 if call_state == state: 122 ad.droid.telecomCallStopListeningForEvent(call_id, 123 tel_defines.EventTelecomCallStateChanged) 124 return True 125 else: 126 log.info("Droid {} in call {} state {}".format( 127 ad.serial, call_id, call_state)) 128 continue 129 except queue.Empty: 130 log.info("Did not get into state {} dev {}".format( 131 state, ad.serial)) 132 ad.droid.telecomCallStopListeningForEvent(call_id, 133 tel_defines.EventTelecomCallStateChanged) 134 return False 135 return False 136 137 def hangup_call(log, ad, call_id): 138 """Hangup a number 139 140 Args: 141 log: log object 142 ad: android device object 143 call_id: Call to hangup. 144 145 Returns: 146 True if success, False if fail. 147 """ 148 log.info("Hanging up droid {} call {}".format( 149 ad.serial, call_id)) 150 # First check that we are in call, otherwise fail. 151 if not ad.droid.telecomIsInCall(): 152 log.info("We are not in-call {}".format(ad.serial)) 153 return False 154 155 # Make sure we are registered with the events. 156 ad.droid.telecomStartListeningForCallRemoved() 157 158 # Disconnect call. 159 ad.droid.telecomCallDisconnect(call_id) 160 161 # Wait for removed event. 162 event = None 163 try: 164 event = ad.ed.pop_event( 165 tel_defines.EventTelecomCallRemoved, 166 tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE) 167 except queue.Empty: 168 log.info("Did not get TelecomCallRemoved event") 169 return False 170 finally: 171 ad.droid.telecomStopListeningForCallRemoved() 172 173 log.info("Removed call {}".format(event)) 174 if event['data']['CallId'] != call_id: 175 return False 176 177 return True 178 179 def hangup_conf(log, ad, conf_id): 180 """Hangup a conference call 181 182 Args: 183 log: log object 184 ad: android device object 185 conf_id: Conf call to hangup. 186 187 Returns: 188 True if success, False if fail. 189 """ 190 log.info("hangup_conf: Hanging up droid {} call {}".format( 191 ad.serial, conf_id)) 192 193 # First check that we are in call, otherwise fail. 194 if not ad.droid.telecomIsInCall(): 195 log.info("We are not in-call {}".format(ad.serial)) 196 return False 197 198 # Get the list of children for this conference. 199 all_calls = get_call_id_children(log, ad, conf_id) 200 201 # All calls that needs disconnecting (Parent + Children) 202 all_calls.add(conf_id) 203 204 # Make sure we are registered with the events. 205 ad.droid.telecomStartListeningForCallRemoved() 206 207 # Disconnect call. 208 ad.droid.telecomCallDisconnect(conf_id) 209 210 # Wait for removed event. 211 while len(all_calls) > 0: 212 event = None 213 try: 214 event = ad.ed.pop_event( 215 tel_defines.EventTelecomCallRemoved, 216 tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE) 217 except queue.Empty: 218 log.info("Did not get TelecomCallRemoved event") 219 ad.droid.telecomStopListeningForCallRemoved() 220 return False 221 222 removed_call_id = event['data']['CallId'] 223 all_calls.remove(removed_call_id) 224 log.info("Removed call {} left calls {}".format(removed_call_id, all_calls)) 225 226 ad.droid.telecomStopListeningForCallRemoved() 227 return True 228 229 def accept_call(log, ad, call_id): 230 """Accept a number 231 232 Args: 233 log: log object 234 ad: android device object 235 call_id: Call to accept. 236 237 Returns: 238 True if success, False if fail. 239 """ 240 log.info("Accepting call at droid {} call {}".format( 241 ad.serial, call_id)) 242 # First check we are in call, otherwise fail. 243 if not ad.droid.telecomIsInCall(): 244 log.info("We are not in-call {}".format(ad.serial)) 245 return False 246 247 # Accept the call and wait for the call to be accepted on AG. 248 ad.droid.telecomCallAnswer(call_id, tel_defines.VT_STATE_AUDIO_ONLY) 249 if not wait_for_call_state(log, ad, call_id, tel_defines.CALL_STATE_ACTIVE): 250 log.error("Call {} on droid {} not active".format( 251 call_id, ad.serial)) 252 return False 253 254 return True 255 256 def wait_for_not_in_call(log, ad): 257 """Wait for the droid to be OUT OF CALLING state. 258 259 Args: 260 log: log object 261 ad: android device object 262 263 Returns: 264 True if success, False if fail. 265 """ 266 ad.droid.telecomStartListeningForCallRemoved() 267 268 calls = ad.droid.telecomCallGetCallIds() 269 if len(calls) > 1: 270 log.info("More than one call {} {}".format(calls, ad.serial)) 271 return False 272 273 if len(calls) > 0: 274 log.info("Got calls {} for {}".format( 275 calls, ad.serial)) 276 try: 277 event = ad.ed.pop_event( 278 tel_defines.EventTelecomCallRemoved, 279 tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE) 280 except queue.Empty: 281 log.info("wait_for_not_in_call Did not get {} droid {}".format( 282 tel_defines.EventTelecomCallRemoved, 283 ad.serial)) 284 return False 285 finally: 286 ad.droid.telecomStopListeningForCallRemoved() 287 288 # Either we removed the only call or we never had a call previously, either 289 # ways simply check if we are in in call now. 290 return (not ad.droid.telecomIsInCall()) 291 292 def wait_for_dialing(log, ad): 293 """Wait for the droid to be in dialing state. 294 295 Args: 296 log: log object 297 ad: android device object 298 299 Returns: 300 True if success, False if fail. 301 """ 302 # Start listening for events before anything else happens. 303 ad.droid.telecomStartListeningForCallAdded() 304 305 # First check if we re in call, then simply return. 306 if ad.droid.telecomIsInCall(): 307 ad.droid.telecomStopListeningForCallAdded() 308 return True 309 310 call_id = None 311 # Now check if we already have calls matching the state. 312 calls_in_state = get_calls_in_states(log, ad, 313 [tel_defines.CALL_STATE_CONNECTING, 314 tel_defines.CALL_STATE_DIALING]) 315 316 # If not then we need to poll for the calls themselves. 317 if len(calls_in_state) == 0: 318 event = None 319 try: 320 event = ad.ed.pop_event( 321 tel_defines.EventTelecomCallAdded, 322 tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE) 323 except queue.Empty: 324 log.info("Did not get {}".format( 325 tel_defines.EventTelecomCallAdded)) 326 return False 327 finally: 328 ad.droid.telecomStopListeningForCallAdded() 329 call_id = event['data']['CallId'] 330 else: 331 call_id = calls_in_state[0] 332 333 # We may still not be in-call if the call setup is going on. 334 # We wait for the call state to move to dialing. 335 log.info("call id {} droid {}".format(call_id, ad.serial)) 336 if not wait_for_call_state( 337 log, ad, call_id, tel_defines.CALL_STATE_DIALING): 338 return False 339 340 # Finally check the call state. 341 return ad.droid.telecomIsInCall() 342 343 def wait_for_ringing(log, ad): 344 """Wait for the droid to be in ringing state. 345 346 Args: 347 log: log object 348 ad: android device object 349 350 Returns: 351 True if success, False if fail. 352 """ 353 log.info("waiting for ringing {}".format(ad.serial)) 354 # Start listening for events before anything else happens. 355 ad.droid.telecomStartListeningForCallAdded() 356 357 # First check if we re in call, then simply return. 358 if ad.droid.telecomIsInCall(): 359 log.info("Device already in call {}".format(ad.serial)) 360 ad.droid.telecomStopListeningForCallAdded() 361 return True 362 363 call_id = None 364 # Now check if we already have calls matching the state. 365 calls_in_state = ad.droid.telecomCallGetCallIds() 366 367 for c_id in calls_in_state: 368 if ad.droid.telecomCallGetCallState(c_id) == tel_defines.CALL_STATE_RINGING: 369 return True 370 371 event = None 372 call_id = None 373 try: 374 event = ad.ed.pop_event( 375 tel_defines.EventTelecomCallAdded, 376 tel_defines.MAX_WAIT_TIME_CALLEE_RINGING) 377 except queue.Empty: 378 log.info("Did not get {} droid {}".format( 379 tel_defines.EventTelecomCallAdded, 380 ad.serial)) 381 return False 382 finally: 383 ad.droid.telecomStopListeningForCallAdded() 384 call_id = event['data']['CallId'] 385 log.info("wait_for_ringing call found {} dev {}".format( 386 call_id, ad.serial)) 387 388 # If the call already existed then we would have returned above otherwise 389 # we will verify that the newly added call is indeed ringing. 390 if not wait_for_call_state( 391 log, ad, call_id, tel_defines.CALL_STATE_RINGING): 392 log.info("No ringing call id {} droid {}".format( 393 call_id, ad.serial)) 394 return False 395 return True 396 397 def wait_for_active(log, ad): 398 """Wait for the droid to be in active call state. 399 400 Args: 401 log: log object 402 ad: android device object 403 404 Returns: 405 True if success, False if fail. 406 """ 407 log.info("waiting for active {}".format(ad.serial)) 408 # Start listening for events before anything else happens. 409 ad.droid.telecomStartListeningForCallAdded() 410 411 call_id = None 412 # Now check if we already have calls matching the state. 413 calls_in_state = ad.droid.telecomCallGetCallIds() 414 415 if len(calls_in_state) == 0: 416 event = None 417 try: 418 event = ad.ed.pop_event( 419 tel_defines.EventTelecomCallAdded, 420 tel_defines.MAX_WAIT_TIME_CALLEE_RINGING) 421 except queue.Empty: 422 log.info("Did not get {} droid {}".format( 423 tel_defines.EventTelecomCallAdded, 424 ad.serial)) 425 return False 426 finally: 427 ad.droid.telecomStopListeningForCallAdded() 428 call_id = event['data']['CallId'] 429 log.info("wait_for_ringing call found {} dev {}".format( 430 call_id, ad.serial)) 431 else: 432 call_id = calls_in_state[0] 433 434 # We have found a new call to be added now wait it to transition into 435 # active state. 436 if not wait_for_call_state( 437 log, ad, call_id, tel_defines.CALL_STATE_ACTIVE): 438 log.info("No active call id {} droid {}".format( 439 call_id, ad.serial)) 440 return False 441 return True 442 443 def wait_for_conference(log, ad, conf_calls): 444 """Wait for the droid to be in a conference with calls specified 445 in conf_calls. 446 447 Args: 448 log: log object 449 ad: android device object 450 conf_calls: List of calls that should transition to conference 451 452 Returns: 453 call_id if success, None if fail. 454 """ 455 conf_calls = set(conf_calls) 456 457 log.info("waiting for conference {}".format(ad.serial)) 458 ad.droid.telecomStartListeningForCallAdded() 459 460 call_ids = ad.droid.telecomCallGetCallIds() 461 462 # Check if we have a conference call and if the children match 463 for call_id in call_ids: 464 call_chld = get_call_id_children(log, ad, call_id) 465 if call_chld == conf_calls: 466 ad.droid.telecomStopListeningForCallAdded() 467 return call_id 468 469 # If not poll for new calls. 470 event = None 471 call_id = None 472 try: 473 event = ad.ed.pop_event( 474 tel_defines.EventTelecomCallAdded, 475 tel_defines.MAX_WAIT_TIME_CALLEE_RINGING) 476 log.info("wait_for_conference event {} droid {}".format( 477 event, ad.serial)) 478 except queue.Empty: 479 log.error("Did not get {} droid {}".format( 480 tel_defines.EventTelecomCallAdded, 481 ad.serial)) 482 return None 483 finally: 484 ad.droid.telecomStopListeningForCallAdded() 485 call_id = event['data']['CallId'] 486 487 # Now poll until the children change. 488 ad.droid.telecomCallStartListeningForEvent( 489 call_id, tel_defines.EVENT_CALL_CHILDREN_CHANGED) 490 491 event = None 492 while True: 493 try: 494 event = ad.ed.pop_event( 495 tel_defines.EventTelecomCallChildrenChanged, 496 tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE) 497 call_chld = set(event['data']['Event']) 498 log.info("wait_for_conference children chld event {}".format(call_chld)) 499 if call_chld == conf_calls: 500 ad.droid.telecomCallStopListeningForEvent( 501 call_id, tel_defines.EVENT_CALL_CHILDREN_CHANGED) 502 return call_id 503 except queue.Empty: 504 log.error("Did not get {} droid {}".format( 505 tel_defines.EventTelecomCallChildrenChanged, ad.serial)) 506 ad.droid.telecomCallStopListeningForEvent( 507 call_id, tel_defines.EVENT_CALL_CHILDREN_CHANGED) 508 return None 509 510 def get_call_id_children(log, ad, call_id): 511 """Return the list of calls that are children to call_id 512 513 Args: 514 log: log object 515 ad: android device object 516 call_id: Conference call id 517 518 Returns: 519 List containing call_ids. 520 """ 521 call = ad.droid.telecomCallGetCallById(call_id) 522 call_chld = set(call['Children']) 523 log.info("get_call_id_children droid {} call {} children {}".format( 524 ad.serial, call, call_chld)) 525 return call_chld 526 527 def get_calls_in_states(log, ad, call_states): 528 """Return the list of calls that are any of the states passed in call_states 529 530 Args: 531 log: log object 532 ad: android device object 533 call_states: List of desired call states 534 535 Returns: 536 List containing call_ids. 537 """ 538 # Get the list of calls. 539 call_ids = ad.droid.telecomCallGetCallIds() 540 call_in_state = [] 541 for call_id in call_ids: 542 call = ad.droid.telecomCallGetCallById(call_id) 543 log.info("Call id: {} desc: {}".format(call_id, call)) 544 if call['State'] in call_states: 545 log.info("Adding call id {} to result set.".format(call_id)) 546 call_in_state.append(call_id) 547 return call_in_state 548