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
17import statistics
18
19from acts import asserts
20from acts import signals
21from acts.base_test import BaseTestClass
22from acts_contrib.test_utils.gnss.testtracker_util import log_testtracker_uuid
23from acts_contrib.test_utils.gnss import gnss_test_utils as gutils
24from acts_contrib.test_utils.tel import tel_logging_utils as tutils
25from acts_contrib.test_utils.tel.tel_test_utils import verify_internet_connection
26from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
27from acts.utils import get_current_epoch_time
28from acts_contrib.test_utils.gnss.gnss_test_utils import delete_lto_file, pair_to_wearable
29from acts_contrib.test_utils.gnss.gnss_test_utils import process_gnss_by_gtw_gpstool
30from acts_contrib.test_utils.gnss.gnss_test_utils import start_gnss_by_gtw_gpstool
31from acts_contrib.test_utils.gnss.gnss_test_utils import check_tracking_file
32from uiautomator import Device
33
34
35class GnssWearableTetherFunctionTest(BaseTestClass):
36    """ GNSS Wearable Tether Function Tests"""
37    def setup_class(self):
38        super().setup_class()
39        self.watch = self.android_devices[0]
40        self.phone = self.android_devices[1]
41        self.phone.uia = Device(self.phone.serial)
42        req_params = ["pixel_lab_network", "standalone_cs_criteria",
43                      "flp_ttff_max_threshold", "pixel_lab_location",
44                      "flp_ttff_cycle", "default_gnss_signal_attenuation",
45                      "flp_waiting_time", "tracking_test_time",
46                      "far_start_criteria", "ttff_test_cycle"]
47        self.unpack_userparams(req_param_names=req_params)
48        # create hashmap for SSID
49        self.ssid_map = {}
50        for network in self.pixel_lab_network:
51            SSID = network["SSID"]
52            self.ssid_map[SSID] = network
53        self.ttff_mode = {"cs": "Cold Start",
54                          "ws": "Warm Start",
55                          "hs": "Hot Start"}
56        gutils._init_device(self.watch)
57        pair_to_wearable(self.watch, self.phone)
58
59    def teardown_class(self):
60        super().teardown_class()
61        gutils.reboot(self.phone)
62
63    def setup_test(self):
64        gutils.log_current_epoch_time(self.watch, "test_start_time")
65        log_testtracker_uuid(self.watch, self.current_test_name)
66        gutils.get_baseband_and_gms_version(self.watch)
67        gutils.clear_logd_gnss_qxdm_log(self.watch)
68        gutils.clear_logd_gnss_qxdm_log(self.phone)
69        gutils.set_attenuator_gnss_signal(self.watch, self.attenuators,
70                                          self.default_gnss_signal_attenuation)
71        if not verify_internet_connection(self.watch.log, self.watch, retries=5,
72                                          expected_state=True):
73            time.sleep(60)
74            if not verify_internet_connection(self.watch.log, self.watch, retries=3,
75                                              expected_state=True):
76                raise signals.TestFailure("Fail to connect to LTE network.")
77
78    def teardown_test(self):
79        gutils.stop_pixel_logger(self.watch)
80        tutils.stop_adb_tcpdump(self.watch)
81        gutils.set_attenuator_gnss_signal(self.watch, self.attenuators,
82                                       self.default_gnss_signal_attenuation)
83        if self.watch.droid.connectivityCheckAirplaneMode():
84            self.watch.log.info("Force airplane mode off")
85            toggle_airplane_mode(self.watch.log, self.watch, new_state=False)
86        gutils.log_current_epoch_time(self.watch, "test_end_time")
87
88    def on_fail(self, test_name, begin_time):
89        self.watch.take_bug_report(test_name, begin_time)
90        gutils.get_gnss_qxdm_log(self.watch)
91        tutils.get_tcpdump_log(self.watch, test_name, begin_time)
92
93    def start_qxdm_and_tcpdump_log(self):
94        """Start QXDM and adb tcpdump if collect_logs is True."""
95        gutils.start_pixel_logger(self.watch)
96        tutils.start_adb_tcpdump(self.watch)
97
98    def flp_ttff(self, mode, criteria, location):
99        self.start_qxdm_and_tcpdump_log()
100        start_gnss_by_gtw_gpstool(self.phone, True, api_type="FLP")
101        time.sleep(self.flp_waiting_time)
102        self.watch.unlock_screen(password=None)
103        begin_time = get_current_epoch_time()
104        process_gnss_by_gtw_gpstool(
105            self.watch, self.standalone_cs_criteria, api_type="flp")
106        gutils.start_ttff_by_gtw_gpstool(
107            self.watch, mode, iteration=self.flp_ttff_cycle)
108        results = gutils.process_ttff_by_gtw_gpstool(
109            self.watch, begin_time, location, api_type="flp")
110        gutils.check_ttff_data(self.watch, results, mode, criteria)
111        self.check_location_from_phone()
112        start_gnss_by_gtw_gpstool(self.phone, False, api_type="FLP")
113
114    def check_location_from_phone(self):
115        watch_file = check_tracking_file(self.watch)
116        phone_file = check_tracking_file(self.phone)
117        return gutils.compare_watch_phone_location(self, watch_file, phone_file)
118
119    def run_ttff_via_gtw_gpstool(self, mode, criteria):
120        """Run GNSS TTFF test with selected mode and parse the results.
121
122        Args:
123            mode: "cs", "ws" or "hs"
124            criteria: Criteria for the TTFF.
125        """
126        begin_time = get_current_epoch_time()
127        gutils.process_gnss_by_gtw_gpstool(self.watch, self.standalone_cs_criteria, clear_data=False)
128        gutils.start_ttff_by_gtw_gpstool(self.watch, mode, self.ttff_test_cycle)
129        ttff_data = gutils.process_ttff_by_gtw_gpstool(
130            self.watch, begin_time, self.pixel_lab_location)
131        result = gutils.check_ttff_data(
132            self.watch, ttff_data, self.ttff_mode.get(mode), criteria)
133        asserts.assert_true(
134            result, "TTFF %s fails to reach designated criteria of %d "
135                    "seconds." % (self.ttff_mode.get(mode), criteria))
136
137    """ Test Cases """
138    def test_flp_ttff_cs(self):
139        """Verify FLP TTFF Cold Start while tether with phone.
140
141        Steps:
142            1. Pair with phone via Bluetooth.
143            2. FLP TTFF Cold Start for 10 iteration.
144            3. Check location source is from Phone.
145
146        Expected Results:
147            1. FLP TTFF Cold Start results should be within
148            flp_ttff_max_threshold.
149            2. Watch uses phone's FLP location.
150        """
151        self.flp_ttff("cs", self.flp_ttff_max_threshold, self.pixel_lab_location)
152
153    def test_flp_ttff_ws(self):
154        """Verify FLP TTFF Warm Start while tether with phone.
155
156        Steps:
157            1. Pair with phone via Bluetooth.
158            2. FLP TTFF Warm Start for 10 iteration.
159            3. Check location source is from Phone.
160
161        Expected Results:
162            1. FLP TTFF Warm Start results should be within
163            flp_ttff_max_threshold.
164            2. Watch uses phone's FLP location.
165        """
166        self.flp_ttff("ws", self.flp_ttff_max_threshold, self.pixel_lab_location)
167
168    def test_flp_ttff_hs(self):
169        """Verify FLP TTFF Hot Start while tether with phone.
170
171        Steps:
172            1. Pair with phone via Bluetooth.
173            2. FLP TTFF Hot Start for 10 iteration.
174            3. Check location source is from Phone.
175
176        Expected Results:
177            1. FLP TTFF Hot Start results should be within
178            flp_ttff_max_threshold.
179            2. Watch uses phone's FLP location.
180        """
181        self.flp_ttff("hs", self.flp_ttff_max_threshold, self.pixel_lab_location)
182
183    def test_tracking_during_bt_disconnect_resume(self):
184        """Verify tracking is correct during Bluetooth disconnect and resume.
185
186        Steps:
187            1. Make sure watch Bluetooth is on and in paired status.
188            2. Do 1 min tracking.
189            3. After 1 min tracking, check location source is using phone's FLP.
190            4. Turn off watch Bluetooth, and do 1 min tracking.
191            5. After 1 min tracking, check tracking results.
192            6. Repeat Step 1 to Step 5 for 5 times.
193
194        Expected Results:
195            1. Watch uses phone's FLP location in Bluetooth connect state.
196            2. Tracking results should be within pixel_lab_location criteria.
197        """
198        self.start_qxdm_and_tcpdump_log()
199        for i in range(1, 4):
200            if not self.watch.droid.bluetoothCheckState():
201                self.watch.droid.bluetoothToggleState(True)
202                self.watch.log.info("Turn Bluetooth on")
203                self.watch.log.info("Wait 40s for Bluetooth auto re-connect")
204                time.sleep(40)
205            if not gutils.is_bluetooth_connected(self.watch, self.phone):
206                raise signals.TestFailure("Fail to connect to device via Bluetooth.")
207            start_gnss_by_gtw_gpstool(self.phone, True, api_type="FLP")
208            time.sleep(self.flp_waiting_time)
209            start_gnss_by_gtw_gpstool(self.watch, True, api_type="FLP")
210            time.sleep(self.flp_waiting_time)
211            self.watch.log.info("Wait 1 min for tracking")
212            time.sleep(self.tracking_test_time)
213            if not self.check_location_from_phone():
214                raise signals.TestFailure("Watch is not using phone location")
215            self.watch.droid.bluetoothToggleState(False)
216            self.watch.log.info("Turn off Watch Bluetooth")
217            self.watch.log.info("Wait 1 min for tracking")
218            time.sleep(self.tracking_test_time)
219            if self.check_location_from_phone():
220                raise signals.TestError("Watch should not use phone location")
221            gutils.parse_gtw_gpstool_log(self.watch, self.pixel_lab_location, api_type="FLP")
222            start_gnss_by_gtw_gpstool(self.phone, False, api_type="FLP")
223
224    def test_oobe_first_fix(self):
225        """Verify first fix after OOBE pairing within the criteria
226
227        Steps:
228            1. Pair watch to phone during OOBE.
229            2. Ensure LTO file download in watch.
230            3. Ensure UTC time inject in watch.
231            4. Enable AirPlane mode to untether to phone.
232            5. Open GPSTool to get first fix in LTO and UTC time injected.
233            6. Repeat Step1 ~ Step5 for 5 times.
234
235        Expected Results:
236            1. First fix should be within far_start_threshold.
237        """
238        oobe_results_all = []
239        for i in range(1,4):
240            self.watch.log.info("First fix after OOBE pairing - attempts %s" % i)
241            pair_to_wearable(self.watch, self.phone)
242            self.start_qxdm_and_tcpdump_log()
243            begin_time = get_current_epoch_time()
244            gutils.check_xtra_download(self.watch, begin_time)
245            gutils.check_inject_time(self.watch)
246            self.watch.log.info("Turn airplane mode on")
247            # self.watch.droid.connectivityToggleAirplaneMode(True)
248            toggle_airplane_mode(self.watch.log, self.watch, new_state=True)
249            oobe_results = gutils.process_gnss(
250                self.watch, self.far_start_criteria, clear_data=False)
251            oobe_results_all.append(oobe_results)
252        self.watch.log.info(f"TestResult Max_OOBE_First_Fix {max(oobe_results_all)}")
253        self.watch.log.info(f"TestResult Avg_OOBE_First_Fix {statistics.mean(oobe_results_all)}")
254        # self.watch.droid.connectivityToggleAirplaneMode(False)
255        toggle_airplane_mode(self.watch.log, self.watch, new_state=False)
256        self.watch.log.info("Turn airplane mode off")
257
258    def test_oobe_first_fix_with_network_connection(self):
259        """Verify first fix after OOBE pairing within the criteria
260
261        Steps:
262            1. Pair watch to phone during OOBE.
263            2. Ensure LTO file download in watch.
264            3. Ensure UTC time inject in watch.
265            4. Turn off Bluetooth to untether to phone.
266            5. Open GPSTool to get first fix in LTO and UTC time injected.
267            6. Repeat Step1 ~ Step5 for 5 times.
268
269        Expected Results:
270            1. First fix should be within far_start_threshold.
271        """
272        oobe_results_all = []
273        for i in range(1,4):
274            self.watch.log.info("First fix after OOBE pairing - attempts %s" % i)
275            pair_to_wearable(self.watch, self.phone)
276            self.start_qxdm_and_tcpdump_log()
277            begin_time = get_current_epoch_time()
278            gutils.check_xtra_download(self.watch, begin_time)
279            gutils.check_inject_time(self.watch)
280            self.watch.log.info("Turn off Bluetooth to disconnect to phone")
281            self.watch.droid.bluetoothToggleState(False)
282            oobe_results = gutils.process_gnss(
283                self.watch, self.far_start_criteria, clear_data=False)
284            oobe_results_all.append(oobe_results)
285        self.watch.log.info(f"TestResult Max_OOBE_First_Fix {max(oobe_results_all)}")
286        self.watch.log.info(f"TestResult Avg_OOBE_First_Fix {statistics.mean(oobe_results_all)}")
287
288    def test_far_start_ttff(self):
289        """Verify Far Start (Warm Start v4) TTFF within the criteria
290
291        Steps:
292            1. Pair watch to phone during OOBE.
293            2. Ensure LTO file download in watch.
294            3. Ensure UTC time inject in watch.
295            4. Enable AirPlane mode to untether to phone.
296            5. TTFF Warm Start for 10 iteration.
297
298        Expected Results:
299            1. TTFF should be within far_start_threshold.
300        """
301        pair_to_wearable(self.watch, self.phone)
302        self.start_qxdm_and_tcpdump_log()
303        begin_time = get_current_epoch_time()
304        gutils.check_xtra_download(self.watch, begin_time)
305        gutils.check_inject_time(self.watch)
306        self.watch.log.info("Turn airplane mode on")
307        toggle_airplane_mode(self.watch.log, self.watch, new_state=True)
308        self.run_ttff_via_gtw_gpstool("ws", self.far_start_criteria)
309