Refactored 2 DSDS WFC SS test classes

Both TelLiveGFTDSDSWfcSupplementaryServiceTest and Nsa5gDSDSWfcSupplementaryServiceTest are refactored.

1. Common internal functions are moved to tel_dsds_utils.py.
2. Procedure to set up phone is improved for the following conditions:
   - The traget slot is a 5G slot but not the DDS-slot.
   - The target slot is a 5G slot but can only attach to LTE network due to the other 5G slot already attaching to 5G network.
3. DDS switching of each test case will be performed earlier than phone setup procedure.
4. Removed ensure_network_generation_for_subscription from phone_setup_csfb_for_subscription since it is performed already earlier in phone_setup_4g_for_subscription or phone_setup_5g_for_subscription.

Bug: None
Test: Yes, locally
Change-Id: I380c0972038ef9421f9ab3ce52133e0113d6af3c
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_dsds_utils.py b/acts_tests/acts_contrib/test_utils/tel/tel_dsds_utils.py
index 3adb61a..d5c6dc5 100644
--- a/acts_tests/acts_contrib/test_utils/tel/tel_dsds_utils.py
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_dsds_utils.py
@@ -22,6 +22,7 @@
 from acts_contrib.test_utils.tel.loggers.protos.telephony_metric_pb2 import TelephonyVoiceTestResult
 from acts_contrib.test_utils.tel.loggers.telephony_metric_logger import TelephonyMetricLogger
 from acts_contrib.test_utils.tel.tel_defines import INVALID_SUB_ID
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_incoming_voice_sub_id
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_voice_sub_id
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
@@ -31,24 +32,31 @@
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_subid_on_same_network_of_host_ad
 from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
 from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected
 from acts_contrib.test_utils.tel.tel_test_utils import multithread_func
 from acts_contrib.test_utils.tel.tel_test_utils import num_active_calls
 from acts_contrib.test_utils.tel.tel_test_utils import verify_incall_state
 from acts_contrib.test_utils.tel.tel_test_utils import verify_http_connection
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
 from acts_contrib.test_utils.tel.tel_test_utils import set_call_waiting
 from acts_contrib.test_utils.tel.tel_test_utils import initiate_call
 from acts_contrib.test_utils.tel.tel_test_utils import wait_and_reject_call_for_subscription
 from acts_contrib.test_utils.tel.tel_test_utils import get_slot_index_from_subid
 from acts_contrib.test_utils.tel.tel_test_utils import erase_call_forwarding_by_mmi
 from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_wfc_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import set_wfc_mode_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import set_call_forwarding_by_mmi
 from acts_contrib.test_utils.tel.tel_voice_utils import three_phone_call_forwarding_short_seq
 from acts_contrib.test_utils.tel.tel_voice_utils import three_phone_call_waiting_short_seq
 from acts_contrib.test_utils.tel.tel_voice_utils import swap_calls
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_general
 from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_on_rat
 from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_on_rat
 from acts_contrib.test_utils.tel.tel_voice_utils import two_phone_call_msim_for_slot
 from acts_contrib.test_utils.tel.tel_voice_conf_utils import _test_ims_conference_merge_drop_second_call_from_participant
 from acts_contrib.test_utils.tel.tel_voice_conf_utils import _test_wcdma_conference_merge_drop
+from acts_contrib.test_utils.tel.tel_voice_conf_utils import _three_phone_call_mo_add_mt
 
 CallResult = TelephonyVoiceTestResult.CallResult.Value
 tel_logger = TelephonyMetricLogger.for_test_case()
@@ -631,7 +639,7 @@
         contrary.
 
     Returns:
