1#!/usr/bin/env python3.4
2#
3#   Copyright 2016 - 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 os
18import threading
19import time
20
21from acts import base_test
22from acts import asserts
23from acts import utils
24from acts.controllers import adb
25from acts.controllers import iperf_server as ip_server
26from acts.controllers import monsoon
27from acts.test_decorators import test_tracker_info
28from acts.test_utils.wifi import wifi_test_utils as wutils
29from acts.utils import force_airplane_mode
30from acts.utils import set_adaptive_brightness
31from acts.utils import set_ambient_display
32from acts.utils import set_auto_rotate
33from acts.utils import set_location_service
34
35pmc_base_cmd = ("am broadcast -a com.android.pmc.action.AUTOPOWER --es"
36                " PowerAction ")
37start_pmc_cmd = ("am start -S -n com.android.pmc/com.android.pmc."
38                 "PMCMainActivity")
39pmc_interval_cmd = ("am broadcast -a com.android.pmc.action.SETPARAMS --es "
40                    "Interval %s ")
41pmc_set_params = "am broadcast -a com.android.pmc.action.SETPARAMS --es "
42
43pmc_start_connect_scan_cmd = "%sStartConnectivityScan" % pmc_base_cmd
44pmc_stop_connect_scan_cmd = "%sStopConnectivityScan" % pmc_base_cmd
45pmc_start_gscan_no_dfs_cmd = "%sStartGScanBand" % pmc_base_cmd
46pmc_start_gscan_specific_channels_cmd = "%sStartGScanChannel" % pmc_base_cmd
47pmc_stop_gscan_cmd = "%sStopGScan" % pmc_base_cmd
48pmc_start_iperf_client = "%sStartIperfClient" % pmc_base_cmd
49pmc_stop_iperf_client = "%sStopIperfClient" % pmc_base_cmd
50pmc_turn_screen_on = "%sTurnScreenOn" % pmc_base_cmd
51pmc_turn_screen_off = "%sTurnScreenOff" % pmc_base_cmd
52# Path of the iperf json output file from an iperf run.
53pmc_iperf_json_file = "/sdcard/iperf.txt"
54
55
56class WifiPowerTest(base_test.BaseTestClass):
57    def setup_class(self):
58        self.offset = 5 * 60
59        self.hz = 5000
60        self.scan_interval = 15
61        # Continuosly download
62        self.download_interval = 0
63        self.mon_data_path = os.path.join(self.log_path, "Monsoon")
64        self.mon = self.monsoons[0]
65        self.mon.set_voltage(4.2)
66        self.mon.set_max_current(7.8)
67        self.dut = self.android_devices[0]
68        self.mon.attach_device(self.dut)
69        asserts.assert_true(
70            self.mon.usb("auto"),
71            "Failed to turn USB mode to auto on monsoon.")
72        asserts.assert_true(
73            force_airplane_mode(self.dut, True),
74            "Can not turn on airplane mode on: %s" % self.dut.serial)
75        set_location_service(self.dut, False)
76        set_adaptive_brightness(self.dut, False)
77        set_ambient_display(self.dut, False)
78        self.dut.adb.shell("settings put system screen_brightness 0")
79        set_auto_rotate(self.dut, False)
80        required_userparam_names = (
81            # These two params should follow the format of
82            # {"SSID": <SSID>, "password": <Password>}
83            "network_2g",
84            "network_5g",
85            "iperf_server_address")
86        self.unpack_userparams(required_userparam_names, threshold=None)
87        wutils.wifi_test_device_init(self.dut)
88        try:
89            self.attn = self.attenuators[0]
90            self.attn.set_atten(0)
91        except AttributeError:
92            self.log.warning("No attenuator found, some tests will fail.")
93            pass
94
95    def teardown_class(self):
96        self.mon.usb("on")
97
98    def setup_test(self):
99        # Default measurement time is 30min with an offset of 5min. Each test
100        # can overwrite this by setting self.duration and self.offset.
101        self.offset = 5 * 60
102        self.duration = 20 * 60 + self.offset
103        self.start_pmc()
104        wutils.reset_wifi(self.dut)
105        self.dut.ed.clear_all_events()
106
107    def on_fail(self, test_name, begin_time):
108        self.dut.take_bug_report(test_name, begin_time)
109
110    def on_pass(self, test_name, begin_time):
111        self.dut.take_bug_report(test_name, begin_time)
112
113    def start_pmc(self):
114        """Starts a new instance of PMC app on the device and initializes it.
115
116        This function performs the following:
117        1. Starts a new instance of PMC (killing any existing instances).
118        2. Turns on PMC verbose logging.
119        3. Sets up the server IP address/port for download/iperf tests.
120        4. Removes an existing iperf json output files.
121        """
122        self.dut.adb.shell(start_pmc_cmd)
123        self.dut.adb.shell("setprop log.tag.PMC VERBOSE")
124        self.iperf_server = self.iperf_servers[0]
125        # Setup iperf related params on the client side.
126        self.dut.adb.shell("%sServerIP %s" % (pmc_set_params,
127                                              self.iperf_server_address))
128        self.dut.adb.shell("%sServerPort %s" % (pmc_set_params,
129                                                self.iperf_server.port))
130        try:
131            self.dut.adb.shell("rm %s" % pmc_iperf_json_file)
132        except adb.AdbError:
133            pass
134
135    def get_iperf_result(self):
136        """Pulls the iperf json output from device.
137
138        Returns:
139            An IPerfResult object based on the iperf run output.
140        """
141        dest = os.path.join(self.iperf_server.log_path, "iperf.txt")
142        self.dut.adb.pull(pmc_iperf_json_file, " ", dest)
143        result = ip_server.IPerfResult(dest)
144        self.dut.adb.shell("rm %s" % pmc_iperf_json_file)
145        return result
146
147    def measure_and_process_result(self):
148        """Measure the current drawn by the device for the period of
149        self.duration, at the frequency of self.hz.
150
151        If self.threshold exists, also verify that the average current of the
152        measurement is below the acceptable threshold.
153        """
154        tag = self.current_test_name
155        result = self.mon.measure_power(self.hz,
156                                        self.duration,
157                                        tag=tag,
158                                        offset=self.offset)
159        asserts.assert_true(result,
160                            "Got empty measurement data set in %s." % tag)
161        self.log.info(repr(result))
162        data_path = os.path.join(self.mon_data_path, "%s.txt" % tag)
163        monsoon.MonsoonData.save_to_text_file([result], data_path)
164        actual_current = result.average_current
165        actual_current_str = "%.2fmA" % actual_current
166        result_extra = {"Average Current": actual_current_str}
167        if "continuous_traffic" in tag:
168            self.dut.adb.shell(pmc_stop_iperf_client)
169            iperf_result = self.get_iperf_result()
170            asserts.assert_true(iperf_result.avg_rate,
171                                "Failed to send iperf traffic",
172                                extras=result_extra)
173            rate = "%.2fMB/s" % iperf_result.avg_rate
174            result_extra["Average Rate"] = rate
175        model = utils.trim_model_name(self.dut.model)
176        if self.threshold and (model in self.threshold) and (
177                tag in self.threshold[model]):
178            acceptable_threshold = self.threshold[model][tag]
179            asserts.assert_true(
180                actual_current < acceptable_threshold,
181                ("Measured average current in [%s]: %s, which is "
182                 "higher than acceptable threshold %.2fmA.") % (
183                     tag, actual_current_str, acceptable_threshold),
184                extras=result_extra)
185        asserts.explicit_pass("Measurement finished for %s." % tag,
186                              extras=result_extra)
187
188    @test_tracker_info(uuid="99ed6d06-ad07-4650-8434-0ac9d856fafa")
189    def test_power_wifi_off(self):
190        wutils.wifi_toggle_state(self.dut, False)
191        self.measure_and_process_result()
192
193    @test_tracker_info(uuid="086db8fd-4040-45ac-8934-49b4d84413fc")
194    def test_power_wifi_on_idle(self):
195        wutils.wifi_toggle_state(self.dut, True)
196        self.measure_and_process_result()
197
198    @test_tracker_info(uuid="031516d9-b0f5-4f21-bc8b-078258852325")
199    def test_power_disconnected_connectivity_scan(self):
200        try:
201            self.dut.adb.shell(pmc_interval_cmd % self.scan_interval)
202            self.dut.adb.shell(pmc_start_connect_scan_cmd)
203            self.log.info("Started connectivity scan.")
204            self.measure_and_process_result()
205        finally:
206            self.dut.adb.shell(pmc_stop_connect_scan_cmd)
207            self.log.info("Stoped connectivity scan.")
208
209    @test_tracker_info(uuid="5e1f92d7-a79e-459c-aff0-d4acba3adee4")
210    def test_power_connected_2g_idle(self):
211        wutils.reset_wifi(self.dut)
212        self.dut.ed.clear_all_events()
213        wutils.wifi_connect(self.dut, self.network_2g)
214        self.measure_and_process_result()
215
216    @test_tracker_info(uuid="e2b4ab89-420e-4560-a08b-d3bf4336f05d")
217    def test_power_connected_2g_continuous_traffic(self):
218        try:
219            wutils.reset_wifi(self.dut)
220            self.dut.ed.clear_all_events()
221            wutils.wifi_connect(self.dut, self.network_2g)
222            self.iperf_server.start()
223            self.dut.adb.shell(pmc_start_iperf_client)
224            self.log.info("Started iperf traffic.")
225            self.measure_and_process_result()
226        finally:
227            self.iperf_server.stop()
228            self.log.info("Stopped iperf traffic.")
229
230    @test_tracker_info(uuid="a9517306-b967-494e-b471-84de58df8f1b")
231    def test_power_connected_5g_idle(self):
232        wutils.reset_wifi(self.dut)
233        self.dut.ed.clear_all_events()
234        wutils.wifi_connect(self.dut, self.network_5g)
235        self.measure_and_process_result()
236
237    @test_tracker_info(uuid="816716b3-a90b-4835-84b8-d8d761ebfba9")
238    def test_power_connected_5g_continuous_traffic(self):
239        try:
240            wutils.reset_wifi(self.dut)
241            self.dut.ed.clear_all_events()
242            wutils.wifi_connect(self.dut, self.network_5g)
243            self.iperf_server.start()
244            self.dut.adb.shell(pmc_start_iperf_client)
245            self.log.info("Started iperf traffic.")
246            self.measure_and_process_result()
247        finally:
248            self.iperf_server.stop()
249            self.log.info("Stopped iperf traffic.")
250
251    @test_tracker_info(uuid="e2d08e4e-7863-4554-af63-64d41ab0976a")
252    def test_power_gscan_three_2g_channels(self):
253        try:
254            self.dut.adb.shell(pmc_interval_cmd % self.scan_interval)
255            self.dut.adb.shell(pmc_start_gscan_specific_channels_cmd)
256            self.log.info("Started gscan for 2G channels 1, 6, and 11.")
257            self.measure_and_process_result()
258        finally:
259            self.dut.adb.shell(pmc_stop_gscan_cmd)
260            self.log.info("Stopped gscan.")
261
262    @test_tracker_info(uuid="0095b7e7-94b9-4cd9-912f-51971949748b")
263    def test_power_gscan_all_channels_no_dfs(self):
264        try:
265            self.dut.adb.shell(pmc_interval_cmd % self.scan_interval)
266            self.dut.adb.shell(pmc_start_gscan_no_dfs_cmd)
267            self.log.info("Started gscan for all non-DFS channels.")
268            self.measure_and_process_result()
269        finally:
270            self.dut.adb.shell(pmc_stop_gscan_cmd)
271            self.log.info("Stopped gscan.")
272
273    @test_tracker_info(uuid="263d1b68-8eb0-4e7f-99d4-3ca23ca359ce")
274    def test_power_connected_2g_gscan_all_channels_no_dfs(self):
275        try:
276            wutils.wifi_connect(self.dut, self.network_2g)
277            self.dut.adb.shell(pmc_interval_cmd % self.scan_interval)
278            self.dut.adb.shell(pmc_start_gscan_no_dfs_cmd)
279            self.log.info("Started gscan for all non-DFS channels.")
280            self.measure_and_process_result()
281        finally:
282            self.dut.adb.shell(pmc_stop_gscan_cmd)
283            self.log.info("Stopped gscan.")
284
285    @test_tracker_info(uuid="aad1a39d-01f9-4fa5-a23a-b85d54210f3c")
286    def test_power_connected_5g_gscan_all_channels_no_dfs(self):
287        try:
288            wutils.wifi_connect(self.dut, self.network_5g)
289            self.dut.adb.shell(pmc_interval_cmd % self.scan_interval)
290            self.dut.adb.shell(pmc_start_gscan_no_dfs_cmd)
291            self.log.info("Started gscan for all non-DFS channels.")
292            self.measure_and_process_result()
293        finally:
294            self.dut.adb.shell(pmc_stop_gscan_cmd)
295            self.log.info("Stopped gscan.")
296
297    @test_tracker_info(uuid="8f72cd5f-1c66-4ced-92d9-b7ebadf76424")
298    def test_power_auto_reconnect(self):
299        """
300        Steps:
301            1. Connect to network, wait for three minutes.
302            2. Attenuate AP away, wait for one minute.
303            3. Make AP reappear, wait for three minutes for the device to
304               reconnect to the Wi-Fi network.
305        """
306        self.attn.set_atten(0)
307        wutils.wifi_connect(self.dut, self.network_2g)
308
309        def attn_control():
310            for i in range(7):
311                self.log.info("Iteration %s: Idle 3min after AP appeared.", i)
312                time.sleep(3 * 60)
313                self.attn.set_atten(90)
314                self.log.info("Iteration %s: Idle 1min after AP disappeared.",
315                              i)
316                time.sleep(60)
317                self.attn.set_atten(0)
318
319        t = threading.Thread(target=attn_control)
320        t.start()
321        try:
322            self.measure_and_process_result()
323        finally:
324            t.join()
325
326    @test_tracker_info(uuid="a6db5964-3c68-47fa-b4c9-49f880549031")
327    def test_power_screen_on_wifi_off(self):
328        self.duration = 10 * 60
329        self.offset = 4 * 60
330        wutils.wifi_toggle_state(self.dut, False)
331        try:
332            self.dut.adb.shell(pmc_turn_screen_on)
333            self.measure_and_process_result()
334        finally:
335            self.dut.adb.shell(pmc_turn_screen_off)
336
337    @test_tracker_info(uuid="230d667a-aa42-4123-9dae-2036429ed574")
338    def test_power_screen_on_wifi_connected_2g_idle(self):
339        self.duration = 10 * 60
340        self.offset = 4 * 60
341        wutils.wifi_toggle_state(self.dut, True)
342        wutils.wifi_connect(self.dut, self.network_2g)
343        try:
344            self.dut.adb.shell(pmc_turn_screen_on)
345            self.measure_and_process_result()
346        finally:
347            self.dut.adb.shell(pmc_turn_screen_off)
348