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
17import time
18from queue import Empty
19from acts.test_utils.tel.tel_defines import AUDIO_ROUTE_EARPIECE
20from acts.test_utils.tel.tel_defines import CALL_STATE_RINGING
21from acts.test_utils.tel.tel_defines import INCALL_UI_DISPLAY_BACKGROUND
22from acts.test_utils.tel.tel_defines import INCALL_UI_DISPLAY_FOREGROUND
23from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_ACCEPT_CALL_TO_OFFHOOK_EVENT
24from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_CALL_INITIATION
25from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_CALLEE_RINGING
26from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_NW_SELECTION
27from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_TELECOM_RINGING
28from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_VIDEO_SESSION_EVENT
29from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_VOLTE_ENABLED
30from acts.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
31from acts.test_utils.tel.tel_defines import NETWORK_SERVICE_VOICE
32from acts.test_utils.tel.tel_defines import GEN_4G
33from acts.test_utils.tel.tel_defines import RAT_1XRTT
34from acts.test_utils.tel.tel_defines import RAT_IWLAN
35from acts.test_utils.tel.tel_defines import RAT_LTE
36from acts.test_utils.tel.tel_defines import RAT_UMTS
37from acts.test_utils.tel.tel_defines import TELEPHONY_STATE_OFFHOOK
38from acts.test_utils.tel.tel_defines import TELEPHONY_STATE_RINGING
39from acts.test_utils.tel.tel_defines import VT_STATE_AUDIO_ONLY
40from acts.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL
41from acts.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL_PAUSED
42from acts.test_utils.tel.tel_defines import VT_STATE_RX_ENABLED
43from acts.test_utils.tel.tel_defines import VT_STATE_RX_PAUSED
44from acts.test_utils.tel.tel_defines import VT_STATE_TX_ENABLED
45from acts.test_utils.tel.tel_defines import VT_STATE_TX_PAUSED
46from acts.test_utils.tel.tel_defines import VT_STATE_STATE_INVALID
47from acts.test_utils.tel.tel_defines import VT_VIDEO_QUALITY_DEFAULT
48from acts.test_utils.tel.tel_defines import WAIT_TIME_ACCEPT_VIDEO_CALL_TO_CHECK_STATE
49from acts.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
50from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
51from acts.test_utils.tel.tel_defines import WFC_MODE_DISABLED
52from acts.test_utils.tel.tel_defines import EventCallStateChanged
53from acts.test_utils.tel.tel_defines import EventTelecomVideoCallSessionModifyRequestReceived
54from acts.test_utils.tel.tel_defines import EventTelecomVideoCallSessionModifyResponseReceived
55from acts.test_utils.tel.tel_defines import EVENT_VIDEO_SESSION_MODIFY_RESPONSE_RECEIVED
56from acts.test_utils.tel.tel_defines import EVENT_VIDEO_SESSION_MODIFY_REQUEST_RECEIVED
57from acts.test_utils.tel.tel_defines import CallStateContainer
58from acts.test_utils.tel.tel_subscription_utils import get_outgoing_voice_sub_id
59from acts.test_utils.tel.tel_subscription_utils import get_incoming_voice_sub_id
60from acts.test_utils.tel.tel_test_utils import check_phone_number_match
61from acts.test_utils.tel.tel_test_utils import ensure_network_generation
62from acts.test_utils.tel.tel_test_utils import is_event_match
63from acts.test_utils.tel.tel_test_utils import hangup_call
64from acts.test_utils.tel.tel_test_utils import set_wfc_mode
65from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
66from acts.test_utils.tel.tel_test_utils import toggle_volte
67from acts.test_utils.tel.tel_test_utils import verify_incall_state
68from acts.test_utils.tel.tel_test_utils import wait_for_network_generation
69from acts.test_utils.tel.tel_test_utils import wait_for_network_rat_for_subscription
70from acts.test_utils.tel.tel_test_utils import wait_for_ringing_call
71from acts.test_utils.tel.tel_test_utils import wait_for_telecom_ringing
72from acts.test_utils.tel.tel_test_utils import wait_for_video_enabled
73from acts.test_utils.tel.tel_test_utils import get_network_rat
74from acts.test_utils.tel.tel_test_utils import is_wfc_enabled
75from acts.test_utils.tel.tel_voice_utils import is_call_hd
76
77
78def phone_setup_video(log, ad, wfc_mode=WFC_MODE_DISABLED):
79    """Setup phone default sub_id to make video call
80
81    Args:
82        log: log object.
83        ad: android device object
84        wfc_mode: WFC mode to set to.
85            Valid mode includes: WFC_MODE_WIFI_ONLY, WFC_MODE_CELLULAR_PREFERRED,
86            WFC_MODE_WIFI_PREFERRED, WFC_MODE_DISABLED.
87
88    Returns:
89        True if ad (default sub_id) is setup correctly and idle for video call.
90    """
91    return phone_setup_video_for_subscription(log, ad,
92                                              get_outgoing_voice_sub_id(ad),
93                                              wfc_mode)
94
95
96def phone_setup_video_for_subscription(log,
97                                       ad,
98                                       sub_id,
99                                       wfc_mode=WFC_MODE_DISABLED):
100    """Setup phone sub_id to make video call
101
102    Args:
103        log: log object.
104        ad: android device object
105        sub_id: ad's sub id.
106        wfc_mode: WFC mode to set to.
107            Valid mode includes: WFC_MODE_WIFI_ONLY, WFC_MODE_CELLULAR_PREFERRED,
108            WFC_MODE_WIFI_PREFERRED, WFC_MODE_DISABLED.
109
110    Returns:
111        True if ad (sub_id) is setup correctly and idle for video call.
112    """
113
114    toggle_airplane_mode(log, ad, False)
115    if not set_wfc_mode(log, ad, wfc_mode):
116        log.error("{} WFC mode failed to be set to {}.".format(
117            ad.serial, wfc_mode))
118        return False
119    toggle_volte(log, ad, True)
120
121    if not ensure_network_generation(
122            log, ad, GEN_4G, voice_or_data=NETWORK_SERVICE_DATA):
123        log.error("{} voice not in LTE mode.".format(ad.serial))
124        return False
125
126    return phone_idle_video_for_subscription(log, ad, sub_id)
127
128
129def phone_idle_video(log, ad):
130    """Return if phone (default sub_id) is idle for video call.
131
132    Args:
133        log: log object.
134        ad: android device object
135
136    Returns:
137        True if ad is idle for video call.
138    """
139    return phone_idle_video_for_subscription(log, ad,
140                                             get_outgoing_voice_sub_id(ad))
141
142
143def phone_idle_video_for_subscription(log, ad, sub_id):
144    """Return if phone (sub_id) is idle for video call.
145
146    Args:
147        log: log object.
148        ad: android device object
149        sub_id: ad's sub id
150
151    Returns:
152        True if ad (sub_id) is idle for video call.
153    """
154
155    if not wait_for_network_generation(log, ad, GEN_4G):
156        log.error("{} voice not in LTE mode.".format(ad.serial))
157        return False
158
159    if not wait_for_video_enabled(log, ad, MAX_WAIT_TIME_VOLTE_ENABLED):
160        log.error(
161            "{} failed to <report video calling enabled> within {}s.".format(
162                ad.serial, MAX_WAIT_TIME_VOLTE_ENABLED))
163        return False
164    return True
165
166
167def is_phone_in_call_video(log, ad):
168    """Return if ad is in a video call (in expected video state).
169
170    Args:
171        log: log object.
172        ad: android device object
173        video_state: Expected Video call state.
174            This is optional, if it's None,
175            then TX_ENABLED/RX_ENABLED/BIDIRECTIONAL video call state will
176            return True.
177
178    Returns:
179        True if ad (for sub_id) is in a video call (in expected video state).
180    """
181    return is_phone_in_call_video_for_subscription(
182        log, ad, get_outgoing_voice_sub_id(ad))
183
184
185def is_phone_in_call_video_for_subscription(log, ad, sub_id, video_state=None):
186    """Return if ad (for sub_id) is in a video call (in expected video state).
187    Args:
188        log: log object.
189        ad: android device object
190        sub_id: device sub_id
191        video_state: Expected Video call state.
192            This is optional, if it's None,
193            then TX_ENABLED/RX_ENABLED/BIDIRECTIONAL video call state will
194            return True.
195
196    Returns:
197        True if ad is in a video call (in expected video state).
198    """
199
200    if video_state is None:
201        log.info("Verify if {}(subid {}) in video call.".format(
202            ad.serial, sub_id))
203    if not ad.droid.telecomIsInCall():
204        log.error("{} not in call.".format(ad.serial))
205        return False
206    call_list = ad.droid.telecomCallGetCallIds()
207    for call in call_list:
208        state = ad.droid.telecomCallVideoGetState(call)
209        if video_state is None:
210            if {
211                    VT_STATE_AUDIO_ONLY: False,
212                    VT_STATE_TX_ENABLED: True,
213                    VT_STATE_TX_PAUSED: True,
214                    VT_STATE_RX_ENABLED: True,
215                    VT_STATE_RX_PAUSED: True,
216                    VT_STATE_BIDIRECTIONAL: True,
217                    VT_STATE_BIDIRECTIONAL_PAUSED: True,
218                    VT_STATE_STATE_INVALID: False
219            }[state]:
220                return True
221        else:
222            if state == video_state:
223                return True
224        log.info("Non-Video-State: {}".format(state))
225    log.error("Phone not in video call. Call list: {}".format(call_list))
226    return False
227
228
229def is_phone_in_call_viwifi_for_subscription(log, ad, sub_id,
230                                             video_state=None):
231    """Return if ad (for sub_id) is in a viwifi call (in expected video state).
232    Args:
233        log: log object.
234        ad: android device object
235        sub_id: device sub_id
236        video_state: Expected Video call state.
237            This is optional, if it's None,
238            then TX_ENABLED/RX_ENABLED/BIDIRECTIONAL video call state will
239            return True.
240
241    Returns:
242        True if ad is in a video call (in expected video state).
243    """
244
245    if video_state is None:
246        log.info("Verify if {}(subid {}) in video call.".format(
247            ad.serial, sub_id))
248    if not ad.droid.telecomIsInCall():
249        log.error("{} not in call.".format(ad.serial))
250        return False
251    nw_type = get_network_rat(log, ad, NETWORK_SERVICE_DATA)
252    if nw_type != RAT_IWLAN:
253        ad.log.error("Data rat on: %s. Expected: iwlan", nw_type)
254        return False
255    if not is_wfc_enabled(log, ad):
256        ad.log.error("WiFi Calling feature bit is False.")
257        return False
258    call_list = ad.droid.telecomCallGetCallIds()
259    for call in call_list:
260        state = ad.droid.telecomCallVideoGetState(call)
261        if video_state is None:
262            if {
263                    VT_STATE_AUDIO_ONLY: False,
264                    VT_STATE_TX_ENABLED: True,
265                    VT_STATE_TX_PAUSED: True,
266                    VT_STATE_RX_ENABLED: True,
267                    VT_STATE_RX_PAUSED: True,
268                    VT_STATE_BIDIRECTIONAL: True,
269                    VT_STATE_BIDIRECTIONAL_PAUSED: True,
270                    VT_STATE_STATE_INVALID: False
271            }[state]:
272                return True
273        else:
274            if state == video_state:
275                return True
276        ad.log.info("Non-Video-State: %s", state)
277    ad.log.error("Phone not in video call. Call list: %s", call_list)
278    return False
279
280
281def is_phone_in_call_video_bidirectional(log, ad):
282    """Return if phone in bi-directional video call.
283
284    Args:
285        log: log object.
286        ad: android device object
287
288    Returns:
289        True if phone in bi-directional video call.
290    """
291    return is_phone_in_call_video_bidirectional_for_subscription(
292        log, ad, get_outgoing_voice_sub_id(ad))
293
294
295def is_phone_in_call_video_bidirectional_for_subscription(log, ad, sub_id):
296    """Return if phone in bi-directional video call for subscription id.
297
298    Args:
299        log: log object.
300        ad: android device object
301        sub_id: subscription id.
302
303    Returns:
304        True if phone in bi-directional video call.
305    """
306    log.info("Verify if {}(subid {}) in bi-directional video call.".format(
307        ad.serial, sub_id))
308    return is_phone_in_call_video_for_subscription(log, ad, sub_id,
309                                                   VT_STATE_BIDIRECTIONAL)
310
311
312def is_phone_in_call_viwifi_bidirectional(log, ad):
313    """Return if phone in bi-directional viwifi call.
314
315    Args:
316        log: log object.
317        ad: android device object
318
319    Returns:
320        True if phone in bi-directional viwifi call.
321    """
322    return is_phone_in_call_viwifi_bidirectional_for_subscription(
323        log, ad, get_outgoing_voice_sub_id(ad))
324
325
326def is_phone_in_call_viwifi_bidirectional_for_subscription(log, ad, sub_id):
327    """Return if phone in bi-directional viwifi call for subscription id.
328
329    Args:
330        log: log object.
331        ad: android device object
332        sub_id: subscription id.
333
334    Returns:
335        True if phone in bi-directional viwifi call.
336    """
337    ad.log.info("Verify if subid %s in bi-directional video call.", sub_id)
338    return is_phone_in_call_viwifi_for_subscription(log, ad, sub_id,
339                                                    VT_STATE_BIDIRECTIONAL)
340
341
342def is_phone_in_call_video_tx_enabled(log, ad):
343    """Return if phone in tx_enabled video call.
344
345    Args:
346        log: log object.
347        ad: android device object
348
349    Returns:
350        True if phone in tx_enabled video call.
351    """
352    return is_phone_in_call_video_tx_enabled_for_subscription(
353        log, ad, get_outgoing_voice_sub_id(ad))
354
355
356def is_phone_in_call_video_tx_enabled_for_subscription(log, ad, sub_id):
357    """Return if phone in tx_enabled video call for subscription id.
358
359    Args:
360        log: log object.
361        ad: android device object
362        sub_id: subscription id.
363
364    Returns:
365        True if phone in tx_enabled video call.
366    """
367    log.info("Verify if {}(subid {}) in tx_enabled video call.".format(
368        ad.serial, sub_id))
369    return is_phone_in_call_video_for_subscription(log, ad, sub_id,
370                                                   VT_STATE_TX_ENABLED)
371
372
373def is_phone_in_call_video_rx_enabled(log, ad):
374    """Return if phone in rx_enabled video call.
375
376    Args:
377        log: log object.
378        ad: android device object
379
380    Returns:
381        True if phone in rx_enabled video call.
382    """
383    return is_phone_in_call_video_rx_enabled_for_subscription(
384        log, ad, get_outgoing_voice_sub_id(ad))
385
386
387def is_phone_in_call_video_rx_enabled_for_subscription(log, ad, sub_id):
388    """Return if phone in rx_enabled video call for subscription id.
389
390    Args:
391        log: log object.
392        ad: android device object
393        sub_id: subscription id.
394
395    Returns:
396        True if phone in rx_enabled video call.
397    """
398    log.info("Verify if {}(subid {}) in rx_enabled video call.".format(
399        ad.serial, sub_id))
400    return is_phone_in_call_video_for_subscription(log, ad, sub_id,
401                                                   VT_STATE_RX_ENABLED)
402
403
404def is_phone_in_call_voice_hd(log, ad):
405    """Return if phone in hd voice call.
406
407    Args:
408        log: log object.
409        ad: android device object
410
411    Returns:
412        True if phone in hd voice call.
413    """
414    return is_phone_in_call_voice_hd_for_subscription(
415        log, ad, get_outgoing_voice_sub_id(ad))
416
417
418def is_phone_in_call_voice_hd_for_subscription(log, ad, sub_id):
419    """Return if phone in hd voice call for subscription id.
420
421    Args:
422        log: log object.
423        ad: android device object
424        sub_id: subscription id.
425
426    Returns:
427        True if phone in hd voice call.
428    """
429    log.info("Verify if {}(subid {}) in hd voice call.".format(
430        ad.serial, sub_id))
431    if not ad.droid.telecomIsInCall():
432        log.error("{} not in call.".format(ad.serial))
433        return False
434    for call in ad.droid.telecomCallGetCallIds():
435        state = ad.droid.telecomCallVideoGetState(call)
436        if (state == VT_STATE_AUDIO_ONLY and is_call_hd(log, ad, call)):
437            return True
438        log.info("Non-HDAudio-State: {}, property: {}".format(
439            state, ad.droid.telecomCallGetProperties(call)))
440    return False
441
442
443def initiate_video_call(log, ad_caller, callee_number):
444    """Make phone call from caller to callee.
445
446    Args:
447        ad_caller: Caller android device object.
448        callee_number: Callee phone number.
449        emergency : specify the call is emergency.
450            Optional. Default value is False.
451
452    Returns:
453        result: if phone call is placed successfully.
454    """
455
456    wait_time_for_incall_state = MAX_WAIT_TIME_CALL_INITIATION
457    ad_caller.droid.telecomCallNumber(callee_number, True)
458    while wait_time_for_incall_state > 0:
459        wait_time_for_incall_state -= 1
460        if ad_caller.droid.telecomIsInCall():
461            return True
462        time.sleep(1)
463    log.error("Make call fail.")
464    return False
465
466
467def wait_and_answer_video_call(log,
468                               ad,
469                               incoming_number=None,
470                               video_state=VT_STATE_BIDIRECTIONAL,
471                               incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND):
472    """Wait for an incoming call on default voice subscription and
473       accepts the call.
474
475    Args:
476        ad: android device object.
477        incoming_number: Expected incoming number.
478            Optional. Default is None
479        incall_ui_display: after answer the call, bring in-call UI to foreground or
480            background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
481            if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
482            if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
483            else, do nothing.
484
485    Returns:
486        True: if incoming call is received and answered successfully.
487        False: for errors
488    """
489    return wait_and_answer_video_call_for_subscription(
490        log, ad, get_outgoing_voice_sub_id(ad), incoming_number, video_state,
491        incall_ui_display)
492
493
494def wait_and_answer_video_call_for_subscription(
495        log,
496        ad,
497        sub_id,
498        incoming_number=None,
499        video_state=VT_STATE_BIDIRECTIONAL,
500        incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND):
501    """Wait for an incoming call on specified subscription and
502       accepts the call.
503
504    Args:
505        ad: android device object.
506        sub_id: subscription ID
507        incoming_number: Expected incoming number.
508            Optional. Default is None
509        incall_ui_display: after answer the call, bring in-call UI to foreground or
510            background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
511            if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
512            if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
513            else, do nothing.
514
515    Returns:
516        True: if incoming call is received and answered successfully.
517        False: for errors
518    """
519
520    if not wait_for_ringing_call(log, ad, incoming_number):
521        log.error(
522            "Video call could not be established: <{}> never rang.".format(
523                ad.serial))
524        return False
525
526    ad.ed.clear_all_events()
527
528    ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
529
530    if not wait_for_telecom_ringing(log, ad, MAX_WAIT_TIME_TELECOM_RINGING):
531        log.error("Telecom is not ringing.")
532        return False
533
534    log.info("Accept on callee.")
535    ad.droid.telecomAcceptRingingCall(video_state)
536
537    try:
538        ad.ed.wait_for_event(
539            EventCallStateChanged,
540            is_event_match,
541            timeout=MAX_WAIT_TIME_ACCEPT_CALL_TO_OFFHOOK_EVENT,
542            field=CallStateContainer.CALL_STATE,
543            value=TELEPHONY_STATE_OFFHOOK)
544    except Empty:
545        if not ad.droid.telecomIsInCall():
546            log.error("Accept call failed.")
547            return False
548    finally:
549        ad.droid.telephonyStopTrackingCallStateChangeForSubscription(sub_id)
550    if incall_ui_display == INCALL_UI_DISPLAY_FOREGROUND:
551        ad.droid.telecomShowInCallScreen()
552    elif incall_ui_display == INCALL_UI_DISPLAY_BACKGROUND:
553        ad.droid.showHomeScreen()
554    return True
555
556
557def video_call_setup_teardown(log,
558                              ad_caller,
559                              ad_callee,
560                              ad_hangup=None,
561                              video_state=VT_STATE_BIDIRECTIONAL,
562                              verify_caller_func=None,
563                              verify_callee_func=None,
564                              wait_time_in_call=WAIT_TIME_IN_CALL,
565                              incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND):
566    """ Call process, including make a phone call from caller,
567    accept from callee, and hang up. The call is on default subscription
568
569    In call process, call from <droid_caller> to <droid_callee>,
570    accept the call, (optional)then hang up from <droid_hangup>.
571
572    Args:
573        ad_caller: Caller Android Device Object.
574        ad_callee: Callee Android Device Object.
575        ad_hangup: Android Device Object end the phone call.
576            Optional. Default value is None, and phone call will continue.
577        video_state: video state for VT call.
578            Optional. Default value is VT_STATE_BIDIRECTIONAL
579        verify_caller_func: func_ptr to verify caller in correct mode
580            Optional. Default is None
581        verify_callee_func: func_ptr to verify callee in correct mode
582            Optional. Default is None
583        wait_time_in_call: wait time during call.
584            Optional. Default is WAIT_TIME_IN_CALL.
585        incall_ui_display: after answer the call, bring in-call UI to foreground or
586            background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
587            if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
588            if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
589            else, do nothing.
590
591    Returns:
592        True if call process without any error.
593        False if error happened.
594
595    """
596    return video_call_setup_teardown_for_subscription(
597        log, ad_caller, ad_callee, get_outgoing_voice_sub_id(ad_caller),
598        get_incoming_voice_sub_id(ad_callee), ad_hangup, video_state,
599        verify_caller_func, verify_callee_func, wait_time_in_call,
600        incall_ui_display)
601
602
603# TODO: b/26337151 Might be able to re-factor call_setup_teardown and add.
604# Minimal changes.
605def video_call_setup_teardown_for_subscription(
606        log,
607        ad_caller,
608        ad_callee,
609        subid_caller,
610        subid_callee,
611        ad_hangup=None,
612        video_state=VT_STATE_BIDIRECTIONAL,
613        verify_caller_func=None,
614        verify_callee_func=None,
615        wait_time_in_call=WAIT_TIME_IN_CALL,
616        incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND):
617    """ Call process, including make a phone call from caller,
618    accept from callee, and hang up. The call is on specified subscription
619
620    In call process, call from <droid_caller> to <droid_callee>,
621    accept the call, (optional)then hang up from <droid_hangup>.
622
623    Args:
624        ad_caller: Caller Android Device Object.
625        ad_callee: Callee Android Device Object.
626        subid_caller: Caller subscription ID
627        subid_callee: Callee subscription ID
628        ad_hangup: Android Device Object end the phone call.
629            Optional. Default value is None, and phone call will continue.
630        video_state: video state for VT call.
631            Optional. Default value is VT_STATE_BIDIRECTIONAL
632        verify_caller_func: func_ptr to verify caller in correct mode
633            Optional. Default is None
634        verify_callee_func: func_ptr to verify callee in correct mode
635            Optional. Default is None
636        wait_time_in_call: wait time during call.
637            Optional. Default is WAIT_TIME_IN_CALL.
638        incall_ui_display: after answer the call, bring in-call UI to foreground or
639            background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
640            if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
641            if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
642            else, do nothing.
643
644    Returns:
645        True if call process without any error.
646        False if error happened.
647
648    """
649    CHECK_INTERVAL = 60
650
651    class _CallSequenceException(Exception):
652        pass
653
654    caller_number = ad_caller.cfg['subscription'][subid_caller]['phone_num']
655    callee_number = ad_callee.cfg['subscription'][subid_callee]['phone_num']
656
657    log.info("Call from {} to {}".format(caller_number, callee_number))
658
659    try:
660        if not initiate_video_call(log, ad_caller, callee_number):
661            raise _CallSequenceException("Initiate call failed.")
662
663        if not wait_and_answer_video_call_for_subscription(
664                log,
665                ad_callee,
666                subid_callee,
667                incoming_number=caller_number,
668                video_state=video_state,
669                incall_ui_display=incall_ui_display):
670            raise _CallSequenceException("Answer call fail.")
671
672        # ensure that all internal states are updated in telecom
673        time.sleep(WAIT_TIME_ACCEPT_VIDEO_CALL_TO_CHECK_STATE)
674
675        # Check if caller/callee dropped call.
676        if not verify_incall_state(log, [ad_callee, ad_caller], True):
677            raise _CallSequenceException("Call Drop!")
678        # Check Callee first
679        # in case of VT call drop, it usually start from callee
680        if verify_callee_func and not verify_callee_func(log, ad_callee):
681            raise _CallSequenceException("Callee not in correct state!")
682        if verify_caller_func and not verify_caller_func(log, ad_caller):
683            raise _CallSequenceException("Caller not in correct state!")
684
685        # TODO: b/26291165 Replace with reducing the volume as we want
686        # to test route switching
687        ad_caller.droid.telecomCallSetAudioRoute(AUDIO_ROUTE_EARPIECE)
688        ad_callee.droid.telecomCallSetAudioRoute(AUDIO_ROUTE_EARPIECE)
689
690        elapsed_time = 0
691        while (elapsed_time < wait_time_in_call):
692            CHECK_INTERVAL = min(CHECK_INTERVAL,
693                                 wait_time_in_call - elapsed_time)
694            time.sleep(CHECK_INTERVAL)
695            elapsed_time += CHECK_INTERVAL
696
697            # Check Callee first
698            # in case of VT call drop, it usually start from callee
699            if not verify_callee_func:
700                callee_state_result = ad_callee.droid.telecomIsInCall()
701            else:
702                callee_state_result = verify_callee_func(log, ad_callee)
703            if not callee_state_result:
704                raise _CallSequenceException(
705                    "Callee not in correct state at <{}>/<{}> seconds".format(
706                        elapsed_time, wait_time_in_call))
707
708            if not verify_caller_func:
709                caller_state_result = ad_caller.droid.telecomIsInCall()
710            else:
711                caller_state_result = verify_caller_func(log, ad_caller)
712            if not caller_state_result:
713                raise _CallSequenceException(
714                    "Caller not in correct state at <{}>/<{}> seconds".format(
715                        elapsed_time, wait_time_in_call))
716
717        if not ad_hangup:
718            return True
719
720        if not hangup_call(log, ad_hangup):
721            raise _CallSequenceException("Error in Hanging-Up Call")
722        return True
723
724    except _CallSequenceException as e:
725        log.error(e)
726        return False
727    finally:
728        if ad_hangup:
729            for ad in [ad_caller, ad_callee]:
730                try:
731                    if ad.droid.telecomIsInCall():
732                        ad.droid.telecomEndCall()
733                except Exception as e:
734                    log.error(str(e))
735
736
737def video_call_setup(log,
738                     ad_caller,
739                     ad_callee,
740                     video_state=VT_STATE_BIDIRECTIONAL,
741                     incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND):
742    """ Call process, including make a phone call from caller,
743    accept from callee, and hang up. The call is on default subscription
744
745    In call process, call from <droid_caller> to <droid_callee>,
746    accept the call, (optional)then hang up from <droid_hangup>.
747
748    Args:
749        ad_caller: Caller Android Device Object.
750        ad_callee: Callee Android Device Object.
751        incall_ui_display: after answer the call, bring in-call UI to foreground or
752            background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
753            if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
754            if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
755            else, do nothing.
756
757    Returns:
758        True if call process without any error.
759        False if error happened.
760
761    """
762    return video_call_setup_for_subscription(
763        log, ad_caller, ad_callee, get_outgoing_voice_sub_id(ad_caller),
764        get_incoming_voice_sub_id(ad_callee), video_state, incall_ui_display)
765
766
767def video_call_setup_for_subscription(
768        log,
769        ad_caller,
770        ad_callee,
771        subid_caller,
772        subid_callee,
773        video_state=VT_STATE_BIDIRECTIONAL,
774        incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND):
775    """ Call process, including make a phone call from caller,
776    accept from callee, and hang up. The call is on specified subscription
777
778    In call process, call from <droid_caller> to <droid_callee>,
779    accept the call, (optional)then hang up from <droid_hangup>.
780
781    Args:
782        ad_caller: Caller Android Device Object.
783        ad_callee: Callee Android Device Object.
784        subid_caller: Caller subscription ID
785        subid_callee: Callee subscription ID
786        ad_hangup: Android Device Object end the phone call.
787            Optional. Default value is None, and phone call will continue.
788        incall_ui_display: after answer the call, bring in-call UI to foreground or
789            background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
790            if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
791            if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
792            else, do nothing.
793
794    Returns:
795        True if call process without any error.
796        False if error happened.
797
798    """
799
800    class _CallSequenceException(Exception):
801        pass
802
803    caller_number = ad_caller.cfg['subscription'][subid_caller]['phone_num']
804    callee_number = ad_callee.cfg['subscription'][subid_callee]['phone_num']
805
806    log.info("Call from {} to {}".format(caller_number, callee_number))
807
808    try:
809        if not initiate_video_call(log, ad_caller, callee_number):
810            raise _CallSequenceException("Initiate call failed.")
811
812        if not wait_and_answer_video_call_for_subscription(
813                log,
814                ad_callee,
815                subid_callee,
816                incoming_number=caller_number,
817                video_state=video_state,
818                incall_ui_display=incall_ui_display):
819            raise _CallSequenceException("Answer call fail.")
820
821        # ensure that all internal states are updated in telecom
822        time.sleep(WAIT_TIME_ACCEPT_VIDEO_CALL_TO_CHECK_STATE)
823
824        # TODO: b/26291165 Replace with reducing the volume as we want
825        # to test route switching
826        ad_caller.droid.telecomCallSetAudioRoute(AUDIO_ROUTE_EARPIECE)
827        ad_callee.droid.telecomCallSetAudioRoute(AUDIO_ROUTE_EARPIECE)
828
829        return True
830
831    except _CallSequenceException as e:
832        log.error(e)
833        return False
834
835
836def video_call_modify_video(log,
837                            ad_requester,
838                            call_id_requester,
839                            ad_responder,
840                            call_id_responder,
841                            video_state_request,
842                            video_quality_request=VT_VIDEO_QUALITY_DEFAULT,
843                            video_state_response=None,
844                            video_quality_response=None,
845                            verify_func_between_request_and_response=None):
846    """Modifies an ongoing call to change the video_call state
847
848    Args:
849        log: logger object
850        ad_requester: android_device object of the requester
851        call_id_requester: the call_id of the call placing the modify request
852        ad_requester: android_device object of the responder
853        call_id_requester: the call_id of the call receiving the modify request
854        video_state_request: the requested video state
855        video_quality_request: the requested video quality, defaults to
856            QUALITY_DEFAULT
857        video_state_response: the responded video state or, or (default)
858            match the request if None
859        video_quality_response: the responded video quality, or (default)
860            match the request if None
861
862    Returns:
863        A call_id corresponding to the first call in the state, or None
864    """
865
866    if not video_state_response:
867        video_state_response = video_state_request
868    if not video_quality_response:
869        video_quality_response = video_quality_request
870
871    cur_video_state = ad_requester.droid.telecomCallVideoGetState(
872        call_id_requester)
873
874    log.info("State change request from {} to {} requested".format(
875        cur_video_state, video_state_request))
876
877    if cur_video_state == video_state_request:
878        return True
879
880    ad_responder.ed.clear_events(
881        EventTelecomVideoCallSessionModifyRequestReceived)
882
883    ad_responder.droid.telecomCallVideoStartListeningForEvent(
884        call_id_responder, EVENT_VIDEO_SESSION_MODIFY_REQUEST_RECEIVED)
885
886    ad_requester.droid.telecomCallVideoSendSessionModifyRequest(
887        call_id_requester, video_state_request, video_quality_request)
888
889    try:
890        request_event = ad_responder.ed.pop_event(
891            EventTelecomVideoCallSessionModifyRequestReceived,
892            MAX_WAIT_TIME_VIDEO_SESSION_EVENT)
893        log.info(request_event)
894    except Empty:
895        log.error("Failed to receive SessionModifyRequest!")
896        return False
897    finally:
898        ad_responder.droid.telecomCallVideoStopListeningForEvent(
899            call_id_responder, EVENT_VIDEO_SESSION_MODIFY_REQUEST_RECEIVED)
900
901    if (verify_func_between_request_and_response
902            and not verify_func_between_request_and_response()):
903        log.error("verify_func_between_request_and_response failed.")
904        return False
905
906    # TODO: b/26291165 Replace with reducing the volume as we want
907    # to test route switching
908    ad_requester.droid.telecomCallSetAudioRoute(AUDIO_ROUTE_EARPIECE)
909
910    ad_requester.droid.telecomCallVideoStartListeningForEvent(
911        call_id_requester, EVENT_VIDEO_SESSION_MODIFY_RESPONSE_RECEIVED)
912
913    ad_responder.droid.telecomCallVideoSendSessionModifyResponse(
914        call_id_responder, video_state_response, video_quality_response)
915
916    try:
917        response_event = ad_requester.ed.pop_event(
918            EventTelecomVideoCallSessionModifyResponseReceived,
919            MAX_WAIT_TIME_VIDEO_SESSION_EVENT)
920        log.info(response_event)
921    except Empty:
922        log.error("Failed to receive SessionModifyResponse!")
923        return False
924    finally:
925        ad_requester.droid.telecomCallVideoStopListeningForEvent(
926            call_id_requester, EVENT_VIDEO_SESSION_MODIFY_RESPONSE_RECEIVED)
927
928    # TODO: b/26291165 Replace with reducing the volume as we want
929    # to test route switching
930    ad_responder.droid.telecomCallSetAudioRoute(AUDIO_ROUTE_EARPIECE)
931
932    return True
933
934
935def is_call_id_in_video_state(log, ad, call_id, video_state):
936    """Return is the call_id is in expected video_state
937
938    Args:
939        log: logger object
940        ad: android_device object
941        call_id: call id
942        video_state: valid VIDEO_STATE
943
944    Returns:
945        True is call_id in expected video_state; False if not.
946    """
947    return video_state == ad.droid.telecomCallVideoGetState(call_id)
948
949
950def get_call_id_in_video_state(log, ad, video_state):
951    """Gets the first call reporting a given video_state
952        from among the active calls
953
954    Args:
955        log: logger object
956        ad: android_device object
957        video_state: valid VIDEO_STATE
958
959    Returns:
960        A call_id corresponding to the first call in the state, or None
961    """
962
963    if not ad.droid.telecomIsInCall():
964        log.error("{} not in call.".format(ad.serial))
965        return None
966    for call in ad.droid.telecomCallGetCallIds():
967        if is_call_id_in_video_state(log, ad, call, video_state):
968            return call
969    return None
970
971
972def video_call_downgrade(log,
973                         ad_requester,
974                         call_id_requester,
975                         ad_responder,
976                         call_id_responder,
977                         video_state_request=None,
978                         video_quality_request=VT_VIDEO_QUALITY_DEFAULT):
979    """Downgrade Video call to video_state_request.
980    Send telecomCallVideoSendSessionModifyRequest from ad_requester.
981    Get video call state from ad_requester and ad_responder.
982    Verify video calls states are correct and downgrade succeed.
983
984    Args:
985        log: logger object
986        ad_requester: android_device object of the requester
987        call_id_requester: the call_id of the call placing the modify request
988        ad_requester: android_device object of the responder
989        call_id_requester: the call_id of the call receiving the modify request
990        video_state_request: the requested downgrade video state
991            This parameter is optional. If this parameter is None:
992                if call_id_requester current is bi-directional, will downgrade to RX_ENABLED
993                if call_id_requester current is RX_ENABLED, will downgrade to AUDIO_ONLY
994        video_quality_request: the requested video quality, defaults to
995            QUALITY_DEFAULT
996    Returns:
997        True if downgrade succeed.
998    """
999    if (call_id_requester is None) or (call_id_responder is None):
1000        log.error("call_id_requester: {}, call_id_responder: {}".format(
1001            call_id_requester, call_id_responder))
1002        return False
1003    current_video_state_requester = ad_requester.droid.telecomCallVideoGetState(
1004        call_id_requester)
1005    if video_state_request is None:
1006        if (current_video_state_requester == VT_STATE_BIDIRECTIONAL
1007                or current_video_state_requester ==
1008                VT_STATE_BIDIRECTIONAL_PAUSED):
1009            video_state_request = VT_STATE_RX_ENABLED
1010        elif (current_video_state_requester == VT_STATE_TX_ENABLED
1011              or current_video_state_requester == VT_STATE_TX_PAUSED):
1012            video_state_request = VT_STATE_AUDIO_ONLY
1013        else:
1014            log.error("Can Not Downgrade. ad: {}, current state {}".format(
1015                ad_requester.serial, current_video_state_requester))
1016            return False
1017    expected_video_state_responder = {
1018        VT_STATE_AUDIO_ONLY: VT_STATE_AUDIO_ONLY,
1019        VT_STATE_RX_ENABLED: VT_STATE_TX_ENABLED
1020    }[video_state_request]
1021
1022    ad_requester.droid.telecomCallVideoStartListeningForEvent(
1023        call_id_requester, EVENT_VIDEO_SESSION_MODIFY_RESPONSE_RECEIVED)
1024
1025    ad_requester.droid.telecomCallVideoSendSessionModifyRequest(
1026        call_id_requester, video_state_request, video_quality_request)
1027
1028    try:
1029        response_event = ad_requester.ed.pop_event(
1030            EventTelecomVideoCallSessionModifyResponseReceived,
1031            MAX_WAIT_TIME_VIDEO_SESSION_EVENT)
1032        log.info(response_event)
1033    except Empty:
1034        log.error("Failed to receive SessionModifyResponse!")
1035        return False
1036    finally:
1037        ad_requester.droid.telecomCallVideoStopListeningForEvent(
1038            call_id_requester, EVENT_VIDEO_SESSION_MODIFY_RESPONSE_RECEIVED)
1039
1040    time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
1041    # TODO: b/26291165 Replace with reducing the volume as we want
1042    # to test route switching
1043    ad_requester.droid.telecomCallSetAudioRoute(AUDIO_ROUTE_EARPIECE)
1044    ad_responder.droid.telecomCallSetAudioRoute(AUDIO_ROUTE_EARPIECE)
1045
1046    time.sleep(WAIT_TIME_IN_CALL)
1047    if video_state_request != ad_requester.droid.telecomCallVideoGetState(
1048            call_id_requester):
1049        log.error("requester not in correct state. expected:{}, current:{}"
1050                  .format(video_state_request,
1051                          ad_requester.droid.telecomCallVideoGetState(
1052                              call_id_requester)))
1053        return False
1054    if (expected_video_state_responder !=
1055            ad_responder.droid.telecomCallVideoGetState(call_id_responder)):
1056        log.error(
1057            "responder not in correct state. expected:{}, current:{}".format(
1058                expected_video_state_responder,
1059                ad_responder.droid.telecomCallVideoGetState(
1060                    call_id_responder)))
1061        return False
1062
1063    return True
1064
1065
1066def verify_video_call_in_expected_state(log, ad, call_id, call_video_state,
1067                                        call_state):
1068    """Return True if video call is in expected video state and call state.
1069
1070    Args:
1071        log: logger object
1072        ad: android_device object
1073        call_id: ad's call id
1074        call_video_state: video state to validate.
1075        call_state: call state to validate.
1076
1077    Returns:
1078        True if video call is in expected video state and call state.
1079    """
1080    if not is_call_id_in_video_state(log, ad, call_id, call_video_state):
1081        log.error("Call is not in expected {} state. Current state {}".format(
1082            call_video_state, ad.droid.telecomCallVideoGetState(call_id)))
1083        return False
1084    if ad.droid.telecomCallGetCallState(call_id) != call_state:
1085        log.error("Call is not in expected {} state. Current state {}".format(
1086            call_state, ad.droid.telecomCallGetCallState(call_id)))
1087        return False
1088    return True
1089