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