• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3.4
2#
3#   Copyright 2022 - The Android Open Source Project
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 collections
18import logging
19import os
20import re
21import time
22from queue import Empty
23from acts.controllers.adb_lib.error import AdbError
24from acts.controllers.android_lib.tel import tel_utils
25
26PCC_PRESET_MAPPING = {
27    'N257': {
28        'low': 2054999,
29        'mid': 2079165,
30        'high': 2090832
31    },
32    'N258': {
33        'low': 2017499,
34        'mid': 2043749,
35        'high': 2057499
36    },
37    'N260': {
38        'low': 2229999,
39        'mid': 2254165,
40        'high': 2265832
41    },
42    'N261': {
43        'low': 2071667
44    }
45}
46
47DUPLEX_MODE_TO_BAND_MAPPING = {
48    'LTE': {
49        'FDD': [
50            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 19, 20, 21,
51            22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 65, 66, 67, 68, 69, 70,
52            71, 72, 73, 74, 75, 76, 85, 252, 255
53        ],
54        'TDD': [
55            33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 45, 46, 47, 48,
56            50, 51, 53
57        ]
58    },
59    'NR5G': {
60        'FDD': [
61            'N1', 'N2', 'N3', 'N5', 'N7', 'N8', 'N12', 'N13', 'N14', 'N18',
62            'N20', 'N25', 'N26', 'N28', 'N30', 'N65', 'N66', 'N70', 'N71',
63            'N74'
64        ],
65        'TDD': [
66            'N34', 'N38', 'N39', 'N40', 'N41', 'N48', 'N50', 'N51', 'N53',
67            'N77', 'N78', 'N79', 'N90', 'N257', 'N258', 'N259', 'N260', 'N261'
68        ]
69    },
70}
71
72SHORT_SLEEP = 1
73LONG_SLEEP = 10
74
75POWER_STATS_DUMPSYS_CMD = 'dumpsys android.hardware.power.stats.IPowerStats/default delta'
76
77def extract_test_id(testcase_params, id_fields):
78    test_id = collections.OrderedDict(
79        (param, testcase_params[param]) for param in id_fields)
80    return test_id
81
82def generate_endc_combo_config_from_string(endc_combo_str):
83    """Function to generate ENDC combo config from combo string
84
85    Args:
86        endc_combo_str: ENDC combo descriptor (e.g. B48A[4];A[1]+N5A[2];A[1])
87    Returns:
88        endc_combo_config: dictionary with all ENDC combo settings
89    """
90    endc_combo_config = collections.OrderedDict()
91    endc_combo_config['endc_combo_name'] = endc_combo_str
92    endc_combo_str = endc_combo_str.replace(' ', '')
93    endc_combo_list = endc_combo_str.split('+')
94    cell_config_list = list()
95    lte_cell_count = 0
96    nr_cell_count = 0
97    lte_scc_list = []
98    nr_dl_carriers = []
99    nr_ul_carriers = []
100    lte_dl_carriers = []
101    lte_ul_carriers = []
102
103    cell_config_regex = re.compile(
104        r'(?P<cell_type>[B,N])(?P<band>[0-9]+)(?P<bandwidth_class>[A-Z])\[bw=(?P<dl_bandwidth>[0-9]+)\]'
105        r'(\[ch=)?(?P<channel>[0-9]+)?\]?'
106        r'\[ant=(?P<dl_mimo_config>[0-9]+),?(?P<transmission_mode>[TM0-9]+)?,?(?P<num_layers>[0-9]+)?,?(?P<num_codewords>[0-9]+)?\]?;?'
107        r'(?P<ul_bandwidth_class>[A-Z])?(\[ant=)?(?P<ul_mimo_config>[0-9])?(\])?'
108    )
109    for cell_string in endc_combo_list:
110        cell_config = re.match(cell_config_regex, cell_string).groupdict()
111        if cell_config['cell_type'] == 'B':
112            # Configure LTE specific parameters
113            cell_config['cell_type'] = 'LTE'
114            lte_cell_count = lte_cell_count + 1
115            cell_config['cell_number'] = lte_cell_count
116            if cell_config['cell_number'] == 1:
117                cell_config['pcc'] = 1
118                endc_combo_config['lte_pcc'] = cell_config['cell_number']
119            else:
120                cell_config['pcc'] = 0
121                lte_scc_list.append(cell_config['cell_number'])
122            cell_config['duplex_mode'] = 'FDD' if int(
123                cell_config['band']
124            ) in DUPLEX_MODE_TO_BAND_MAPPING['LTE']['FDD'] else 'TDD'
125            cell_config['dl_mimo_config'] = 'D{nss}U{nss}'.format(
126                nss=cell_config['dl_mimo_config'])
127            cell_config['dl_subframe_allocation'] = [1] * 10
128            cell_config['tdd_frame_config'] = 5
129            cell_config['tdd_ssf_config']=8
130            cell_config['tdd_ssf_config'] = 8
131            lte_dl_carriers.append(cell_config['cell_number'])
132        else:
133            # Configure NR specific parameters
134            cell_config['cell_type'] = 'NR5G'
135            nr_cell_count = nr_cell_count + 1
136            cell_config['cell_number'] = nr_cell_count
137            nr_dl_carriers.append(cell_config['cell_number'])
138            #TODO: fix NSA/SA indicator
139            cell_config['nr_cell_type'] = 'NSA'
140            cell_config['band'] = 'N' + cell_config['band']
141            cell_config['duplex_mode'] = 'FDD' if cell_config[
142                'band'] in DUPLEX_MODE_TO_BAND_MAPPING['NR5G']['FDD'] else 'TDD'
143            cell_config['subcarrier_spacing'] = 'MU0' if cell_config[
144                'duplex_mode'] == 'FDD' else 'MU1'
145            cell_config['dl_mimo_config'] = 'N{nss}X{nss}'.format(
146                nss=cell_config['dl_mimo_config'])
147
148        cell_config['dl_bandwidth_class'] = cell_config['bandwidth_class']
149        cell_config['dl_bandwidth'] = 'BW' + cell_config['dl_bandwidth']
150        cell_config[
151            'ul_enabled'] = 1 if cell_config['ul_bandwidth_class'] else 0
152        if cell_config['ul_enabled']:
153            cell_config['ul_mimo_config'] = 'N{nss}X{nss}'.format(
154                nss=cell_config['ul_mimo_config'])
155            if cell_config['cell_type'] == 'LTE':
156                lte_ul_carriers.append(cell_config['cell_number'])
157            elif cell_config['cell_type'] == 'NR5G':
158                nr_ul_carriers.append(cell_config['cell_number'])
159        cell_config_list.append(cell_config)
160    endc_combo_config['lte_cell_count'] = lte_cell_count
161    endc_combo_config['nr_cell_count'] = nr_cell_count
162    endc_combo_config['nr_dl_carriers'] = nr_dl_carriers
163    endc_combo_config['nr_ul_carriers'] = nr_ul_carriers
164    endc_combo_config['cell_list'] = cell_config_list
165    endc_combo_config['lte_scc_list'] = lte_scc_list
166    endc_combo_config['lte_dl_carriers'] = lte_dl_carriers
167    endc_combo_config['lte_ul_carriers'] = lte_ul_carriers
168    return endc_combo_config
169
170
171def generate_endc_combo_config_from_csv_row(test_config):
172    """Function to generate ENDC combo config from CSV test config
173
174    Args:
175        test_config: dict containing ENDC combo config from CSV
176    Returns:
177        endc_combo_config: dictionary with all ENDC combo settings
178    """
179    for key, value in test_config.items():
180        if value == '':
181            test_config[key] = None
182    endc_combo_config = collections.OrderedDict()
183    lte_cell_count = 0
184    nr_cell_count = 0
185    lte_scc_list = []
186    nr_dl_carriers = []
187    nr_ul_carriers = []
188    lte_dl_carriers = []
189    lte_ul_carriers = []
190
191    cell_config_list = []
192    if 'lte_band' in test_config and test_config['lte_band']:
193        lte_cell = {
194            'cell_type': 'LTE',
195            'cell_number': 1,
196            'pcc': 1,
197            'band': test_config['lte_band'],
198            'dl_bandwidth': test_config['lte_bandwidth'],
199            'ul_enabled': 1,
200            'duplex_mode': test_config['lte_duplex_mode'],
201            'dl_mimo_config': 'D{nss}U{nss}'.format(nss=test_config['lte_dl_mimo_config']),
202            'ul_mimo_config': 'D{nss}U{nss}'.format(nss=test_config['lte_ul_mimo_config']),
203            'transmission_mode': test_config['lte_tm_mode'],
204            'num_codewords': test_config['lte_codewords'],
205            'num_layers': test_config['lte_layers'],
206            'dl_subframe_allocation':
207                list(test_config['lte_dl_subframe_allocation']) if test_config['lte_dl_subframe_allocation'] else [1]*10,
208            'tdd_frame_config': test_config['lte_tdd_frame_config'],
209            'tdd_ssf_config': test_config['lte_tdd_ssf_config']
210        }
211        cell_config_list.append(lte_cell)
212        endc_combo_config['lte_pcc'] = 1
213        lte_cell_count = 1
214        lte_dl_carriers = [1]
215        lte_ul_carriers = [1]
216
217    if 'nr_band' in test_config and test_config['nr_band']:
218        nr_cell = {
219            'cell_type':
220            'NR5G',
221            'cell_number':
222            1,
223            'band':
224            test_config['nr_band'],
225            'nr_cell_type':
226            test_config['nr_cell_type'],
227            'duplex_mode':
228            test_config['nr_duplex_mode'],
229            'dl_mimo_config':
230            'N{nss}X{nss}'.format(nss=test_config['nr_dl_mimo_config']),
231            'dl_bandwidth_class':
232            'A',
233            'dl_bandwidth':
234            test_config['nr_bandwidth'],
235            'ul_enabled':
236            1,
237            'ul_bandwidth_class':
238            'A',
239            'ul_mimo_config':
240            'N{nss}X{nss}'.format(nss=test_config['nr_ul_mimo_config']),
241            'subcarrier_spacing':
242            'MU0' if test_config['nr_scs'] == '15' else 'MU1'
243        }
244        cell_config_list.append(nr_cell)
245        nr_cell_count = 1
246        nr_dl_carriers = [1]
247        nr_ul_carriers = [1]
248
249    endc_combo_config['lte_cell_count'] = lte_cell_count
250    endc_combo_config['nr_cell_count'] = nr_cell_count
251    endc_combo_config['nr_dl_carriers'] = nr_dl_carriers
252    endc_combo_config['nr_ul_carriers'] = nr_ul_carriers
253    endc_combo_config['cell_list'] = cell_config_list
254    endc_combo_config['lte_scc_list'] = lte_scc_list
255    endc_combo_config['lte_dl_carriers'] = lte_dl_carriers
256    endc_combo_config['lte_ul_carriers'] = lte_ul_carriers
257    return endc_combo_config
258
259
260class DeviceUtils():
261
262    def __new__(self, dut, log):
263        if hasattr(dut,
264                   'device_type') and dut.device_type == 'android_non_pixel':
265            return AndroidNonPixelDeviceUtils(dut, log)
266        else:
267            return PixelDeviceUtils(dut, log)
268
269
270class PixelDeviceUtils():
271
272    def __init__(self, dut, log):
273        self.dut = dut
274        self.log = log
275        self.set_screen_timeout(15)
276
277    def stop_services(self):
278        """Gracefully stop sl4a before power measurement"""
279        self.dut.stop_services()
280
281    def start_services(self):
282        self.dut.start_services()
283
284    def start_pixel_logger(self):
285        """Function to start pixel logger with default log mask.
286
287        Args:
288            ad: android device on which to start logger
289        """
290
291        try:
292            self.dut.adb.shell(
293                'rm -R /storage/emulated/0/Android/data/com.android.pixellogger/files/logs/logs/'
294            )
295        except:
296            pass
297        self.dut.adb.shell(
298            'am startservice -a com.android.pixellogger.service.logging.LoggingService.ACTION_START_LOGGING'
299        )
300
301    def stop_pixel_logger(self, log_path, tag=None):
302        """Function to stop pixel logger and retrieve logs
303
304        Args:
305            ad: android device on which to start logger
306            log_path: location of saved logs
307        """
308        self.dut.adb.shell(
309            'am startservice -a com.android.pixellogger.service.logging.LoggingService.ACTION_STOP_LOGGING'
310        )
311        logging.info('Waiting for Pixel log file')
312        file_name = None
313        file_size = 0
314        previous_file_size = 0
315        for idx in range(600):
316            try:
317                file = self.dut.adb.shell(
318                    'ls -l /storage/emulated/0/Android/data/com.android.pixellogger/files/logs/logs/'
319                ).split(' ')
320                file_name = file[-1]
321                file_size = file[-4]
322            except:
323                file_name = None
324                file_size = 0
325            if file_name and file_size == previous_file_size:
326                logging.info('Log file found after {}s.'.format(idx))
327                break
328            else:
329                previous_file_size = file_size
330                time.sleep(1)
331        try:
332            local_file_name = '{}_{}'.format(file_name,
333                                             tag) if tag else file_name
334            local_path = os.path.join(log_path, local_file_name)
335            self.dut.pull_files(
336                '/storage/emulated/0/Android/data/com.android.pixellogger/files/logs/logs/{}'
337                .format(file_name), log_path)
338            return local_path
339        except:
340            logging.error('Could not pull pixel logs.')
341
342    def log_system_power_metrics(self, verbose=1):
343        # Log temperature sensors
344        if verbose:
345            temp_sensors = self.dut.adb.shell(
346                'ls -1 /dev/thermal/tz-by-name/').splitlines()
347        else:
348            temp_sensors = ['BIG', 'battery', 'quiet_therm', 'usb_pwr_therm']
349        temp_measurements = collections.OrderedDict()
350        for sensor in temp_sensors:
351            try:
352                temp_measurements[sensor] = self.dut.adb.shell(
353                    'cat /dev/thermal/tz-by-name/{}/temp'.format(sensor))
354            except:
355                temp_measurements[sensor] = float('nan')
356        logging.debug(
357            'Temperature sensor readings: {}'.format(temp_measurements))
358
359        # Log mitigation items
360        if verbose:
361            mitigation_points = [
362                "batoilo",
363                "ocp_cpu1",
364                "ocp_cpu2",
365                "ocp_gpu",
366                "ocp_tpu",
367                "smpl_warn",
368                "soft_ocp_cpu1",
369                "soft_ocp_cpu2",
370                "soft_ocp_gpu",
371                "soft_ocp_tpu",
372                "vdroop1",
373                "vdroop2",
374            ]
375        else:
376            mitigation_points = [
377                "batoilo",
378                "smpl_warn",
379                "vdroop1",
380                "vdroop2",
381            ]
382
383        parameters_f = ['count', 'capacity', 'timestamp', 'voltage']
384        parameters_v = ['count', 'cap', 'time', 'volt']
385        mitigation_measurements = collections.OrderedDict()
386        for mp in mitigation_points:
387            mitigation_measurements[mp] = collections.OrderedDict()
388            for par_f, par_v in zip(parameters_f, parameters_v):
389                mitigation_measurements[mp][par_v] = self.dut.adb.shell(
390                    'cat /sys/devices/virtual/pmic/mitigation/last_triggered_{}/{}_{}'
391                    .format(par_f, mp, par_v))
392        logging.debug(
393            'Mitigation readings: {}'.format(mitigation_measurements))
394
395        # Log power meter items
396        power_meter_measurements = collections.OrderedDict()
397        for device in ['device0', 'device1']:
398            power_str = self.dut.adb.shell(
399                'cat /sys/bus/iio/devices/iio:{}/lpf_power'.format(
400                    device)).splitlines()
401            power_meter_measurements[device] = collections.OrderedDict()
402            for line in power_str:
403                if line.startswith('CH'):
404                    try:
405                        line_split = line.split(', ')
406                        power_meter_measurements[device][line_split[0]] = int(
407                            line_split[1])
408                    except (IndexError, ValueError):
409                        continue
410                elif line.startswith('t='):
411                    try:
412                        power_meter_measurements[device]['t_pmeter'] = int(
413                            line[2:])
414                    except (IndexError, ValueError):
415                        continue
416                else:
417                    continue
418            logging.debug(
419                'Power Meter readings: {}'.format(power_meter_measurements))
420
421            # Log battery items
422            if verbose:
423                battery_parameters = [
424                    "act_impedance", "capacity", "charge_counter",
425                    "charge_full", "charge_full_design", "current_avg",
426                    "current_now", "cycle_count", "health", "offmode_charger",
427                    "present", "rc_switch_enable", "resistance", "status",
428                    "temp", "voltage_avg", "voltage_now", "voltage_ocv"
429                ]
430            else:
431                battery_parameters = [
432                    "capacity", "current_avg", "current_now", "voltage_avg",
433                    "voltage_now", "voltage_ocv"
434                ]
435
436            battery_meaurements = collections.OrderedDict()
437            for par in battery_parameters:
438                battery_meaurements['bat_{}'.format(par)] = self.dut.adb.shell(
439                    'cat /sys/class/power_supply/maxfg/{}'.format(par))
440            logging.debug('Battery readings: {}'.format(battery_meaurements))
441
442    def log_odpm(self, file_path):
443        """Dumpsys ODPM data and save it."""
444        try:
445            stats = self.dut.adb.shell(POWER_STATS_DUMPSYS_CMD)
446            with open(file_path, 'w') as f:
447                f.write(stats)
448        except AdbError as e:
449            self.log.warning('Error dumping and saving odpm')
450
451    def send_at_command(self, at_command):
452        at_cmd_output = self.dut.adb.shell(
453            'am instrument -w -e request {} -e response wait '
454            '"com.google.mdstest/com.google.mdstest.instrument.ModemATCommandInstrumentation"'
455            .format(at_command))
456        return at_cmd_output
457
458    def get_rx_measurements(self, cell_type):
459        cell_type_int = 7 if cell_type == 'LTE' else 8
460        try:
461            rx_meas = self.send_at_command(
462                'AT+GOOGGETRXMEAS\={}?'.format(cell_type_int))
463        except:
464            rx_meas = ''
465        rsrp_regex = r"RSRP\[\d+\]\s+(-?\d+)"
466        rsrp_values = [float(x) for x in re.findall(rsrp_regex, rx_meas)]
467        rsrq_regex = r"RSRQ\[\d+\]\s+(-?\d+)"
468        rsrq_values = [float(x) for x in re.findall(rsrq_regex, rx_meas)]
469        rssi_regex = r"RSSI\[\d+\]\s+(-?\d+)"
470        rssi_values = [float(x) for x in re.findall(rssi_regex, rx_meas)]
471        sinr_regex = r"SINR\[\d+\]\s+(-?\d+)"
472        sinr_values = [float(x) for x in re.findall(sinr_regex, rx_meas)]
473        return {
474            'rsrp': rsrp_values,
475            'rsrq': rsrq_values,
476            'rssi': rssi_values,
477            'sinr': sinr_values
478        }
479
480    def get_fr2_tx_power(self):
481        try:
482            tx_power = self.send_at_command('AT+MMPDREAD=0,2,0')
483        except:
484            tx_power = ''
485        logging.info(tx_power)
486
487    def toggle_airplane_mode(self,
488                             new_state=None,
489                             strict_checking=True,
490                             try_index=0):
491        """ Toggle the state of airplane mode.
492
493        Args:
494            log: log handler.
495            ad: android_device object.
496            new_state: Airplane mode state to set to.
497                If None, opposite of the current state.
498            strict_checking: Whether to turn on strict checking that checks all features.
499            try_index: index of apm toggle
500
501        Returns:
502            result: True if operation succeed. False if error happens.
503        """
504        if hasattr(
505                self.dut, 'toggle_airplane_mode'
506        ) and 'at_command' in self.dut.toggle_airplane_mode['method']:
507            cfun_setting = 0 if new_state else 1
508            self.log.info(
509                'Toggling airplane mode {} by AT command.'.format(new_state))
510            self.send_at_command('AT+CFUN={}'.format(cfun_setting))
511        elif self.dut.skip_sl4a or try_index % 2 == 0:
512            self.log.info(
513                'Toggling airplane mode {} by adb.'.format(new_state))
514            return tel_utils.toggle_airplane_mode_by_adb(
515                self.log, self.dut, new_state)
516        else:
517            self.log.info(
518                'Toggling airplane mode {} by msim.'.format(new_state))
519            return self.toggle_airplane_mode_msim(
520                new_state, strict_checking=strict_checking)
521
522    def toggle_airplane_mode_msim(self, new_state=None, strict_checking=True):
523        """ Toggle the state of airplane mode.
524
525        Args:
526            log: log handler.
527            ad: android_device object.
528            new_state: Airplane mode state to set to.
529                If None, opposite of the current state.
530            strict_checking: Whether to turn on strict checking that checks all features.
531
532        Returns:
533            result: True if operation succeed. False if error happens.
534        """
535
536        cur_state = self.dut.droid.connectivityCheckAirplaneMode()
537        if cur_state == new_state:
538            self.dut.log.info("Airplane mode already in %s", new_state)
539            return True
540        elif new_state is None:
541            new_state = not cur_state
542            self.dut.log.info("Toggle APM mode, from current tate %s to %s",
543                              cur_state, new_state)
544        sub_id_list = []
545        active_sub_info = self.dut.droid.subscriptionGetAllSubInfoList()
546        if active_sub_info:
547            for info in active_sub_info:
548                sub_id_list.append(info['subscriptionId'])
549
550        self.dut.ed.clear_all_events()
551        time.sleep(0.1)
552        service_state_list = []
553        if new_state:
554            service_state_list.append(tel_utils.SERVICE_STATE_POWER_OFF)
555            self.dut.log.info("Turn on airplane mode")
556
557        else:
558            # If either one of these 3 events show up, it should be OK.
559            # Normal SIM, phone in service
560            service_state_list.append(tel_utils.SERVICE_STATE_IN_SERVICE)
561            # NO SIM, or Dead SIM, or no Roaming coverage.
562            service_state_list.append(tel_utils.SERVICE_STATE_OUT_OF_SERVICE)
563            service_state_list.append(tel_utils.SERVICE_STATE_EMERGENCY_ONLY)
564            self.dut.log.info("Turn off airplane mode")
565
566        for sub_id in sub_id_list:
567            self.dut.droid.telephonyStartTrackingServiceStateChangeForSubscription(
568                sub_id)
569
570        timeout_time = time.time() + LONG_SLEEP
571        self.dut.droid.connectivityToggleAirplaneMode(new_state)
572
573        try:
574            try:
575                event = self.dut.ed.wait_for_event(
576                    tel_utils.EVENT_SERVICE_STATE_CHANGED,
577                    tel_utils.is_event_match_for_list,
578                    timeout=LONG_SLEEP,
579                    field=tel_utils.ServiceStateContainer.SERVICE_STATE,
580                    value_list=service_state_list)
581                self.dut.log.info("Got event %s", event)
582            except Empty:
583                self.dut.log.warning(
584                    "Did not get expected service state change to %s",
585                    service_state_list)
586            finally:
587                for sub_id in sub_id_list:
588                    self.dut.droid.telephonyStopTrackingServiceStateChangeForSubscription(
589                        sub_id)
590        except Exception as e:
591            self.dut.log.error(e)
592
593        # APM on (new_state=True) will turn off bluetooth but may not turn it on
594        try:
595            if new_state and not tel_utils._wait_for_bluetooth_in_state(
596                    self.log, self.dut, False, timeout_time - time.time()):
597                self.dut.log.error(
598                    "Failed waiting for bluetooth during airplane mode toggle")
599                if strict_checking: return False
600        except Exception as e:
601            self.dut.log.error("Failed to check bluetooth state due to %s", e)
602            if strict_checking:
603                raise
604
605        # APM on (new_state=True) will turn off wifi but may not turn it on
606        if new_state and not tel_utils._wait_for_wifi_in_state(
607                self.log, self.dut, False, timeout_time - time.time()):
608            self.dut.log.error(
609                "Failed waiting for wifi during airplane mode toggle on")
610            if strict_checking: return False
611
612        if self.dut.droid.connectivityCheckAirplaneMode() != new_state:
613            self.dut.log.error("Set airplane mode to %s failed", new_state)
614            return False
615        return True
616
617    def set_screen_timeout(self, timeout=5):
618        self.dut.adb.shell('settings put system screen_off_timeout {}'.format(
619            timeout * 1000))
620    def get_screen_state(self):
621        screen_state_output = self.dut.adb.shell(
622            "dumpsys display | grep 'mScreenState'")
623        if 'ON' in screen_state_output:
624            return 1
625        else:
626            return 0
627
628    def set_screen_state(self, state):
629        curr_state = self.get_screen_state()
630        if state == curr_state:
631            self.log.debug('Screen state already {}'.format(state))
632        elif state == True:
633            self.dut.adb.shell('input keyevent KEYCODE_WAKEUP')
634        elif state == False:
635            self.dut.adb.shell('input keyevent KEYCODE_SLEEP')
636
637    def go_to_sleep(self):
638        if self.dut.skip_sl4a:
639            self.set_screen_state(0)
640        else:
641            self.dut.droid.goToSleepNow()
642
643class AndroidNonPixelDeviceUtils():
644
645    def __init__(self, dut, log):
646        self.dut = dut
647        self.log = log
648        self.set_screen_timeout()
649        if getattr(dut, "stop_logcat", 0):
650            self.log.info('Stopping ADB logcat.')
651            dut.stop_adb_logcat()
652
653    def start_services(self):
654        self.log.debug('stop_services not supported on non_pixel devices')
655
656    def stop_services(self):
657        self.log.debug('stop_services not supported on non_pixel devices')
658
659    def start_pixel_logger(self):
660        self.log.debug('start_pixel_logger not supported on non_pixel devices')
661
662    def stop_pixel_logger(self, log_path, tag=None):
663        self.log.debug('stop_pixel_logger not supported on non_pixel devices')
664
665    def log_system_power_metrics(self, verbose=1):
666        self.log.debug(
667            'log_system_power_metrics not supported on non_pixel devices')
668
669    def log_odpm(self, file_path):
670        self.log.debug('log_odpm not supported on non_pixel devices')
671
672    def send_at_command(self, at_command):
673        self.log.debug('send_at_command not supported on non_pixel devices')
674
675    def get_rx_measurements(self, cell_type):
676        self.log.debug(
677            'get_rx_measurements not supported on non_pixel devices')
678
679    def get_tx_measurements(self, cell_type):
680        self.log.debug(
681            'get_tx_measurements not supported on non_pixel devices')
682
683    def toggle_airplane_mode(self,
684                             new_state=None,
685                             strict_checking=True,
686                             try_index=0):
687        cur_state = bool(
688            int(self.dut.adb.shell("settings get global airplane_mode_on")))
689        if new_state == cur_state:
690            self.log.info(
691                'Airplane mode already in {} state.'.format(cur_state))
692        else:
693            self.tap_airplane_mode()
694
695    def get_screen_state(self):
696        screen_state_output = self.dut.adb.shell(
697            "dumpsys display | grep 'mScreenState'")
698        if 'ON' in screen_state_output:
699            return 1
700        else:
701            return 0
702
703    def set_screen_state(self, state):
704        curr_state = self.get_screen_state()
705        if state == curr_state:
706            self.log.debug('Screen state already {}'.format(state))
707        elif state == True:
708            self.dut.adb.shell('input keyevent KEYCODE_WAKEUP')
709        elif state == False:
710            self.dut.adb.shell('input keyevent KEYCODE_SLEEP')
711
712    def go_to_sleep(self):
713        self.set_screen_state(0)
714
715    def set_screen_timeout(self, timeout=5):
716        self.dut.adb.shell('settings put system screen_off_timeout {}'.format(
717            timeout * 1000))
718
719    def tap_airplane_mode(self):
720        self.set_screen_state(1)
721        for command in self.dut.toggle_airplane_mode['screen_routine']:
722            self.dut.adb.shell(command)
723            time.sleep(SHORT_SLEEP)
724        self.set_screen_state(0)
725