-        True of False
+        True or False
     """
     ad_host = ads[0]
     ad_p1 = ads[1]
@@ -831,4 +839,436 @@
                 log, ads, call_ab_id, call_ac_id)
         else:
             return _test_wcdma_conference_merge_drop(
+                log, ads, call_ab_id, call_ac_id)
+
+def msim_volte_wfc_call_forwarding(
+        log,
+        ads,
+        callee_slot,
+        dds_slot,
+        callee_rat=["5g_wfc", "5g_wfc"],
+        call_forwarding_type="unconditional",
+        is_airplane_mode=False,
+        is_wifi_connected=False,
+        wfc_mode=[
+            WFC_MODE_CELLULAR_PREFERRED,
+            WFC_MODE_CELLULAR_PREFERRED],
+        wifi_network_ssid=None,
+        wifi_network_pass=None):
+    """Make VoLTE/WFC call to the primary device at specific slot with DDS
+    at specific slot, and then forwarded to 3rd device with specific call
+    forwarding type.
+
+    Test step:
+    1. Get sub IDs of specific slots of both MO and MT devices.
+    2. Switch DDS to specific slot.
+    3. Check HTTP connection after DDS switch.
+    4. Set up phones in desired RAT.
+    5. Register and enable call forwarding with specifc type.
+    6. Make VoLTE/WFC call to the primary device and wait for being
+        forwarded to 3rd device.
+
+    Args:
+        callee_slot: Slot of primary device receiving and forwarding MT call
+                        (0 or 1)
+        dds_slot: Preferred data slot
+        callee_rat: RAT for both slots of the primary device
+        call_forwarding_type:
+            "unconditional"
+            "busy"
+            "not_answered"
+            "not_reachable"
+        is_airplane_mode: True or False for WFC setup
+        wfc_mode: Cellular preferred or Wi-Fi preferred.
+        wifi_network_ssid: SSID of Wi-Fi AP
+        wifi_network_pass: Password of Wi-Fi AP SSID
+
+    Returns:
+        True or False
+    """
+    ad_caller = ads[1]
+    ad_callee = ads[0]
+    ad_forwarded_callee = ads[2]
+
+    if not toggle_airplane_mode(log, ad_callee, False):
+        ad_callee.log.error("Failed to disable airplane mode.")
+        return False
+
+    # Set up callee (primary device)
+    callee_sub_id = get_subid_from_slot_index(
+        log, ad_callee, callee_slot)
+    if callee_sub_id == INVALID_SUB_ID:
+        log.warning(
+            "Failed to get sub ID at slot %s.", callee_slot)
+        return
+    callee_other_sub_id = get_subid_from_slot_index(
+        log, ad_callee, 1-callee_slot)
+    set_voice_sub_id(ad_callee, callee_sub_id)
+    ad_callee.log.info(
+        "Sub ID for incoming call at slot %s: %s",
+        callee_slot, get_incoming_voice_sub_id(ad_callee))
+
+    # Set up caller
+    _, caller_sub_id, _ = get_subid_on_same_network_of_host_ad(ads)
+    if caller_sub_id == INVALID_SUB_ID:
+        ad_caller.log.warning("Failed to get proper sub ID of the caller")
+        return
+    set_voice_sub_id(ad_caller, caller_sub_id)
+    ad_caller.log.info(
+        "Sub ID for outgoing call of the caller: %s",
+        get_outgoing_voice_sub_id(ad_caller))
+
+    # Set up forwarded callee
+    _, _, forwarded_callee_sub_id = get_subid_on_same_network_of_host_ad(
+        ads)
+    if forwarded_callee_sub_id == INVALID_SUB_ID:
+        ad_forwarded_callee.log.warning(
+            "Failed to get proper sub ID of the forwarded callee.")
+        return
+    set_voice_sub_id(ad_forwarded_callee, forwarded_callee_sub_id)
+    ad_forwarded_callee.log.info(
+        "Sub ID for incoming call of the forwarded callee: %s",
+        get_incoming_voice_sub_id(ad_forwarded_callee))
+
+    ad_callee.log.info("Step 1: Switch DDS.")
+    if dds_slot:
+        if not set_dds_on_slot_1(ad_callee):
+            ad_callee.log.warning(
+                "Failed to set DDS at eSIM on %s", ad_callee.serial)
+            return
+    else:
+        if not set_dds_on_slot_0(ad_callee):
+            ad_callee.log.warning(
+                "Failed to set DDS at pSIM on %s", ad_callee.serial)
+            return
+
+    ad_callee.log.info("Step 2: Check HTTP connection after DDS switch.")
+    if not verify_http_connection(log, ad_callee):
+        ad_callee.log.error("Failed to verify http connection.")
+        return False
+    else:
+        ad_callee.log.info("Verify http connection successfully.")
+
+    is_callee_in_call = is_phone_in_call_on_rat(
+        log, ad_callee, callee_rat[callee_slot], only_return_fn=True)
+
+    if is_airplane_mode:
+        set_call_forwarding_by_mmi(log, ad_callee, ad_forwarded_callee)
+
+    ad_callee.log.info("Step 3: Set up phones in desired RAT.")
+    if callee_slot == 1:
+        phone_setup_on_rat(
+            log,
+            ad_callee,
+            callee_rat[0],
+            callee_other_sub_id,
+            is_airplane_mode,
+            wfc_mode[0],
+            wifi_network_ssid,
+            wifi_network_pass)
+
+    elif callee_slot == 0:
+        phone_setup_on_rat(
+            log,
+            ad_callee,
+            callee_rat[1],
+            callee_other_sub_id,
+            is_airplane_mode,
+            wfc_mode[1],
+            wifi_network_ssid,
+            wifi_network_pass)
+
+    argv = (
+        log,
+        ad_callee,
+        callee_rat[callee_slot],
+        callee_sub_id,
+        is_airplane_mode,
+        wfc_mode[callee_slot],
+        wifi_network_ssid,
+        wifi_network_pass)
+
+    tasks = [(phone_setup_voice_general, (log, ad_caller)),
+            (phone_setup_on_rat, argv),
+            (phone_setup_voice_general, (log, ad_forwarded_callee))]
+
+    if not multithread_func(log, tasks):
+        log.error("Phone Failed to Set Up Properly.")
+        tel_logger.set_result(CallResult("CALL_SETUP_FAILURE"))
+        raise signals.TestFailure("Failed",
+            extras={"fail_reason": "Phone Failed to Set Up Properly."})
+
+    if is_wifi_connected:
+        if not ensure_wifi_connected(
+            log,
+            ad_callee,
+            wifi_network_ssid,
+            wifi_network_pass,
+            apm=is_airplane_mode):
+            return False
+        time.sleep(5)
+
+    if "wfc" not in callee_rat[callee_slot]:
+        if not toggle_wfc_for_subscription(
+            log,
+            ad_callee,
+            new_state=True,
+            sub_id=callee_sub_id):
+            return False
+        if not set_wfc_mode_for_subscription(
+            ad_callee, wfc_mode[callee_slot], sub_id=callee_sub_id):
+            return False
+
+    log.info(
+        "Step 4: Make voice call with call forwarding %s.",
+        call_forwarding_type)
+    result = three_phone_call_forwarding_short_seq(
+        log,
+        ad_callee,
+        None,
+        is_callee_in_call,
+        ad_caller,
+        ad_forwarded_callee,
+        call_forwarding_type=call_forwarding_type)
+
+    if not result:
+        log.error(
+            "Failed to make MO call from %s to %s slot %s and forward"
+            " to %s.",
+            ad_caller.serial,
+            ad_callee.serial,
+            callee_slot,
+            ad_forwarded_callee.serial)
+    return result
+
+def msim_volte_wfc_call_voice_conf(
+        log,
+        ads,
+        host_slot,
+        dds_slot,
+        host_rat=["5g_wfc", "5g_wfc"],
+        merge=True,
+        disable_cw=False,
+        is_airplane_mode=False,
+        is_wifi_connected=False,
+        wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED],
+        reject_once=False,
+        wifi_network_ssid=None,
+        wifi_network_pass=None):
+    """Make a VoLTE/WFC conference call at specific slot with DDS at
+        specific slot.
+
+    Test step:
+    1. Get sub IDs of specific slots of both MO and MT devices.
+    2. Set up phones in desired RAT
+    3. Enable VoLTE/WFC.
+    4. Switch DDS to specific slot.
+    5. Check HTTP connection after DDS switch.
+    6. Make 3-way VoLTE/WFC call.
+    7. Swap calls.
+    8. Merge calls.
+
+    Args:
+        host_slot: Slot on the primary device to host the comference call.
+                    0 or 1 (0 for pSIM or 1 for eSIM)call
+        dds_slot: Preferred data slot
+        host_rat: RAT for both slots of the primary devicevice
+        merge: True for merging 2 calls into the conference call. False for
+                not merging 2 separated call.
+        disable_cw: True for disabling call waiting and False on the
+                    contrary.
+        enable_volte: True for enabling and False for disabling VoLTE for
+                        each slot on the primary device
+        enable_wfc: True for enabling and False for disabling WFC for
+                    each slot on the primary device
+        is_airplane_mode: True or False for WFC setup
+        wfc_mode: Cellular preferred or Wi-Fi preferred.
+        reject_once: True for rejecting the 2nd call once from the 3rd
+                        device (Phone C) to the primary device (Phone A).
+        wifi_network_ssid: SSID of Wi-Fi AP
+        wifi_network_pass: Password of Wi-Fi AP SSID
+
+    Returns:
+        True or False
+    """
+    ad_host = ads[0]
+    ad_p1 = ads[1]
+    ad_p2 = ads[2]
+
+    host_sub_id = get_subid_from_slot_index(log, ad_host, host_slot)
+    if host_sub_id == INVALID_SUB_ID:
+        ad_host.log.warning("Failed to get sub ID at slot.", host_slot)
+        return
+    host_other_sub_id = get_subid_from_slot_index(
+        log, ad_host, 1-host_slot)
+    set_voice_sub_id(ad_host, host_sub_id)
+    ad_host.log.info(
+        "Sub ID for outgoing call at slot %s: %s",
+        host_slot, get_outgoing_voice_sub_id(ad_host))
+
+    _, p1_sub_id, p2_sub_id = get_subid_on_same_network_of_host_ad(ads)
+
+    if p1_sub_id == INVALID_SUB_ID:
+        ad_p1.log.warning("Failed to get proper sub ID.")
+        return
+    set_voice_sub_id(ad_p1, p1_sub_id)
+    ad_p1.log.info(
+        "Sub ID for incoming call: %s",
+        get_incoming_voice_sub_id(ad_p1))
+
+    if p2_sub_id == INVALID_SUB_ID:
+        ad_p2.log.warning("Failed to get proper sub ID.")
+        return
+    set_voice_sub_id(ad_p2, p2_sub_id)
+    ad_p2.log.info(
+        "Sub ID for incoming call: %s", get_incoming_voice_sub_id(ad_p2))
+
+    ad_host.log.info("Step 1: Switch DDS.")
+    if dds_slot:
+        if not set_dds_on_slot_1(ad_host):
+            ad_host.log.warning(
+                "Failed to set DDS at eSIM on %s", ad_host.serial)
+            return
+    else:
+        if not set_dds_on_slot_0(ad_host):
+            ad_host.log.warning(
+                "Failed to set DDS at pSIM on %s", ad_host.serial)
+            return
+
+    ad_host.log.info("Step 2: Check HTTP connection after DDS switch.")
+    if not verify_http_connection(log, ads[0]):
+        ad_host.log.error("Failed to verify http connection.")
+        return False
+    else:
+        ad_host.log.info("Verify http connection successfully.")
+
+    if disable_cw:
+        if not set_call_waiting(log, ad_host, enable=0):
+            return False
+
+    ad_host.log.info("Step 3: Set up phones in desired RAT.")
+    if host_slot == 1:
+        phone_setup_on_rat(
+            log,
+            ad_host,
+            host_rat[0],
+            host_other_sub_id,
+            is_airplane_mode,
+            wfc_mode[0],
+            wifi_network_ssid,
+            wifi_network_pass)
+
+    elif host_slot == 0:
+        phone_setup_on_rat(
+            log,
+            ad_host,
+            host_rat[1],
+            host_other_sub_id,
+            is_airplane_mode,
+            wfc_mode[1],
+            wifi_network_ssid,
+            wifi_network_pass)
+
+    argv = (
+        log,
+        ad_host,
+        host_rat[host_slot],
+        host_sub_id,
+        is_airplane_mode,
+        wfc_mode[host_slot],
+        wifi_network_ssid,
+        wifi_network_pass)
+
+    tasks = [(phone_setup_voice_general, (log, ad_p1)),
+            (phone_setup_on_rat, argv),
+            (phone_setup_voice_general, (log, ad_p2))]
+
+    if not multithread_func(log, tasks):
+        log.error("Phone Failed to Set Up Properly.")
+        tel_logger.set_result(CallResult("CALL_SETUP_FAILURE"))
+        raise signals.TestFailure("Failed",
+            extras={"fail_reason": "Phone Failed to Set Up Properly."})
+
+    if is_wifi_connected:
+        if not ensure_wifi_connected(
+            log,
+            ad_host,
+            wifi_network_ssid,
+            wifi_network_pass,
+            apm=is_airplane_mode):
+            return False
+        time.sleep(5)
+
+    if "wfc" not in host_rat[host_slot]:
+        if not toggle_wfc_for_subscription(
+            log,
+            ad_host,
+            new_state=True,
+            sub_id=host_sub_id):
+            return False
+        if not set_wfc_mode_for_subscription(
+            ad_host, wfc_mode[host_slot], sub_id=host_sub_id):
+            return False
+
+    log.info("Step 4: Make 3-way voice call.")
+    is_host_in_call = is_phone_in_call_on_rat(
+        log, ad_host, host_rat[host_slot], only_return_fn=True)
+    call_ab_id = _three_phone_call_mo_add_mt(
+        log,
+        [ad_host, ad_p1, ad_p2],
+        [None, None, None],
+        [is_host_in_call, None, None],
+        reject_once=reject_once)
+
+    if call_ab_id is None:
+        if disable_cw:
+            set_call_waiting(log, ad_host, enable=1)
+            if str(getattr(ad_host, "exception", None)) == \
+                "PhoneA call PhoneC failed.":
+                ads[0].log.info("PhoneA failed to call PhoneC due to call"
+                " waiting being disabled.")
+                delattr(ad_host, "exception")
+                return True
+        log.error("Failed to get call_ab_id")
+        return False
+    else:
+        if disable_cw:
+            set_call_waiting(log, ad_host, enable=0)
+            return False
+
+    calls = ads[0].droid.telecomCallGetCallIds()
+    ads[0].log.info("Calls in PhoneA %s", calls)
+    if num_active_calls(log, ads[0]) != 2:
+        return False
+    if calls[0] == call_ab_id:
+        call_ac_id = calls[1]
+    else:
+        call_ac_id = calls[0]
+
+    if call_ac_id is None:
+        log.error("Failed to get call_ac_id")
+        return False
+
+    num_swaps = 2
+    ad_host.log.info("Step 5: Begin Swap x%s test.", num_swaps)
+    if not swap_calls(log, ads, call_ab_id, call_ac_id,
+                        num_swaps):
+        ad_host.log.error("Swap test failed.")
+        return False
+
+    if not merge:
+        result = True
+        if not hangup_call(log, ads[1]):
+            result =  False
+        if not hangup_call(log, ads[2]):
+            result =  False
+        return result
+    else:
+        ad_host.log.info("Step 6: Merge calls.")
+
+        if re.search('csfb|2g|3g', host_rat[host_slot].lower(), re.I):
+            return _test_wcdma_conference_merge_drop(
+                log, ads, call_ab_id, call_ac_id)
+        else:
+            return _test_ims_conference_merge_drop_second_call_from_participant(
                 log, ads, call_ab_id, call_ac_id)
\ No newline at end of file