1#   Copyright 2019 - The Android Open Source Project
2#
3#   Licensed under the Apache License, Version 2.0 (the "License");
4#   you may not use this file except in compliance with the License.
5#   You may obtain a copy of the License at
6#
7#       http://www.apache.org/licenses/LICENSE-2.0
8#
9#   Unless required by applicable law or agreed to in writing, software
10#   distributed under the License is distributed on an "AS IS" BASIS,
11#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12#   See the License for the specific language governing permissions and
13#   limitations under the License.
14"""
15Sanity testing for RequestCellInfoUpdate() / GetAllCellInfo() API on Android Q
16and regression check for GetAllCellInfo() on Android P
17"""
18
19import time
20from acts.test_decorators import test_tracker_info
21from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
22from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected, \
23    toggle_airplane_mode, ensure_phones_idle, start_qxdm_loggers
24from acts.test_utils.wifi import wifi_test_utils
25from acts.utils import disable_usb_charging, enable_usb_charging
26
27NANO_TO_SEC = 1000000000
28RATE_LIMIT_HIGH = 2
29RATE_LIMIT_LOW = 10
30CELL_INFO_UPDATE_WAIT_TIME_HIGH = 10
31CELL_INFO_UPDATE_WAIT_TIME_LOW = 30
32TIME_BETWEEN_CONSECUTIVE_API_CALLS = 0.1
33# wait for 2 sec before start of new test case to account for previous
34# calls to the API by previous test case.
35WAIT_BEFORE_TEST_CASE_START = 2
36WAIT_FOR_CELLULAR_CONNECTION = 20
37
38
39class TelLiveCellInfoTest(TelephonyBaseTest):
40    def setup_class(self):
41        super().setup_class()
42        self.ad = self.android_devices[0]
43        self.wifi_network_ssid = self.user_params.get(
44            "wifi_network_ssid") or self.user_params.get(
45            "wifi_network_ssid_2g") or self.user_params.get(
46            "wifi_network_ssid_5g")
47        self.wifi_network_pass = self.user_params.get(
48            "wifi_network_pass") or self.user_params.get(
49            "wifi_network_pass_2g") or self.user_params.get(
50            "wifi_network_ssid_5g")
51        if self.ad.droid.connectivityCheckAirplaneMode():
52            toggle_airplane_mode(self.log, self.ad, False)
53            time.sleep(WAIT_FOR_CELLULAR_CONNECTION)
54
55        return True
56
57    def setup_test(self):
58        return True
59
60    def teardown_test(self):
61        return True
62
63    def teardown_class(self):
64        return True
65
66    def time_delta_in_sec(self, time1_ns, time2_ns):
67        """To convert time stamps in nano seconds into seconds and return time
68            delta between the two rounded to 3 digits.
69
70         Args:
71             time1_ns, time2_ns - time stamps in nano seconds
72
73         Returns:
74             delta between the two  time stamps in seconds
75        """
76        sec = round(abs((time1_ns - time2_ns) / NANO_TO_SEC), 3)
77        return sec
78
79    def nano_to_sec(self, nano_time):
80        """ To convert and return time stamp from nano seconds to seconds,
81            rounded to 3 digits.
82
83        Args:
84            nano_time - time in nano seconds
85
86        Returns:
87            time in seconds
88        """
89        sec = round(nano_time / NANO_TO_SEC, 3)
90        return sec
91
92    def request_cell_info_update_ts(self):
93        """ SL4A API call for RequestCellInfoUpdate()
94
95        Returns:
96            Android system and modem timestamps for
97            RequestCellInfoUpdate() API
98        """
99        system_ts = self.ad.droid.getSystemElapsedRealtimeNanos()
100        self.log.info("System TS from boot: {}".format(system_ts))
101        try:
102            request_cell_info_update = \
103                self.ad.droid.telephonyRequestCellInfoUpdate()[0]
104        except Exception as e:
105            self.log.error(
106                'Failed to read request cell info update from device, please '
107                'check if device is camped to cellular network.')
108            return False
109        modem_ts = request_cell_info_update['timestamp']
110        self.log.info("Modem TS from boot: {}".format(modem_ts))
111        return modem_ts, system_ts
112
113    def _request_cell_info_update(self):
114        time.sleep(WAIT_BEFORE_TEST_CASE_START)
115        try:
116            self.ad.droid.wakeUpNow()
117            modem_ts, system_ts = self.request_cell_info_update_ts()
118            if modem_ts > system_ts:
119                self.log.info("Modem TS exceeds System TS by:{} secs".format(
120                    self.time_delta_in_sec(modem_ts, system_ts)))
121                return True
122            else:
123                return False
124        except Exception as e:
125            self.log.error("Exception during request_cell_info_update_ts():" +
126                           str(e))
127            return False
128
129    def get_all_cell_info_ts(self):
130        """SL4A API call for GetAllCellInfo()
131
132        Returns:
133            Android system and modem timestamp for GetAllCellInfo() API
134        """
135        system_ts = self.ad.droid.getSystemElapsedRealtimeNanos()
136        self.log.info("System TS from boot: {}".format(system_ts))
137        try:
138            get_all_cell_info = self.ad.droid.telephonyGetAllCellInfo()[0]
139        except Exception as e:
140            self.log.error(
141                'Failed to read get all cell info from device, please '
142                'check if device is camped to cellular network.')
143            return False
144        modem_ts = get_all_cell_info['timestamp']
145        self.log.info("Modem TS from boot: {}".format(modem_ts))
146        return modem_ts, system_ts
147
148    def _get_all_cell_info(self, apk_type):
149        """
150        Args:
151            apk_type: SL4A APK type - 'Q' for android Q and 'P' for android P
152
153        Returns:
154            Result True if Pass, False if Fail.
155        """
156        time.sleep(WAIT_BEFORE_TEST_CASE_START)
157        try:
158            self.ad.droid.wakeUpNow()
159            modem_ts, system_ts = self.get_all_cell_info_ts()
160            if apk_type == 'Q':
161                if modem_ts < system_ts:
162                    self.log.info(
163                        "System TS exceeds Modem TS by:{} secs".format(
164                            self.time_delta_in_sec(modem_ts, system_ts)))
165                    return True
166                else:
167                    return False
168            elif apk_type == 'P':
169                if modem_ts >= system_ts:
170                    self.log.info(
171                        "Modem TS exceeds System TS by:{} secs".format(
172                            self.time_delta_in_sec(modem_ts, system_ts)))
173                    return True
174                else:
175                    return False
176        except Exception as e:
177            self.log.error("Exception during get_all_cell_info_ts(): " +
178                           str(e))
179            return False
180
181    def request_cell_info_update_rate_limit(self, margin, state):
182        """Get time difference between two cell info updates.
183
184        FOR Q APK: Gets the modem timestamp when RequestCellInfoUpdate()
185        API is called and waits for it to update, and calculates the time
186        delta between the modem timestamp update.
187
188        Args:
189            margin - time in seconds to wait for an updated modem timestamp
190            after calling RequestCellInfoUpdate()
191            state - UE power state (high or low)
192
193        Returns:
194            Time delta between two consecutive unique modem timestamps for
195            margin (sec) corresponding to the UE state as passed in the
196            arguments.
197            False: if the value remains same for margin (sec) passed in method
198            arguments.
199        """
200        try:
201            ts1 = ts2 = self.ad.droid.telephonyRequestCellInfoUpdate()[0][
202                'timestamp']
203        except Exception as e:
204            self.log.error(
205                'Failed to read request cell info update from device, please '
206                'check if device is camped to cellular network.')
207            return False
208        self.log.info("Modem timestamp: {}".format(ts1))
209        timeout = time.time() + margin
210        while ts1 == ts2:
211            ts2 = self.ad.droid.telephonyRequestCellInfoUpdate()[0][
212                'timestamp']
213            time.sleep(TIME_BETWEEN_CONSECUTIVE_API_CALLS)
214            if time.time() > timeout:
215                self.log.info(
216                    "Modem timestamp from RequestCellInfoUpdate() for {} "
217                    "powered state "
218                    "not refreshed for {} sec".format(state, margin))
219                return False
220        time_delta = self.nano_to_sec(ts2 - ts1)
221        self.log.info("Updated Modem timestamp: {} in {} sec for "
222
223                      "UE in {} powered state".format(ts2, time_delta, state))
224        return time_delta
225
226    def get_all_cell_info_rate_limit(self, margin, state):
227        """Get time difference between two cell info updates
228
229        FOR P APK: Gets the modem timestamp when GetAllCellInfo() API
230        is called and waits for it to update, and calculates the time delta
231        between the modem timestamp update.
232
233        Args:
234            margin - time in seconds to wait for an updated modem timestamp
235            after calling GetAllCellInfo()
236            state - UE power state (high or low)
237
238        Returns:
239            Time delta between two consecutive unique modem timestamps for
240            margin (sec) corresponding to the UE state as passed in the
241            arguments.
242            False: if the value remains same for margin (sec) passed in
243            method arguments.
244        """
245        try:
246            ts1 = ts2 = self.ad.droid.telephonyGetAllCellInfo()[0]['timestamp']
247        except Exception as e:
248            self.log.error(
249                'Failed to read get all cell info from device, please '
250                'check if device is camped to cellular network.')
251            return False
252        self.log.info("Modem timestamp: {}".format(ts1))
253        timeout = time.time() + margin
254        while ts1 == ts2:
255            ts2 = self.ad.droid.telephonyGetAllCellInfo()[0]['timestamp']
256            time.sleep(TIME_BETWEEN_CONSECUTIVE_API_CALLS)
257            if time.time() > timeout:
258                self.log.info(
259                    "Modem timestamp from GetAllCellInfo() for {} "
260                    "powered state "
261                    "not refreshed for {} sec".format(state, margin))
262                return False
263        time_delta = self.nano_to_sec(ts2 - ts1)
264        self.log.info("Updated Modem timestamp: {} in {} sec for "
265                      "UE in {} powered state".format(ts2, time_delta, state))
266        return time_delta
267
268    def _refresh_get_all_cell_info(self):
269        try:
270            modem_ts1, system_ts1 = self.get_all_cell_info_ts()
271            modem_ts2, system_ts2 = self.request_cell_info_update_ts()
272            modem_ts3, system_ts3 = self.get_all_cell_info_ts()
273            if modem_ts3 > modem_ts1:
274                self.log.info(
275                    "Modem TS from GetAllCellInfo() is updated after "
276                    "RequestCellInfoUpdate() is called"
277                    " by :{} secs".format(
278                        self.time_delta_in_sec(modem_ts3, modem_ts1)))
279                return True
280            else:
281                return False
282        except Exception as e:
283            self.log.error("Exception during GetAllCellInfo() Refresh:" +
284                           str(e))
285            return False
286
287    def _power_state_screen_off(self, apk_type):
288        """
289        Args:
290            apk_type: SL4A APK type - 'Q' for android Q and 'P' for android P
291
292        Returns:
293            Result True if Pass, False if Fail.
294        """
295        try:
296            self.ad.droid.goToSleepNow()
297            if apk_type == 'Q':
298                time_delta_low = self.request_cell_info_update_rate_limit(
299                    CELL_INFO_UPDATE_WAIT_TIME_LOW, 'low')
300            elif apk_type == 'P':
301                time_delta_low = self.get_all_cell_info_rate_limit(
302                    CELL_INFO_UPDATE_WAIT_TIME_LOW, 'low')
303            if int(time_delta_low) == RATE_LIMIT_LOW:
304                return True
305            else:
306                return False
307        except Exception as e:
308            self.log.error(
309                "Exception during request_cell_info_update_rate_limit():" +
310                str(e))
311            return False
312
313    def _power_state_screen_on_wifi_off(self, apk_type):
314        """
315        Args:
316            apk_type: SL4A APK type - 'Q' for android Q and 'P' for android P
317
318        Returns:
319            Result True if Pass, False if Fail.
320        """
321        try:
322            self.ad.droid.wakeUpNow()
323            wifi_test_utils.wifi_toggle_state(
324                self.ad, new_state=False, assert_on_fail=True)
325            if apk_type == 'Q':
326                time_delta = self.request_cell_info_update_rate_limit(
327                    CELL_INFO_UPDATE_WAIT_TIME_HIGH, 'high')
328            elif apk_type == 'P':
329                time_delta = self.get_all_cell_info_rate_limit(
330                    CELL_INFO_UPDATE_WAIT_TIME_HIGH, 'high')
331            if int(time_delta) == RATE_LIMIT_HIGH:
332                return True
333            else:
334                return False
335        except Exception as e:
336            self.log.error(
337                "Exception during request_cell_info_update_rate_limit():" +
338                str(e))
339            return False
340
341    def _rate_limit_charging_off(self, apk_type):
342        """
343        Args:
344            apk_type: SL4A APK type - 'Q' for android Q and 'P' for android P
345
346        Returns:
347            Result True if Pass, False if Fail.
348        """
349        try:
350            self.ad.droid.wakeUpNow()
351
352            if not ensure_wifi_connected(self.log, self.ad,
353                                         self.wifi_network_ssid,
354                                         self.wifi_network_pass):
355                self.ad.log.error("Failed to connect to wifi")
356                return False
357            """ Disable Charging """
358            disable_usb_charging(self.ad)
359            if apk_type == 'P':
360                time_delta = self.get_all_cell_info_rate_limit(
361                    CELL_INFO_UPDATE_WAIT_TIME_LOW, 'low')
362            elif apk_type == 'Q':
363                time_delta = self.request_cell_info_update_rate_limit(
364                    CELL_INFO_UPDATE_WAIT_TIME_LOW, 'low')
365            """ Enable Charging """
366            enable_usb_charging(self.ad)
367            if int(time_delta) == RATE_LIMIT_LOW:
368                return True
369            else:
370                return False
371        except Exception as e:
372            self.log.error("Exception in rate_limit function:" + str(e))
373            return False
374
375    def _rate_limit_switch(self, apk_type):
376        """
377        Args:
378            apk_type: SL4A APK type - 'Q' for android Q and 'P' for android P
379
380        Returns:
381            Result True if Pass, False if Fail.
382        """
383        try:
384            pass_count = 0
385            while pass_count != 3:
386                """Put UE in sleep for low powered state"""
387                self.ad.droid.goToSleepNow()
388                if apk_type == 'P':
389                    time_delta_low = self.get_all_cell_info_rate_limit(
390                        CELL_INFO_UPDATE_WAIT_TIME_LOW, 'low')
391                elif apk_type == 'Q':
392                    time_delta_low = self.request_cell_info_update_rate_limit(
393                        CELL_INFO_UPDATE_WAIT_TIME_LOW, 'low')
394                if int(time_delta_low) == RATE_LIMIT_LOW:
395                    """Wake up UE and turn ON WiFi for high powered state"""
396                    self.ad.droid.wakeUpNow()
397                    wifi_test_utils.wifi_toggle_state(
398                        self.ad, new_state=False, assert_on_fail=True)
399                    if apk_type == 'P':
400                        time_delta_high = self.get_all_cell_info_rate_limit(
401                            CELL_INFO_UPDATE_WAIT_TIME_HIGH, 'high')
402                    elif apk_type == 'Q':
403                        time_delta_high = \
404                            self.request_cell_info_update_rate_limit(
405                                CELL_INFO_UPDATE_WAIT_TIME_HIGH, 'high')
406                    if int(time_delta_high) == RATE_LIMIT_HIGH:
407                        pass_count += 1
408                    else:
409                        return False
410                else:
411                    return False
412            return True
413        except Exception as e:
414            self.log.error("Exception during rate limit switch:" + str(e))
415            return False
416
417    """ Tests Start """
418    """ Q Targeted APK Test Cases """
419
420    @test_tracker_info(uuid="")
421    @TelephonyBaseTest.tel_test_wrap
422    def test_q_request_cell_info_update(self):
423        """To verify a Q apk receives refreshed info when calling the
424        RequestCellInfoUpdate() API
425
426        Returns:
427            True if pass; False if fail
428        """
429        return self._request_cell_info_update()
430
431    @test_tracker_info(uuid="")
432    @TelephonyBaseTest.tel_test_wrap
433    def test_q_get_all_cell_info(self):
434        """ To verify a Q apk always receives cached info and never gets
435        refreshed info when calling the GetAllCellInfo() API.
436
437        Returns:
438            True if pass; False if fail
439        """
440        return self._get_all_cell_info(apk_type='Q')
441
442    @test_tracker_info(uuid="")
443    @TelephonyBaseTest.tel_test_wrap
444    def test_q_get_all_cell_info_refreshed(self):
445        """ To verify in a Q APK the cached info in GetAllCellInfo() is updated
446        after RequestCellInfoUpdate() API provides a refreshed cell info
447        from the modem.
448
449        Returns:
450            True if pass; False if fail
451        """
452        return self._refresh_get_all_cell_info()
453
454    @test_tracker_info(uuid="")
455    @TelephonyBaseTest.tel_test_wrap
456    def test_q_request_cell_info_update_screen_off(self):
457        """ To verify RATE_LIMIT_LOW is applied when the UE is in low powered
458        state (Screen Off)
459
460        Returns:
461            True if Pass and False if Fail
462        """
463        return self._power_state_screen_off(apk_type='Q')
464
465    @test_tracker_info(uuid="")
466    @TelephonyBaseTest.tel_test_wrap
467    def test_q_request_cell_info_update_screen_on_wifi_off(self):
468        """ To verify RATE_LIMIT_HIGH is applied when the UE is in high powered
469        state (Screen On / WiFi Off)
470
471        Returns:
472            True if Pass and False if Fail
473        """
474        return self._power_state_screen_on_wifi_off(apk_type='Q')
475
476    @test_tracker_info(uuid="")
477    @TelephonyBaseTest.tel_test_wrap
478    def test_q_rate_limit_charging_off(self):
479        """ To verify RATE_LIMIT_LOW is applied when the UE is in low powered
480        state (Screen On / WiFi On/Phone NOT charging)
481
482        Returns:
483            True if Pass and False if Fail
484        """
485        return self._rate_limit_charging_off(apk_type='Q')
486
487    @test_tracker_info(uuid="")
488    @TelephonyBaseTest.tel_test_wrap
489    def test_q_rate_limit_switch(self):
490        """ To verify rate limiting while UE is moving between high-powered &
491        low-powered states for 3 iterations.
492
493        Returns:
494            True if Pass and False if Fail.
495        """
496        return self._rate_limit_switch(apk_type='Q')
497
498    """ P Targeted APK Test Cases """
499
500    @test_tracker_info(uuid="")
501    @TelephonyBaseTest.tel_test_wrap
502    def test_p_get_all_cell_info(self):
503        """ To verify a P apk always receives refreshed info when calling the
504        GetAllCellInfo() API
505
506        Returns:
507            True if pass; False if fail
508        """
509        return self._get_all_cell_info(apk_type='P')
510
511    @test_tracker_info(uuid="")
512    @TelephonyBaseTest.tel_test_wrap
513    def test_p_get_all_cell_info_screen_off(self):
514        """ To verify RATE_LIMIT_LOW is applied when the UE is in low powered
515        state (Screen OFF)
516
517        Returns:
518            True if Pass and False if Fail
519        """
520        return self._power_state_screen_off(apk_type='P')
521
522    @test_tracker_info(uuid="")
523    @TelephonyBaseTest.tel_test_wrap
524    def test_p_get_all_cell_info_screen_on_wifi_off(self):
525        """ To verify RATE_LIMIT_HIGH is applied when the UE is in high powered
526        state (Screen ON/ WiFi OFF/ Cellular ON)
527
528        Returns:
529            True if Pass and False if Fail
530        """
531        return self._power_state_screen_on_wifi_off(apk_type='P')
532
533    @test_tracker_info(uuid="")
534    @TelephonyBaseTest.tel_test_wrap
535    def test_p_rate_limit_charging_off(self):
536        """ To verify RATE_LIMIT_LOW is applied when the UE is in low powered
537        state (Screen On / WiFi On/Phone NOT charging)
538
539        Returns:
540            True if Pass and False if Fail
541        """
542        return self._rate_limit_charging_off(apk_type='P')
543
544    @test_tracker_info(uuid="")
545    @TelephonyBaseTest.tel_test_wrap
546    def test_p_rate_limit_switch(self):
547        """ To verify rate limiting while UE is moving between high-powered &
548        low-powered states.
549
550        Returns:
551            True if Pass and False if Fail.
552        """
553        return self._rate_limit_switch(apk_type='P')
554
555    """ Tests End """
556