Home | History | Annotate | Download | only in car
      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