1#!/usr/bin/env python3
2#
3#   Copyright 2020 - 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.
16import time
17
18from acts import signals
19from acts import utils
20from acts_contrib.test_utils.power.PowerBaseTest import PowerBaseTest
21from acts_contrib.test_utils.gnss import gnss_test_utils as gutils
22from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
23
24DEFAULT_WAIT_TIME = 60
25DPO_NV_VALUE = '15DC'
26MDS_TEST_PACKAGE = 'com.google.mdstest'
27MDS_RUNNER = 'com.google.mdstest.instrument.ModemConfigInstrumentation'
28
29
30class PowerGTWGnssBaseTest(PowerBaseTest):
31    """Power GTW Gnss Base test"""
32
33    def setup_class(self):
34        super().setup_class()
35        self.ad = self.android_devices[0]
36        req_params = [
37            'wifi_network', 'test_location', 'qdsp6m_path',
38            'calibrate_target', 'interval', 'meas_interval'
39        ]
40        self.unpack_userparams(req_param_names=req_params)
41        self.set_xtra_data()
42
43    def setup_test(self):
44        gutils.log_current_epoch_time(self.ad, "test_start_time")
45        super().setup_test()
46        # Enable DPO
47        self.enable_DPO(True)
48        # Enable GNSS setting for GNSS standalone mode
49        self.ad.adb.shell('settings put secure location_mode 3')
50        # Recover attenuation value to strong CN
51        self.set_attenuation(self.atten_level['strong_signal'])
52
53    def teardown_test(self):
54        begin_time = utils.get_current_epoch_time()
55        self.ad.take_bug_report(self.test_name, begin_time)
56        gutils.get_gnss_qxdm_log(self.ad, self.qdsp6m_path)
57        gutils.log_current_epoch_time(self.ad, "test_end_time")
58
59    def set_xtra_data(self):
60        gutils.disable_xtra_throttle(self.ad)
61        self.turn_on_wifi_connection()
62        gutils.enable_gnss_verbose_logging(self.ad)
63        gutils.start_gnss_by_gtw_gpstool(self.ad, True, 'gnss')
64        time.sleep(30)
65        gutils.start_gnss_by_gtw_gpstool(self.ad, False, 'gnss')
66
67    def turn_on_wifi_connection(self):
68        """Turn on wifi connection."""
69        wutils.wifi_toggle_state(self.ad, True)
70        gutils.connect_to_wifi_network(self.ad, self.wifi_network)
71
72    def set_cell_only(self):
73        """Turn off wifi connection, enable cell service."""
74        wutils.wifi_toggle_state(self.ad, False)
75        utils.force_airplane_mode(self.ad, False)
76
77    def baseline_test(self):
78        """Baseline power measurement"""
79        self.ad.droid.goToSleepNow()
80        self.collect_power_data()
81        self.ad.log.info('TestResult AVG_Current %.2f' % self.avg_current)
82
83    def start_gnss_tracking_with_power_data(self,
84                                            mode='default',
85                                            is_signal=True,
86                                            freq=0,
87                                            lowpower=False,
88                                            meas=False):
89        """Start GNSS tracking and collect power metrics.
90
91        Args:
92            is_signal: default True, False for no Gnss signal test.
93            freq: an integer to set location update frequency.
94            lowpower: a boolean to set GNSS Low Power Mode.
95            meas: a boolean to set GNSS Measurement registeration.
96        """
97        c_power, c_tracking, c_acquisition = self.request_power_stat()
98        self.ad.adb.shell('settings put secure location_mode 3')
99        gutils.start_gnss_by_gtw_gpstool(self.ad, True, 'gnss', True, freq,
100                                         lowpower, meas)
101        self.ad.droid.goToSleepNow()
102
103        self.ad.log.info('Collect SV data for %d seconds.' % DEFAULT_WAIT_TIME)
104        time.sleep(DEFAULT_WAIT_TIME)
105
106        samples = self.collect_power_data()
107        self.ad.log.info('TestResult AVG_Current %.2f' % self.avg_current)
108        self.calibrate_avg_current(samples)
109        self.ad.send_keycode('WAKEUP')
110
111        gutils.start_gnss_by_gtw_gpstool(self.ad, False, 'gnss')
112        n_power, n_tracking, n_acquisition = self.request_power_stat()
113        self.ad.log.info("TestResult Total_power: %.2f" %(n_power - c_power))
114        self.ad.log.info("TestResult Tracking: %.2f" %(n_tracking - c_tracking))
115        self.ad.log.info("TestResult Acquisition: %.2f" %(n_acquisition - c_acquisition))
116        gutils.parse_gtw_gpstool_log(self.ad, self.test_location, type='gnss')
117
118    def calibrate_avg_current(self, samples):
119        """Calibrate average current by filtering AP wake up current with target
120           value.
121
122        Args:
123            samples: a list of tuples where the first element is a timestamp
124            and the second element is a current sample.
125        """
126        calibrate_results = [
127            sample[1] * 1000 for sample in samples
128            if sample[1] * 1000 < self.calibrate_target
129        ]
130        avg_current = sum(calibrate_results) / len(calibrate_results)
131        self.ad.log.info('TestResult Calibrate_AVG_Current %.2f' % avg_current)
132
133    def enable_DPO(self, enable):
134        """Enable or disable the DPO option.
135
136        Args:
137            enable: True or False to enable DPO.
138        """
139        self.ad.log.info('Change DPO to new state: %s.' % enable)
140        val = '02' if enable else '00'
141        options = {'request': 'writeNV', 'item': DPO_NV_VALUE, 'data': val}
142        instrument_cmd = gutils.build_instrumentation_call(
143            MDS_TEST_PACKAGE, MDS_RUNNER, options=options)
144        result = self.ad.adb.shell(instrument_cmd)
145        if 'SUCCESS' not in result:
146            self.ad.log.info(result)
147            raise signals.TestFailure('DPO is not able to Turn: %s' % enable)
148        self.dut_rockbottom()
149
150    def request_power_stat(self):
151        """Request the power state via command.
152        Returns:
153            total_power, tracking, acquisition power consumption.
154            If the device does not support, return 0, 0, 0
155        """
156        self.ad.adb.shell('cmd location providers send-extra-command gps request_power_stats')
157        time.sleep(1)
158        res = self.ad.adb.shell('dumpsys location | grep -A 10 -i \'power stats\'')
159        if res:
160            for line in res.split("\n"):
161                if "total power" in line:
162                    total_power = line.split(" ")[-1].split("mJ")[0]
163                if "single-band tracking" in line:
164                    single_tracking = line.split(" ")[-1].split("mJ")[0]
165                    self.ad.log.info(single_tracking)
166                if "multi-band tracking" in line:
167                    multi_tracking = line.split(" ")[-1].split("mJ")[0]
168                if "single-band acquisition" in line:
169                    single_acquisition = line.split(" ")[-1].split("mJ")[0]
170                if "multi-band acquisition" in line:
171                    multi_acquisition = line.split(" ")[-1].split("mJ")[0]
172            tracking = float(single_tracking) + float(multi_tracking)
173            acquisition = float(single_acquisition) + float(multi_acquisition)
174            self.ad.log.info("total power: %.2f" %float(total_power))
175            self.ad.log.info("tracking: %.2f" %tracking)
176            self.ad.log.info("acquisition: %.2f" %acquisition)
177            return float(total_power), tracking, acquisition
178        return 0, 0, 0
179