1#!/usr/bin/env python3
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# This is test util for subscription setup.
18# It will be deleted once we have better solution for subscription ids.
19import re
20import time
21
22from acts_contrib.test_utils.tel.tel_defines import CHIPSET_MODELS_LIST
23from acts_contrib.test_utils.tel.tel_defines import INVALID_PORT_INDEX
24from acts_contrib.test_utils.tel.tel_defines import INVALID_SIM_SLOT_INDEX
25from acts_contrib.test_utils.tel.tel_defines import INVALID_SUB_ID
26from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_CHANGE_DATA_SUB_ID
27from future import standard_library
28
29standard_library.install_aliases()
30
31
32def initial_set_up_for_subid_information(log, ad):
33    """Initial subid setup for voice, message and data according to ad's
34    attribute.
35
36    Setup sub id properties for android device. Including the followings:
37        incoming_voice_sub_id
38        incoming_message_sub_id
39        outgoing_voice_sub_id
40        outgoing_message_sub_id
41        default_data_sub_id
42
43    Args:
44        log: log object
45        ad: android device object
46
47    Returns:
48        None
49    """
50    # outgoing_voice_sub_id
51    # If default_voice_sim_slot_index is set in config file, then use sub_id
52    # of this SIM as default_outgoing_sub_id. If default_voice_sim_slot_index
53    # is not set, then use default voice sub_id as default_outgoing_sub_id.
54    # Outgoing voice call will be made on default_outgoing_sub_id by default.
55    if hasattr(ad, "default_voice_sim_slot_index"):
56        outgoing_voice_sub_id = get_subid_from_slot_index(
57            log, ad, ad.default_voice_sim_slot_index)
58        set_subid_for_outgoing_call(ad, outgoing_voice_sub_id)
59    else:
60        outgoing_voice_sub_id = ad.droid.subscriptionGetDefaultVoiceSubId()
61    setattr(ad, "outgoing_voice_sub_id", outgoing_voice_sub_id)
62
63    # outgoing_message_sub_id
64    # If default_message_sim_slot_index is set in config file, then use sub_id
65    # of this SIM as outgoing_message_sub_id. If default_message_sim_slot_index
66    # is not set, then use default Sms sub_id as outgoing_message_sub_id.
67    # Outgoing SMS will be sent on outgoing_message_sub_id by default.
68    if hasattr(ad, "default_message_sim_slot_index"):
69        outgoing_message_sub_id = get_subid_from_slot_index(
70            log, ad, ad.default_message_sim_slot_index)
71        set_subid_for_message(ad, outgoing_message_sub_id)
72    else:
73        outgoing_message_sub_id = ad.droid.subscriptionGetDefaultSmsSubId()
74    setattr(ad, "outgoing_message_sub_id", outgoing_message_sub_id)
75
76    # default_data_sub_id
77    # If default_data_sim_slot_index is set in config file, then use sub_id
78    # of this SIM as default_data_sub_id. If default_data_sim_slot_index
79    # is not set, then use default Data sub_id as default_data_sub_id.
80    # Data connection will be established on default_data_sub_id by default.
81    if hasattr(ad, "default_data_sim_slot_index"):
82        default_data_sub_id = get_subid_from_slot_index(
83            log, ad, ad.default_data_sim_slot_index)
84        set_subid_for_data(ad, default_data_sub_id, 0)
85    else:
86        default_data_sub_id = ad.droid.subscriptionGetDefaultDataSubId()
87    setattr(ad, "default_data_sub_id", default_data_sub_id)
88
89    # This is for Incoming Voice Sub ID
90    # If "incoming_voice_sim_slot_index" is set in config file, then
91    # incoming voice call will call to the phone number of the SIM in
92    # "incoming_voice_sim_slot_index".
93    # If "incoming_voice_sim_slot_index" is NOT set in config file,
94    # then incoming voice call will call to the phone number of default
95    # subId.
96    if hasattr(ad, "incoming_voice_sim_slot_index"):
97        incoming_voice_sub_id = get_subid_from_slot_index(
98            log, ad, ad.incoming_voice_sim_slot_index)
99    else:
100        incoming_voice_sub_id = ad.droid.subscriptionGetDefaultVoiceSubId()
101    setattr(ad, "incoming_voice_sub_id", incoming_voice_sub_id)
102
103    # This is for Incoming SMS Sub ID
104    # If "incoming_message_sim_slot_index" is set in config file, then
105    # incoming SMS be sent to the phone number of the SIM in
106    # "incoming_message_sim_slot_index".
107    # If "incoming_message_sim_slot_index" is NOT set in config file,
108    # then incoming SMS be sent to the phone number of default
109    # subId.
110    if hasattr(ad, "incoming_message_sim_slot_index"):
111        incoming_message_sub_id = get_subid_from_slot_index(
112            log, ad, ad.incoming_message_sim_slot_index)
113    else:
114        incoming_message_sub_id = ad.droid.subscriptionGetDefaultSmsSubId()
115    setattr(ad, "incoming_message_sub_id", incoming_message_sub_id)
116
117
118def get_default_data_sub_id(ad):
119    """ Get default data subscription id
120    """
121    if hasattr(ad, "default_data_sub_id"):
122        return ad.default_data_sub_id
123    else:
124        return ad.droid.subscriptionGetDefaultDataSubId()
125
126
127def get_outgoing_message_sub_id(ad):
128    """ Get outgoing message subscription id
129    """
130    if hasattr(ad, "outgoing_message_sub_id"):
131        return ad.outgoing_message_sub_id
132    else:
133        return ad.droid.subscriptionGetDefaultSmsSubId()
134
135
136def get_outgoing_voice_sub_id(ad):
137    """ Get outgoing voice subscription id
138    """
139    if hasattr(ad, "outgoing_voice_sub_id"):
140        return ad.outgoing_voice_sub_id
141    else:
142        return ad.droid.subscriptionGetDefaultVoiceSubId()
143
144
145def get_incoming_voice_sub_id(ad):
146    """ Get incoming voice subscription id
147    """
148    if hasattr(ad, "incoming_voice_sub_id"):
149        return ad.incoming_voice_sub_id
150    else:
151        return ad.droid.subscriptionGetDefaultVoiceSubId()
152
153
154def get_incoming_message_sub_id(ad):
155    """ Get incoming message subscription id
156    """
157    if hasattr(ad, "incoming_message_sub_id"):
158        return ad.incoming_message_sub_id
159    else:
160        return ad.droid.subscriptionGetDefaultSmsSubId()
161
162
163def get_subid_by_adb(ad, sim_slot_index):
164    """Get the subscription ID for a SIM at a particular slot via adb command.
165
166    Args:
167        ad: android device object.
168        sim_slot_index: slot 0 or slot 1.
169
170    Returns:
171        Subscription ID.
172    """
173    try:
174        output = ad.adb.shell("dumpsys isub | grep subIds")
175        pattern = re.compile(r"sSlotIndexToSubId\[%d\]:\s*subIds=%d=\[(\d)\]" %
176            (sim_slot_index, sim_slot_index))
177        sub_id = pattern.findall(output)
178    except Exception as e:
179        error_msg = "%s due to %s" % ("Failed to get the subid", e)
180        ad.log.error(error_msg)
181        return INVALID_SUB_ID
182    return int(sub_id[0]) if sub_id else INVALID_SUB_ID
183
184
185def get_subid_from_slot_index(log, ad, sim_slot_index):
186    """ Get the subscription ID for a SIM at a particular slot.
187
188    Args:
189        ad: android_device object.
190        sim_slot_index: 0 for pSIM,
191                        1 for eSIM port 0,
192                        2 for eSIM port 1.
193
194    Returns:
195        result: Subscription ID
196    """
197    siminfo = ad.adb.shell(
198        "content query --uri content://telephony/siminfo")
199    if "port_index" in siminfo and getattr(ad, "mep", False):
200        pattern_port = re.compile(r"port_index=(\d)")
201        pattern_sub = re.compile(r" _id=(\d+)")
202        pattern_embedded = re.compile(r"is_embedded=(\d+)")
203        siminfo_list = siminfo.splitlines()
204        for row in siminfo_list:
205            sub_id = pattern_sub.findall(row)
206            sub_id = int(sub_id[-1]) if sub_id else INVALID_SUB_ID
207            port_id = pattern_port.findall(row)
208            port_id = int(port_id[-1]) if port_id else INVALID_PORT_INDEX
209            is_embedded = int(pattern_embedded.findall(row)[-1])
210            if port_id == INVALID_PORT_INDEX:
211                continue
212            elif sim_slot_index == 0 and is_embedded == 0:
213                return sub_id
214            elif sim_slot_index-1 == port_id and is_embedded == 1:
215                return sub_id
216    else:
217        sim_slot_index = 1 if sim_slot_index else 0
218        subInfo = ad.droid.subscriptionGetAllSubInfoList()
219        for info in subInfo:
220            if info['simSlotIndex'] == sim_slot_index:
221                return info['subscriptionId']
222    return INVALID_SUB_ID
223
224
225def get_subid_from_logical_slot(ad, logical_slot):
226    """ Get the subscription ID for a SIM at a particular logical slot.
227
228    Args:
229        ad: android_device object.
230        logical_slot: The logical slot(0 or 1).
231
232    Returns:
233        result: Subscription ID
234    """
235    logical_slot = 1 if logical_slot else 0
236    subInfo = ad.droid.subscriptionGetAllSubInfoList()
237    for info in subInfo:
238        if info['simSlotIndex'] == logical_slot:
239            return info['subscriptionId']
240    return INVALID_SUB_ID
241
242
243def get_operatorname_from_slot_index(ad, sim_slot_index):
244    """ Get the operator name for a SIM at a particular slot
245
246    Args:
247        ad: android_device object.
248
249    Returns:
250        result: Operator Name
251    """
252    subInfo = ad.droid.subscriptionGetAllSubInfoList()
253    for info in subInfo:
254        if info['simSlotIndex'] == sim_slot_index:
255            return info['displayName']
256    return None
257
258
259def get_carrierid_from_slot_index(ad, sim_slot_index):
260    """ Get the carrierId for a SIM at a particular slot
261
262    Args:
263        ad: android_device object.
264        sim_slot_index: slot 0 or slot 1
265
266    Returns:
267        result: CarrierId
268    """
269    subInfo = ad.droid.subscriptionGetAllSubInfoList()
270    for info in subInfo:
271        if info['simSlotIndex'] == sim_slot_index:
272            return info['carrierId']
273    return None
274
275
276def get_isopportunistic_from_slot_index(ad, sim_slot_index):
277    """ Get the isOppotunistic field for a particular slot
278
279    Args:
280        ad: android_device object.
281        sim_slot_index: slot 0 or slot 1
282
283    Returns:
284        result: True or False based on Value set
285    """
286    subInfo = ad.droid.subscriptionGetAllSubInfoList()
287    for info in subInfo:
288        if info['simSlotIndex'] == sim_slot_index:
289            return info['isOpportunistic']
290    return None
291
292
293def set_subid_for_data(ad, sub_id, time_to_sleep=WAIT_TIME_CHANGE_DATA_SUB_ID):
294    """Set subId for data
295
296    Args:
297        ad: android device object.
298        sub_id: subscription id (integer)
299
300    Returns:
301        None
302    """
303    # TODO: Need to check onSubscriptionChanged event. b/27843365
304    if ad.droid.subscriptionGetDefaultDataSubId() != sub_id:
305        ad.droid.subscriptionSetDefaultDataSubId(sub_id)
306        time.sleep(time_to_sleep)
307        setattr(ad, "default_data_sub_id", sub_id)
308
309
310def set_subid_for_message(ad, sub_id):
311    """Set subId for outgoing message
312
313    Args:
314        ad: android device object.
315        sub_id: subscription id (integer)
316
317    Returns:
318        None
319    """
320    ad.droid.subscriptionSetDefaultSmsSubId(sub_id)
321    if hasattr(ad, "outgoing_message_sub_id"):
322        ad.outgoing_message_sub_id = sub_id
323
324
325def set_message_subid(ad, sub_id):
326    """Set subId for both outgoing and incoming messages
327
328    Args:
329        ad: android device object.
330        sub_id: subscription id (integer)
331
332    Returns:
333        None
334    """
335    ad.droid.subscriptionSetDefaultSmsSubId(sub_id)
336    if hasattr(ad, "outgoing_message_sub_id"):
337        ad.outgoing_message_sub_id = sub_id
338    if hasattr(ad, "incoming_message_sub_id"):
339        ad.incoming_message_sub_id = sub_id
340
341
342def set_subid_for_outgoing_call(ad, sub_id):
343    """Set subId for outgoing voice call
344
345    Args:
346        ad: android device object.
347        sub_id: subscription id (integer)
348
349    Returns:
350        None
351    """
352    ad.droid.telecomSetUserSelectedOutgoingPhoneAccountBySubId(sub_id)
353    if hasattr(ad, "outgoing_voice_sub_id"):
354        ad.outgoing_voice_sub_id = sub_id
355
356
357def set_incoming_voice_sub_id(ad, sub_id):
358    """Set default subId for voice calls
359
360    Args:
361        ad: android device object.
362        sub_id: subscription id (integer)
363
364    Returns:
365        None
366    """
367    ad.droid.subscriptionSetDefaultVoiceSubId(sub_id)
368    if hasattr(ad, "incoming_voice_sub_id"):
369        ad.incoming_voice_sub_id = sub_id
370
371
372def set_voice_sub_id(ad, sub_id):
373    """Set default subId for both incoming and outgoing voice calls
374
375    Args:
376        ad: android device object.
377        sub_id: subscription id (integer)
378
379    Returns:
380        None
381    """
382    ad.droid.subscriptionSetDefaultVoiceSubId(sub_id)
383    if hasattr(ad, "incoming_voice_sub_id"):
384        ad.incoming_voice_sub_id = sub_id
385    if hasattr(ad, "outgoing_voice_sub_id"):
386        ad.outgoing_voice_sub_id = sub_id
387
388
389def set_default_sub_for_all_services(ad, slot_id=0):
390    """Set subId for all services
391
392    Args:
393        ad: android device object.
394        slot_id: 0 or 1 (integer)
395
396    Returns:
397        None
398    """
399    sub_id = get_subid_from_slot_index(ad.log, ad, slot_id)
400    ad.log.info("Default Subid for all service is %s", sub_id)
401    set_subid_for_outgoing_call(ad, sub_id)
402    set_incoming_voice_sub_id(ad, sub_id)
403    set_subid_for_data(ad, sub_id)
404    set_subid_for_message(ad, sub_id)
405    ad.droid.telephonyToggleDataConnection(True)
406
407
408def perform_dds_switch(ad):
409    slot_dict = {0: {}, 1: {}}
410    for slot in (0,1):
411        slot_dict[slot]['sub_id'] = get_subid_from_slot_index(ad.log, ad, slot)
412        slot_dict[slot]['operator'] = get_operatorname_from_slot_index(ad, slot)
413    ad.log.debug("%s", slot_dict)
414
415    current_data = get_default_data_sub_id(ad)
416    if slot_dict[0]['sub_id'] == current_data:
417        ad.log.info("DDS Switch from %s to %s", slot_dict[0]['operator'],
418                                                slot_dict[1]['operator'])
419        new_data = slot_dict[1]['sub_id']
420        new_oper = slot_dict[1]['operator']
421    else:
422        ad.log.info("DDS Switch from %s to %s", slot_dict[1]['operator'],
423                                                slot_dict[0]['operator'])
424        new_data = slot_dict[0]['sub_id']
425        new_oper = slot_dict[0]['operator']
426    set_subid_for_data(ad, new_data)
427    ad.droid.telephonyToggleDataConnection(True)
428    if get_default_data_sub_id(ad) == new_data:
429        return new_oper
430    else:
431        ad.log.error("DDS Switch Failed")
432        return False
433
434
435def set_dds_on_slot_0(ad):
436    sub_id = get_subid_from_slot_index(ad.log, ad, 0)
437    if sub_id == INVALID_SUB_ID:
438        ad.log.warning("Invalid sub ID at slot 0")
439        return False
440    operator = get_operatorname_from_slot_index(ad, 0)
441    if ad.droid.subscriptionGetDefaultDataSubId() == sub_id:
442        ad.log.info("Current DDS is already on Sub %s(%s)", sub_id, operator)
443        return True
444    ad.log.info("Setting DDS on Sub %s(%s)", sub_id, operator)
445    set_subid_for_data(ad, sub_id)
446    ad.droid.telephonyToggleDataConnection(True)
447    if get_default_data_sub_id(ad) == sub_id:
448        return True
449    else:
450        return False
451
452
453def set_dds_on_slot_1(ad):
454    sub_id = get_subid_from_slot_index(ad.log, ad, 1)
455    if sub_id == INVALID_SUB_ID:
456        ad.log.warning("Invalid sub ID at slot 1")
457        return False
458    operator = get_operatorname_from_slot_index(ad, 1)
459    if ad.droid.subscriptionGetDefaultDataSubId() == sub_id:
460        ad.log.info("Current DDS is already on Sub %s(%s)", sub_id, operator)
461        return True
462    ad.log.info("Setting DDS on Sub %s(%s)", sub_id, operator)
463    set_subid_for_data(ad, sub_id)
464    ad.droid.telephonyToggleDataConnection(True)
465    if get_default_data_sub_id(ad) == sub_id:
466        return True
467    else:
468        return False
469
470
471def set_dds_on_slot(ad, dds_slot):
472    """Switch DDS to given slot.
473
474    Args:
475        ad: android device object.
476        dds_slot: the slot which be set to DDS.
477                  0 for pSIM,
478                  1 for eSIM port 0,
479                  2 for eSIM port 1.
480
481    Returns:
482        True if success, False if fail.
483    """
484    sub_id = get_subid_from_slot_index(ad.log, ad, dds_slot)
485    if sub_id == INVALID_SUB_ID:
486        ad.log.warning("Invalid sub ID at slot %d", dds_slot)
487        return False
488    operator = get_operatorname_from_slot_index(ad, dds_slot)
489    if ad.droid.subscriptionGetDefaultDataSubId() == sub_id:
490        ad.log.info("Current DDS is already on Sub %s(%s)", sub_id, operator)
491        return True
492    ad.log.info("Setting DDS on Sub %s(%s)", sub_id, operator)
493    set_subid_for_data(ad, sub_id)
494    ad.droid.telephonyToggleDataConnection(True)
495    if get_default_data_sub_id(ad) == sub_id:
496        return True
497    else:
498        return False
499
500
501def set_always_allow_mms_data(ad, sub_id, state=True):
502    """Set always allow mms data on sub_id
503
504    Args:
505        ad: android device object.
506        sub_id: subscription id (integer)
507        state: True or False
508
509    Returns:
510        None
511    """
512    if any(model in ad.model for model in CHIPSET_MODELS_LIST):
513        ad.log.debug("SKIP telephonySetAlwaysAllowMmsData")
514    else:
515        ad.log.debug("telephonySetAlwaysAllowMmsData %s sub_id %s", state, sub_id)
516        ad.droid.telephonySetAlwaysAllowMmsData(sub_id, state)
517    return True
518
519
520def get_cbrs_and_default_sub_id(ad):
521    """Gets CBRS and Default SubId
522
523    Args:
524        ad: android device object.
525
526    Returns:
527        cbrs_subId
528        default_subId
529    """
530    cbrs_subid, default_subid = None, None
531    slot_dict = {0: {}, 1: {}}
532    for slot in (0, 1):
533        slot_dict[slot]['sub_id'] = get_subid_from_slot_index(
534            ad.log, ad, slot)
535        slot_dict[slot]['carrier_id'] = get_carrierid_from_slot_index(
536            ad, slot)
537        slot_dict[slot]['operator'] = get_operatorname_from_slot_index(
538            ad, slot)
539        if slot_dict[slot]['carrier_id'] == 2340:
540            cbrs_subid = slot_dict[slot]['sub_id']
541        else:
542            default_subid = slot_dict[slot]['sub_id']
543        ad.log.info("Slot %d - Sub %s - Carrier %d - %s", slot,
544                    slot_dict[slot]['sub_id'],
545                    slot_dict[slot]['carrier_id'],
546                    slot_dict[slot]['operator'])
547        if not cbrs_subid:
548            ad.log.error("CBRS sub_id is not ACTIVE")
549    return cbrs_subid, default_subid
550
551
552def get_subid_on_same_network_of_host_ad(ads, host_sub_id=None, type="voice"):
553    ad_host = ads[0]
554    ad_p1 = ads[1]
555
556    try:
557        ad_p2 = ads[2]
558    except:
559        ad_p2 = None
560
561    if not host_sub_id:
562        if type == "sms":
563            host_sub_id = get_outgoing_message_sub_id(ad_host)
564        else:
565            host_sub_id = get_incoming_voice_sub_id(ad_host)
566    host_mcc = ad_host.telephony["subscription"][host_sub_id]["mcc"]
567    host_mnc = ad_host.telephony["subscription"][host_sub_id]["mnc"]
568    p1_sub_id = INVALID_SUB_ID
569    p2_sub_id = INVALID_SUB_ID
570    p1_mcc = None
571    p1_mnc = None
572    p2_mcc = None
573    p2_mnc = None
574
575    for ad in [ad_p1, ad_p2]:
576        if ad:
577            for sub_id in ad.telephony["subscription"]:
578                mcc = ad.telephony["subscription"][sub_id]["mcc"]
579                mnc = ad.telephony["subscription"][sub_id]["mnc"]
580
581                if ad == ad_p1:
582                    if p1_sub_id == INVALID_SUB_ID:
583                        p1_sub_id = sub_id
584                    if not p1_mcc:
585                        p1_mcc = mcc
586                    if not p1_mnc:
587                        p1_mnc = mnc
588                elif ad == ad_p2:
589                    if p2_sub_id == INVALID_SUB_ID:
590                        p2_sub_id = sub_id
591                    if not p2_mcc:
592                        p2_mcc = mcc
593                    if not p2_mnc:
594                        p2_mnc = mnc
595
596                if mcc == host_mcc and mnc == host_mnc:
597                    if ad == ad_p1:
598                        p1_sub_id = sub_id
599                        p1_mcc = mcc
600                        p1_mnc = mnc
601
602                    elif ad == ad_p2:
603                        p2_sub_id = sub_id
604                        p2_mcc = mcc
605                        p2_mnc = mnc
606
607    return host_sub_id, p1_sub_id, p2_sub_id
608
609
610def get_slot_index_from_subid(ad, sub_id):
611    try:
612        info = ad.droid.subscriptionGetSubInfoForSubscriber(sub_id)
613        return info['simSlotIndex']
614    except KeyError:
615        return INVALID_SIM_SLOT_INDEX
616
617
618def get_slot_index_from_data_sub_id(ad):
619    """Get slot index from given sub ID for data
620
621    Args:
622        ad: Android object
623
624    Returns:
625        0 for pSIM or 1 for eSIM. Otherwise -1 will be returned.
626    """
627    data_sub_id = get_default_data_sub_id(ad)
628    sub_info = ad.droid.subscriptionGetAllSubInfoList()
629    for info in sub_info:
630        if info['subscriptionId'] == data_sub_id:
631            return info['simSlotIndex']
632    return INVALID_SUB_ID
633
634
635def get_slot_index_from_voice_sub_id(ad):
636    """Get slot index from the current voice sub ID.
637
638    Args:
639        ad: android object
640
641    Returns:
642        0: pSIM
643        1: eSIM
644        INVALID_SUB_ID (-1): if no sub ID is equal to current voice sub ID.
645    """
646    voice_sub_id = get_incoming_voice_sub_id(ad)
647    sub_info = ad.droid.subscriptionGetAllSubInfoList()
648    for info in sub_info:
649        if info['subscriptionId'] == voice_sub_id:
650            return info['simSlotIndex']
651    return INVALID_SUB_ID
652
653
654def get_all_sub_id(ad):
655    """Return all valid subscription IDs.
656
657    Args:
658        ad: Android object
659
660    Returns:
661        List containing all valid subscription IDs.
662    """
663    sub_id_list = []
664    sub_info = ad.droid.subscriptionGetAllSubInfoList()
665    for info in sub_info:
666        if info['simSlotIndex'] != INVALID_SUB_ID:
667            sub_id_list.append(info['subscriptionId'])
668
669    return sub_id_list