1#
2#   Copyright 2018 - The Android Open Source Project
3#
4#   Licensed under the Apache License, Version 2.0 (the "License");
5#   you may not use this file except in compliance with the License.
6#   You may obtain a copy of the License at
7#
8#       http://www.apache.org/licenses/LICENSE-2.0
9#
10#   Unless required by applicable law or agreed to in writing, software
11#   distributed under the License is distributed on an "AS IS" BASIS,
12#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13#   See the License for the specific language governing permissions and
14#   limitations under the License.
15
16import logging
17import os
18import random
19import socket
20import threading
21import time
22
23from acts import asserts
24from acts import base_test
25from acts import utils
26from acts import signals
27from acts import test_runner
28from acts.controllers import adb
29from acts.test_decorators import test_tracker_info
30from acts.test_utils.net import net_test_utils as nutils
31from acts.test_utils.tel.tel_test_utils import _check_file_existance
32from acts.test_utils.tel.tel_test_utils import _generate_file_directory_and_file_name
33from acts.test_utils.wifi import wifi_test_utils as wutils
34from acts.test_utils.net.connectivity_const import MULTIPATH_PREFERENCE_NONE as NONE
35from acts.test_utils.net.connectivity_const import MULTIPATH_PREFERENCE_HANDOVER as HANDOVER
36from acts.test_utils.net.connectivity_const import MULTIPATH_PREFERENCE_RELIABILITY as RELIABILITY
37from acts.test_utils.net.connectivity_const import MULTIPATH_PREFERENCE_PERFORMANCE as PERFORMANCE
38
39DOWNLOAD_PATH = "/sdcard/Download/"
40RELIABLE = RELIABILITY | HANDOVER
41TIMEOUT = 6
42
43class DataCostTest(base_test.BaseTestClass):
44    """ Tests for Wifi Tethering """
45
46    def setup_class(self):
47        """ Setup devices for tethering and unpack params """
48
49        req_params = ("wifi_network", "download_file")
50        self.unpack_userparams(req_params)
51
52        for ad in self.android_devices:
53            nutils.verify_lte_data_and_tethering_supported(ad)
54
55        self.tcpdump_pid = None
56
57    def teardown_class(self):
58        """ Reset settings to default """
59        for ad in self.android_devices:
60            sub_id = str(ad.droid.telephonyGetSubscriberId())
61            ad.droid.connectivityFactoryResetNetworkPolicies(sub_id)
62            ad.droid.connectivitySetDataWarningLimit(sub_id, -1)
63            wutils.reset_wifi(ad)
64
65
66    def teardown_test(self):
67        if self.tcpdump_pid:
68            nutils.stop_tcpdump(self.dut, self.tcpdump_pid, self.test_name)
69            self.tcpdump_pid = None
70
71    def on_fail(self, test_name, begin_time):
72        self.dut.take_bug_report(test_name, begin_time)
73        dumpsys_info=self.dut.adb.shell("dumpsys netstats --uid")
74        self.dut.log.info(dumpsys_info)
75
76    """ Helper functions """
77
78    def _clear_netstats(self, ad):
79        """ Clear netstats stored on device
80
81        Args:
82            ad: Android device object
83        """
84        ad.log.info("Clear netstats record.")
85        ad.adb.shell("rm /data/system/netstats/*")
86        asserts.assert_equal("", ad.adb.shell("ls /data/system/netstats/"),
87                             "Fail to clear netstats.")
88        ad.reboot()
89        time.sleep(30)
90        nutils.verify_lte_data_and_tethering_supported(ad)
91        self._check_multipath_preference_from_dumpsys(ad)
92
93    def _check_multipath_preference_from_dumpsys(self, ad):
94        """ Check cell multipath_preference from dumpsys
95
96        Args:
97            ad: Android device object
98        """
99        try:
100            out = ad.adb.shell("dumpsys connectivity | grep budget")
101        except TimeoutError:
102            ad.log.warning("Fail to get status from dumpsys.")
103            out = ""
104        ad.log.info("MultipathPolicyTracker: %s" % out)
105        if out:
106            asserts.assert_true(
107                "HANDOVER|RELIABILITY" in out,
108                "Cell multipath preference should be HANDOVER|RELIABILITY."
109            )
110
111    def _get_total_data_usage_for_device(self, ad, conn_type, sub_id):
112        """ Get total data usage in MB for device
113
114        Args:
115            ad: Android device object
116            conn_type: MOBILE/WIFI data usage
117            sub_id: subscription id
118
119        Returns:
120            Data usage in MB
121        """
122        # end time should be in milli seconds and at least 2 hours more than the
123        # actual end time. NetStats:bucket is of size 2 hours and to ensure to
124        # get the most recent data usage, end_time should be +2hours
125        end_time = int(time.time() * 1000) + 2 * 1000 * 60 * 60
126        data_usage = ad.droid.connectivityQuerySummaryForDevice(
127            conn_type, sub_id, 0, end_time)
128        data_usage /= 1000.0 * 1000.0 # convert data_usage to MB
129        self.log.info("Total data usage is: %s" % data_usage)
130        return data_usage
131
132    def _check_if_multipath_preference_valid(self, val, exp):
133        """ Check if multipath value is same as expected
134
135        Args:
136            val: multipath preference for the network
137            exp: expected multipath preference value
138        """
139        if exp == NONE:
140            asserts.assert_true(val == exp, "Multipath value should be 0")
141        else:
142            asserts.assert_true(val >= exp,
143                                "Multipath value should be at least %s" % exp)
144
145    def _verify_multipath_preferences(self,
146                                      ad,
147                                      wifi_pref,
148                                      cell_pref,
149                                      wifi_network,
150                                      cell_network):
151        """ Verify mutlipath preferences for wifi and cell networks
152
153        Args:
154            ad: Android device object
155            wifi_pref: Expected multipath value for wifi network
156            cell_pref: Expected multipath value for cell network
157            wifi_network: Wifi network id on the device
158            cell_network: Cell network id on the device
159        """
160        wifi_multipath = \
161            ad.droid.connectivityGetMultipathPreferenceForNetwork(wifi_network)
162        cell_multipath = \
163            ad.droid.connectivityGetMultipathPreferenceForNetwork(cell_network)
164        self.log.info("WiFi multipath preference: %s" % wifi_multipath)
165        self.log.info("Cell multipath preference: %s" % cell_multipath)
166        self.log.info("Checking multipath preference for wifi")
167        self._check_if_multipath_preference_valid(wifi_multipath, wifi_pref)
168        self.log.info("Checking multipath preference for cell")
169        self._check_if_multipath_preference_valid(cell_multipath, cell_pref)
170
171    """ Test Cases """
172
173    @test_tracker_info(uuid="e86c8108-3e84-4668-bae4-e5d2c8c27910")
174    def test_multipath_preference_low_data_limit(self):
175        """ Verify multipath preference when mobile data limit is low
176
177        Steps:
178            1. DUT has WiFi and LTE data
179            2. Set mobile data usage limit to low value
180            3. Verify that multipath preference is 0 for cell network
181        """
182        # set vars
183        ad = self.android_devices[0]
184        self.dut = ad
185        self._clear_netstats(ad)
186        utils.sync_device_time(ad)
187        self.tcpdump_pid = nutils.start_tcpdump(ad, self.test_name)
188
189        sub_id = str(ad.droid.telephonyGetSubscriberId())
190        cell_network = ad.droid.connectivityGetActiveNetwork()
191        self.log.info("cell network %s" % cell_network)
192        wutils.wifi_connect(ad, self.wifi_network)
193        wifi_network = ad.droid.connectivityGetActiveNetwork()
194        self.log.info("wifi network %s" % wifi_network)
195
196        # verify mulipath preference values
197        self._verify_multipath_preferences(
198            ad, RELIABLE, RELIABLE, wifi_network, cell_network)
199
200        # set low data limit on mobile data
201        total_pre = self._get_total_data_usage_for_device(ad, 0, sub_id)
202        self.log.info("Setting data usage limit to %sMB" % (total_pre + 5))
203        ad.droid.connectivitySetDataUsageLimit(
204            sub_id, int((total_pre + 5) * 1000.0 * 1000.0))
205        self.log.info("Setting data warning limit to %sMB" % (total_pre + 5))
206        ad.droid.connectivitySetDataWarningLimit(
207            sub_id, int((total_pre + 5) * 1000.0 * 1000.0))
208
209        # verify multipath preference values
210        curr_time = time.time()
211        while time.time() < curr_time + TIMEOUT:
212            try:
213                self._verify_multipath_preferences(
214                    ad, RELIABLE, NONE, wifi_network, cell_network)
215                return True
216            except signals.TestFailure as e:
217                self.log.debug("%s" % e)
218            time.sleep(1)
219        return False
220
221    @test_tracker_info(uuid="a2781411-d880-476a-9f40-2c67e0f97db9")
222    def test_multipath_preference_data_download(self):
223        """ Verify multipath preference when large file is downloaded
224
225        Steps:
226            1. DUT has WiFi and LTE data
227            2. WiFi is active network
228            3. Download large file over cell network
229            4. Verify multipath preference on cell network is 0
230        """
231        # set vars
232        ad = self.android_devices[1]
233        self.dut = ad
234        self._clear_netstats(ad)
235        utils.sync_device_time(ad)
236        self.tcpdump_pid = nutils.start_tcpdump(ad, self.test_name)
237
238        cell_network = ad.droid.connectivityGetActiveNetwork()
239        self.log.info("cell network %s" % cell_network)
240        wutils.wifi_connect(ad, self.wifi_network)
241        wifi_network = ad.droid.connectivityGetActiveNetwork()
242        self.log.info("wifi network %s" % wifi_network)
243
244        # verify multipath preference for wifi and cell networks
245        self._verify_multipath_preferences(
246            ad, RELIABLE, RELIABLE, wifi_network, cell_network)
247
248        # download file with cell network
249        ad.droid.connectivityNetworkOpenConnection(cell_network,
250                                                   self.download_file)
251        file_folder, file_name = _generate_file_directory_and_file_name(
252            self.download_file, DOWNLOAD_PATH)
253        file_path = os.path.join(file_folder, file_name)
254        self.log.info("File path: %s" % file_path)
255        if _check_file_existance(ad, file_path):
256            self.log.info("File exists. Removing file %s" % file_name)
257            ad.adb.shell("rm -rf %s%s" % (DOWNLOAD_PATH, file_name))
258
259        #  verify multipath preference values
260        curr_time = time.time()
261        while time.time() < curr_time + TIMEOUT:
262            try:
263                self._verify_multipath_preferences(
264                    ad, RELIABLE, NONE, wifi_network, cell_network)
265                return True
266            except signals.TestFailure as e:
267                self.log.debug("%s" % e)
268            time.sleep(1)
269        return False
270
271    # TODO gmoturu@: Need to add tests that use the mobility rig and test when
272    # the WiFi signal is poor and data signal is good.
273