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 29import queue 30import time 31 32from acts import logger 33from acts.test_utils.tel import tel_defines 34 35def 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 85def 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 137def 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 179def 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 229def 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 256def 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 292def 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 343def 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 397def 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 443def 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 510def 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 527def 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