1#!/usr/bin/env python3.5
2#
3#   Copyright 2019 - Google
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 time
18import re
19import os
20import math
21import shutil
22import fnmatch
23import posixpath
24from collections import namedtuple
25
26from acts import utils
27from acts import signals
28from acts.controllers.android_device import list_adb_devices
29from acts.controllers.android_device import list_fastboot_devices
30from acts.controllers.android_device import DEFAULT_QXDM_LOG_PATH
31from acts.controllers.android_device import SL4A_APK_NAME
32from acts.test_utils.wifi import wifi_test_utils as wutils
33from acts.test_utils.tel import tel_test_utils as tutils
34from acts.utils import get_current_epoch_time
35
36WifiEnums = wutils.WifiEnums
37PULL_TIMEOUT = 300
38GNSSSTATUS_LOG_PATH = (
39    "/storage/emulated/0/Android/data/com.android.gpstool/files/")
40QXDM_MASKS = ["GPS.cfg", "GPS-general.cfg", "default.cfg"]
41TTFF_REPORT = namedtuple("TTFF_REPORT", "ttff_loop ttff_sec ttff_pe ttff_cn")
42TRACK_REPORT = namedtuple(
43    "TRACK_REPORT", "track_l5flag track_pe track_top4cn track_cn")
44LOCAL_PROP_FILE_CONTENTS =  """\
45log.tag.LocationManagerService=VERBOSE
46log.tag.GnssLocationProvider=VERBOSE
47log.tag.GnssMeasurementsProvider=VERBOSE
48log.tag.GpsNetInitiatedHandler=VERBOSE
49log.tag.GnssNetworkConnectivityHandler=VERBOSE
50log.tag.ConnectivityService=VERBOSE
51log.tag.ConnectivityManager=VERBOSE
52log.tag.GnssVisibilityControl=VERBOSE"""
53
54
55class GnssTestUtilsError(Exception):
56    pass
57
58
59def remount_device(ad):
60    """Remount device file system to read and write.
61
62    Args:
63        ad: An AndroidDevice object.
64    """
65    for retries in range(5):
66        ad.root_adb()
67        remount_result = ad.adb.remount()
68        ad.log.info("Attempt %d - %s" % (retries + 1, remount_result))
69        if "remount succeeded" in remount_result:
70            break
71        if ad.adb.getprop("ro.boot.veritymode") == "enforcing":
72            disable_verity_result = ad.adb.disable_verity()
73            reboot(ad)
74
75
76def reboot(ad):
77    """Reboot device and check if mobile data is available.
78
79    Args:
80        ad: An AndroidDevice object.
81    """
82    ad.log.info("Reboot device to make changes take effect.")
83    ad.reboot()
84    ad.unlock_screen(password=None)
85    if not int(ad.adb.shell("settings get global mobile_data")) == 1:
86        set_mobile_data(ad, True)
87    utils.sync_device_time(ad)
88
89
90def enable_gnss_verbose_logging(ad):
91    """Enable GNSS VERBOSE Logging and persistent logcat.
92
93    Args:
94        ad: An AndroidDevice object.
95    """
96    remount_device(ad)
97    ad.log.info("Enable GNSS VERBOSE Logging and persistent logcat.")
98    ad.adb.shell("echo DEBUG_LEVEL = 5 >> /vendor/etc/gps.conf")
99    ad.adb.shell("echo %r >> /data/local.prop" % LOCAL_PROP_FILE_CONTENTS)
100    ad.adb.shell("chmod 644 /data/local.prop")
101    ad.adb.shell("setprop persist.logd.logpersistd.size 20000")
102    ad.adb.shell("setprop persist.logd.size 16777216")
103    ad.adb.shell("setprop persist.vendor.radio.adb_log_on 1")
104    ad.adb.shell("setprop persist.logd.logpersistd logcatd")
105    ad.adb.shell("setprop log.tag.copresGcore VERBOSE")
106    ad.adb.shell("sync")
107
108
109def get_am_flags(value):
110    """Returns the (value, type) flags for a given python value."""
111    if type(value) is bool:
112        return str(value).lower(), 'boolean'
113    elif type(value) is str:
114        return value, 'string'
115    raise ValueError("%s should be either 'boolean' or 'string'" % value)
116
117
118def enable_compact_and_particle_fusion_log(ad):
119    """Enable CompactLog, FLP particle fusion log and disable gms
120    location-based quake monitoring.
121
122    Args:
123        ad: An AndroidDevice object.
124    """
125    ad.root_adb()
126    ad.log.info("Enable FLP flags and Disable GMS location-based quake "
127                "monitoring.")
128    overrides = {
129        'compact_log_enabled': True,
130        'flp_use_particle_fusion': True,
131        'flp_particle_fusion_extended_bug_report': True,
132        'flp_event_log_size': '86400',
133        'proks_config': '28',
134        'flp_particle_fusion_bug_report_window_sec': '86400',
135        'flp_particle_fusion_bug_report_max_buffer_size': '86400',
136        'seismic_data_collection': False,
137        'Ealert__enable': False,
138    }
139    for flag, python_value in overrides.items():
140        value, type = get_am_flags(python_value)
141        cmd = ("am broadcast -a com.google.android.gms.phenotype.FLAG_OVERRIDE "
142               "--es package com.google.android.location --es user \* "
143               "--esa flags %s --esa values %s --esa types %s "
144               "com.google.android.gms" % (flag, value, type))
145        ad.adb.shell(cmd)
146    ad.adb.shell("am force-stop com.google.android.gms")
147    ad.adb.shell("am broadcast -a com.google.android.gms.INITIALIZE")
148
149
150def disable_xtra_throttle(ad):
151    """Disable XTRA throttle will have no limit to download XTRA data.
152
153    Args:
154        ad: An AndroidDevice object.
155    """
156    remount_device(ad)
157    ad.log.info("Disable XTRA Throttle.")
158    ad.adb.shell("echo XTRA_TEST_ENABLED=1 >> /vendor/etc/gps.conf")
159    ad.adb.shell("echo XTRA_THROTTLE_ENABLED=0 >> /vendor/etc/gps.conf")
160
161
162def enable_supl_mode(ad):
163    """Enable SUPL back on for next test item.
164
165    Args:
166        ad: An AndroidDevice object.
167    """
168    remount_device(ad)
169    ad.log.info("Enable SUPL mode.")
170    ad.adb.shell("echo SUPL_MODE=1 >> /etc/gps_debug.conf")
171
172
173def disable_supl_mode(ad):
174    """Kill SUPL to test XTRA only test item.
175
176    Args:
177        ad: An AndroidDevice object.
178    """
179    remount_device(ad)
180    ad.log.info("Disable SUPL mode.")
181    ad.adb.shell("echo SUPL_MODE=0 >> /etc/gps_debug.conf")
182    reboot(ad)
183
184
185def kill_xtra_daemon(ad):
186    """Kill XTRA daemon to test SUPL only test item.
187
188    Args:
189        ad: An AndroidDevice object.
190    """
191    ad.root_adb()
192    ad.log.info("Disable XTRA-daemon until next reboot.")
193    ad.adb.shell("killall xtra-daemon")
194
195
196def disable_private_dns_mode(ad):
197    """Due to b/118365122, it's better to disable private DNS mode while
198       testing. 8.8.8.8 private dns sever is unstable now, sometimes server
199       will not response dns query suddenly.
200
201    Args:
202        ad: An AndroidDevice object.
203    """
204    tutils.get_operator_name(ad.log, ad, subId=None)
205    if ad.adb.shell("settings get global private_dns_mode") != "off":
206        ad.log.info("Disable Private DNS mode.")
207        ad.adb.shell("settings put global private_dns_mode off")
208
209
210def _init_device(ad):
211    """Init GNSS test devices.
212
213    Args:
214        ad: An AndroidDevice object.
215    """
216    enable_gnss_verbose_logging(ad)
217    enable_compact_and_particle_fusion_log(ad)
218    disable_xtra_throttle(ad)
219    enable_supl_mode(ad)
220    ad.adb.shell("settings put system screen_off_timeout 1800000")
221    wutils.wifi_toggle_state(ad, False)
222    ad.log.info("Setting Bluetooth state to False")
223    ad.droid.bluetoothToggleState(False)
224    set_gnss_qxdm_mask(ad, QXDM_MASKS)
225    check_location_service(ad)
226    set_wifi_and_bt_scanning(ad, True)
227    disable_private_dns_mode(ad)
228    init_gtw_gpstool(ad)
229    reboot(ad)
230
231
232def connect_to_wifi_network(ad, network):
233    """Connection logic for open and psk wifi networks.
234
235    Args:
236        ad: An AndroidDevice object.
237        network: Dictionary with network info.
238    """
239    SSID = network[WifiEnums.SSID_KEY]
240    ad.ed.clear_all_events()
241    wutils.reset_wifi(ad)
242    wutils.start_wifi_connection_scan_and_ensure_network_found(ad, SSID)
243    wutils.wifi_connect(ad, network, num_of_tries=5)
244
245
246def set_wifi_and_bt_scanning(ad, state=True):
247    """Set Wi-Fi and Bluetooth scanning on/off in Settings -> Location
248
249    Args:
250        ad: An AndroidDevice object.
251        state: True to turn on "Wi-Fi and Bluetooth scanning".
252            False to turn off "Wi-Fi and Bluetooth scanning".
253    """
254    ad.root_adb()
255    if state:
256        ad.adb.shell("settings put global wifi_scan_always_enabled 1")
257        ad.adb.shell("settings put global ble_scan_always_enabled 1")
258        ad.log.info("Wi-Fi and Bluetooth scanning are enabled")
259    else:
260        ad.adb.shell("settings put global wifi_scan_always_enabled 0")
261        ad.adb.shell("settings put global ble_scan_always_enabled 0")
262        ad.log.info("Wi-Fi and Bluetooth scanning are disabled")
263
264
265def check_location_service(ad):
266    """Set location service on.
267       Verify if location service is available.
268
269    Args:
270        ad: An AndroidDevice object.
271    """
272    remount_device(ad)
273    utils.set_location_service(ad, True)
274    location_mode = int(ad.adb.shell("settings get secure location_mode"))
275    ad.log.info("Current Location Mode >> %d" % location_mode)
276    if location_mode != 3:
277        raise signals.TestError("Failed to turn Location on")
278
279
280def clear_logd_gnss_qxdm_log(ad):
281    """Clear /data/misc/logd,
282    /storage/emulated/0/Android/data/com.android.gpstool/files and
283    /data/vendor/radio/diag_logs/logs from previous test item then reboot.
284
285    Args:
286        ad: An AndroidDevice object.
287    """
288    remount_device(ad)
289    ad.log.info("Clear Logd, GNSS and QXDM Log from previous test item.")
290    ad.adb.shell("rm -rf /data/misc/logd", ignore_status=True)
291    ad.adb.shell('find %s -name "*.txt" -type f -delete' % GNSSSTATUS_LOG_PATH)
292    output_path = posixpath.join(DEFAULT_QXDM_LOG_PATH, "logs")
293    ad.adb.shell("rm -rf %s" % output_path, ignore_status=True)
294    reboot(ad)
295
296
297def get_gnss_qxdm_log(ad, qdb_path):
298    """Get /storage/emulated/0/Android/data/com.android.gpstool/files and
299    /data/vendor/radio/diag_logs/logs for test item.
300
301    Args:
302        ad: An AndroidDevice object.
303        qdb_path: The path of qdsp6m.qdb on different projects.
304    """
305    log_path = ad.device_log_path
306    os.makedirs(log_path, exist_ok=True)
307    gnss_log_name = "gnssstatus_log_%s_%s" % (ad.model, ad.serial)
308    gnss_log_path = posixpath.join(log_path, gnss_log_name)
309    os.makedirs(gnss_log_path, exist_ok=True)
310    ad.log.info("Pull GnssStatus Log to %s" % gnss_log_path)
311    ad.adb.pull("%s %s" % (GNSSSTATUS_LOG_PATH+".", gnss_log_path),
312                timeout=PULL_TIMEOUT, ignore_status=True)
313    shutil.make_archive(gnss_log_path, "zip", gnss_log_path)
314    shutil.rmtree(gnss_log_path)
315    output_path = posixpath.join(DEFAULT_QXDM_LOG_PATH, "logs/.")
316    file_count = ad.adb.shell(
317        "find %s -type f -iname *.qmdl | wc -l" % output_path)
318    if not int(file_count) == 0:
319        qxdm_log_name = "QXDM_%s_%s" % (ad.model, ad.serial)
320        qxdm_log_path = posixpath.join(log_path, qxdm_log_name)
321        os.makedirs(qxdm_log_path, exist_ok=True)
322        ad.log.info("Pull QXDM Log %s to %s" % (output_path, qxdm_log_path))
323        ad.adb.pull("%s %s" % (output_path, qxdm_log_path),
324                    timeout=PULL_TIMEOUT, ignore_status=True)
325        for path in qdb_path:
326            output = ad.adb.pull("%s %s" % (path, qxdm_log_path),
327                                 timeout=PULL_TIMEOUT, ignore_status=True)
328            if "No such file or directory" in output:
329                continue
330            break
331        shutil.make_archive(qxdm_log_path, "zip", qxdm_log_path)
332        shutil.rmtree(qxdm_log_path)
333    else:
334        ad.log.error("QXDM file count is %d. There is no QXDM log on device."
335                     % int(file_count))
336
337
338def set_mobile_data(ad, state):
339    """Set mobile data on or off and check mobile data state.
340
341    Args:
342        ad: An AndroidDevice object.
343        state: True to enable mobile data. False to disable mobile data.
344    """
345    ad.root_adb()
346    if state:
347        ad.log.info("Enable mobile data.")
348        ad.adb.shell("svc data enable")
349    else:
350        ad.log.info("Disable mobile data.")
351        ad.adb.shell("svc data disable")
352    time.sleep(5)
353    out = int(ad.adb.shell("settings get global mobile_data"))
354    if state and out == 1:
355        ad.log.info("Mobile data is enabled and set to %d" % out)
356    elif not state and out == 0:
357        ad.log.info("Mobile data is disabled and set to %d" % out)
358    else:
359        ad.log.error("Mobile data is at unknown state and set to %d" % out)
360
361
362def gnss_trigger_modem_ssr_by_adb(ad, dwelltime=60):
363    """Trigger modem SSR crash by adb and verify if modem crash and recover
364    successfully.
365
366    Args:
367        ad: An AndroidDevice object.
368        dwelltime: Waiting time for modem reset. Default is 60 seconds.
369
370    Returns:
371        True if success.
372        False if failed.
373    """
374    begin_time = get_current_epoch_time()
375    ad.root_adb()
376    cmds = ("echo restart > /sys/kernel/debug/msm_subsys/modem",
377            r"echo 'at+cfun=1,1\r' > /dev/at_mdm0")
378    for cmd in cmds:
379        ad.log.info("Triggering modem SSR crash by %s" % cmd)
380        output = ad.adb.shell(cmd, ignore_status=True)
381        if "No such file or directory" in output:
382            continue
383        break
384    time.sleep(dwelltime)
385    ad.send_keycode("HOME")
386    logcat_results = ad.search_logcat("SSRObserver", begin_time)
387    if logcat_results:
388        for ssr in logcat_results:
389            if "mSubsystem='modem', mCrashReason" in ssr["log_message"]:
390                ad.log.debug(ssr["log_message"])
391                ad.log.info("Triggering modem SSR crash successfully.")
392                return True
393        raise signals.TestError("Failed to trigger modem SSR crash")
394    raise signals.TestError("No SSRObserver found in logcat")
395
396
397def gnss_trigger_modem_ssr_by_mds(ad, dwelltime=60):
398    """Trigger modem SSR crash by mds tool and verify if modem crash and recover
399    successfully.
400
401    Args:
402        ad: An AndroidDevice object.
403        dwelltime: Waiting time for modem reset. Default is 60 seconds.
404    """
405    mds_check = ad.adb.shell("pm path com.google.mdstest")
406    if not mds_check:
407        raise signals.TestError("MDS Tool is not properly installed.")
408    ad.root_adb()
409    cmd = ('am instrument -w -e request "4b 25 03 00" '
410           '"com.google.mdstest/com.google.mdstest.instrument'
411           '.ModemCommandInstrumentation"')
412    ad.log.info("Triggering modem SSR crash by MDS")
413    output = ad.adb.shell(cmd, ignore_status=True)
414    ad.log.debug(output)
415    time.sleep(dwelltime)
416    ad.send_keycode("HOME")
417    if "SUCCESS" in output:
418        ad.log.info("Triggering modem SSR crash by MDS successfully.")
419    else:
420        raise signals.TestError(
421            "Failed to trigger modem SSR crash by MDS. \n%s" % output)
422
423
424def pull_mdstool(ad):
425    """Pull ModemDiagnosticSystemTest.apk from device.
426
427    Args:
428        ad: An AndroidDevice object.
429    """
430    out = ad.adb.shell("pm path com.google.mdstest")
431    result = re.search(r"package:(.*)", out)
432    if not result:
433        raise signals.TestError("No ModemDiagnosticSystemTest.apk found.")
434    else:
435        mds_tool = result.group(1)
436        ad.log.info("Get ModemDiagnosticSystemTest.apk from %s" % mds_tool)
437        apkdir = "/tmp/MDS/"
438        os.makedirs(apkdir, exist_ok=True)
439        ad.pull_files([mds_tool], apkdir)
440
441
442def reinstall_mdstool(ad):
443    """Reinstall ModemDiagnosticSystemTest.apk.
444
445    Args:
446        ad: An AndroidDevice object.
447    """
448    ad.log.info("Re-install ModemDiagnosticSystemTest.apk")
449    ad.adb.install("-r -g -t /tmp/MDS/base.apk")
450    mds_check = ad.adb.shell("pm path com.google.mdstest")
451    if not mds_check:
452        raise signals.TestError("MDS Tool is not properly re-installed.")
453    ad.log.info("MDS Tool is re-installed successfully.")
454
455
456def check_xtra_download(ad, begin_time):
457    """Verify XTRA download success log message in logcat.
458
459    Args:
460        ad: An AndroidDevice object.
461        begin_time: test begin time
462
463    Returns:
464        True: xtra_download if XTRA downloaded and injected successfully
465        otherwise return False.
466    """
467    ad.send_keycode("HOME")
468    logcat_results = ad.search_logcat("XTRA download success. "
469                                      "inject data into modem", begin_time)
470    if logcat_results:
471        ad.log.debug("%s" % logcat_results[-1]["log_message"])
472        ad.log.info("XTRA downloaded and injected successfully.")
473        return True
474    ad.log.error("XTRA downloaded FAIL.")
475    return False
476
477
478def pull_gtw_gpstool(ad):
479    """Pull GTW_GPSTool apk from device.
480
481    Args:
482        ad: An AndroidDevice object.
483    """
484    out = ad.adb.shell("pm path com.android.gpstool")
485    result = re.search(r"package:(.*)", out)
486    if not result:
487        tutils.abort_all_tests(ad.log, "Couldn't find GTW GPSTool apk")
488    else:
489        GTW_GPSTool_apk = result.group(1)
490        ad.log.info("Get GTW GPSTool apk from %s" % GTW_GPSTool_apk)
491        apkdir = "/tmp/GNSS/"
492        os.makedirs(apkdir, exist_ok=True)
493        ad.pull_files([GTW_GPSTool_apk], apkdir)
494
495
496def reinstall_gtw_gpstool(ad):
497    """Reinstall GTW_GPSTool apk.
498
499    Args:
500        ad: An AndroidDevice object.
501    """
502    ad.log.info("Re-install GTW GPSTool")
503    ad.adb.install("-r -g -t /tmp/GNSS/base.apk")
504    gpstool_check = ad.adb.shell("pm path com.android.gpstool")
505    if not gpstool_check:
506        raise signals.TestError("GTW GPSTool is not properly re-installed.")
507    ad.log.info("GTW GPSTool is re-installed successfully.")
508
509
510def init_gtw_gpstool(ad):
511    """Init GTW_GPSTool apk.
512
513    Args:
514        ad: An AndroidDevice object.
515    """
516    remount_device(ad)
517    pull_gtw_gpstool(ad)
518    ad.adb.shell("settings put global verifier_verify_adb_installs 0")
519    reinstall_gtw_gpstool(ad)
520
521
522def fastboot_factory_reset(ad):
523    """Factory reset the device in fastboot mode.
524       Pull sl4a apk from device. Terminate all sl4a sessions,
525       Reboot the device to bootloader,
526       factory reset the device by fastboot.
527       Reboot the device. wait for device to complete booting
528       Re-install and start an sl4a session.
529
530    Args:
531        ad: An AndroidDevice object.
532
533    Returns:
534        True if factory reset process complete.
535    """
536    status = True
537    skip_setup_wizard = True
538    out = ad.adb.shell("pm path %s" % SL4A_APK_NAME)
539    result = re.search(r"package:(.*)", out)
540    if not result:
541        tutils.abort_all_tests(ad.log, "Couldn't find sl4a apk")
542    else:
543        sl4a_apk = result.group(1)
544        ad.log.info("Get sl4a apk from %s" % sl4a_apk)
545        ad.pull_files([sl4a_apk], "/tmp/")
546    pull_gtw_gpstool(ad)
547    pull_mdstool(ad)
548    tutils.stop_qxdm_logger(ad)
549    ad.stop_services()
550    attempts = 3
551    for i in range(1, attempts + 1):
552        try:
553            if ad.serial in list_adb_devices():
554                ad.log.info("Reboot to bootloader")
555                ad.adb.reboot("bootloader", ignore_status=True)
556                time.sleep(10)
557            if ad.serial in list_fastboot_devices():
558                ad.log.info("Factory reset in fastboot")
559                ad.fastboot._w(timeout=300, ignore_status=True)
560                time.sleep(30)
561                ad.log.info("Reboot in fastboot")
562                ad.fastboot.reboot()
563            ad.wait_for_boot_completion()
564            ad.root_adb()
565            if ad.skip_sl4a:
566                break
567            if ad.is_sl4a_installed():
568                break
569            ad.log.info("Re-install sl4a")
570            ad.adb.shell("settings put global verifier_verify_adb_installs 0")
571            ad.adb.install("-r -g -t /tmp/base.apk")
572            reinstall_gtw_gpstool(ad)
573            reinstall_mdstool(ad)
574            time.sleep(10)
575            break
576        except Exception as e:
577            ad.log.error(e)
578            if i == attempts:
579                tutils.abort_all_tests(ad.log, str(e))
580            time.sleep(5)
581    try:
582        ad.start_adb_logcat()
583    except Exception as e:
584        ad.log.error(e)
585    if skip_setup_wizard:
586        ad.exit_setup_wizard()
587    if ad.skip_sl4a:
588        return status
589    tutils.bring_up_sl4a(ad)
590    return status
591
592
593def clear_aiding_data_by_gtw_gpstool(ad):
594    """Launch GTW GPSTool and Clear all GNSS aiding data.
595       Wait 5 seconds for GTW GPStool to clear all GNSS aiding
596       data properly.
597
598    Args:
599        ad: An AndroidDevice object.
600    """
601    ad.log.info("Launch GTW GPSTool and Clear all GNSS aiding data")
602    ad.adb.shell("am start -S -n com.android.gpstool/.GPSTool --es mode clear")
603    time.sleep(10)
604
605
606def start_gnss_by_gtw_gpstool(ad, state, type="gnss", bgdisplay=False):
607    """Start or stop GNSS on GTW_GPSTool.
608
609    Args:
610        ad: An AndroidDevice object.
611        state: True to start GNSS. False to Stop GNSS.
612        type: Different API for location fix. Use gnss/flp/nmea
613        bgdisplay: true to run GTW when Display off.
614                   false to not run GTW when Display off.
615    """
616    if state and not bgdisplay:
617        ad.adb.shell("am start -S -n com.android.gpstool/.GPSTool "
618                     "--es mode gps --es type %s" % type)
619    elif state and bgdisplay:
620        ad.adb.shell("am start -S -n com.android.gpstool/.GPSTool --es mode "
621                     "gps --es type {} --ez BG {}".format(type, bgdisplay))
622    if not state:
623        ad.log.info("Stop %s on GTW_GPSTool." % type)
624        ad.adb.shell("am broadcast -a com.android.gpstool.stop_gps_action")
625    time.sleep(3)
626
627
628def process_gnss_by_gtw_gpstool(ad, criteria, type="gnss"):
629    """Launch GTW GPSTool and Clear all GNSS aiding data
630       Start GNSS tracking on GTW_GPSTool.
631
632    Args:
633        ad: An AndroidDevice object.
634        criteria: Criteria for current test item.
635        type: Different API for location fix. Use gnss/flp/nmea
636
637    Returns:
638        True: First fix TTFF are within criteria.
639        False: First fix TTFF exceed criteria.
640    """
641    retries = 3
642    for i in range(retries):
643        begin_time = get_current_epoch_time()
644        clear_aiding_data_by_gtw_gpstool(ad)
645        ad.log.info("Start %s on GTW_GPSTool - attempt %d" % (type.upper(),
646                                                              i+1))
647        start_gnss_by_gtw_gpstool(ad, True, type)
648        for _ in range(10 + criteria):
649            logcat_results = ad.search_logcat("First fixed", begin_time)
650            if logcat_results:
651                ad.log.debug(logcat_results[-1]["log_message"])
652                first_fixed = int(logcat_results[-1]["log_message"].split()[-1])
653                ad.log.info("%s First fixed = %.3f seconds" %
654                            (type.upper(), first_fixed/1000))
655                if (first_fixed/1000) <= criteria:
656                    return True
657                start_gnss_by_gtw_gpstool(ad, False, type)
658                raise signals.TestFailure("Fail to get %s location fixed "
659                                          "within %d seconds criteria."
660                                          % (type.upper(), criteria))
661            time.sleep(1)
662        if not ad.is_adb_logcat_on:
663            ad.start_adb_logcat()
664        check_currrent_focus_app(ad)
665        start_gnss_by_gtw_gpstool(ad, False, type)
666    raise signals.TestFailure("Fail to get %s location fixed within %d "
667                              "attempts." % (type.upper(), retries))
668
669def start_ttff_by_gtw_gpstool(ad, ttff_mode, iteration, aid_data=False):
670    """Identify which TTFF mode for different test items.
671
672    Args:
673        ad: An AndroidDevice object.
674        ttff_mode: TTFF Test mode for current test item.
675        iteration: Iteration of TTFF cycles.
676        aid_data: Boolean for identify aid_data existed or not
677    """
678    begin_time = get_current_epoch_time()
679    if (ttff_mode == "hs" or ttff_mode == "ws") and not aid_data:
680        ad.log.info("Wait 5 minutes to start TTFF %s..." % ttff_mode.upper())
681        time.sleep(300)
682    if ttff_mode == "cs":
683        ad.log.info("Start TTFF Cold Start...")
684        time.sleep(3)
685    for i in range(1, 4):
686        ad.adb.shell("am broadcast -a com.android.gpstool.ttff_action "
687                     "--es ttff %s --es cycle %d" % (ttff_mode, iteration))
688        time.sleep(1)
689        if ad.search_logcat("act=com.android.gpstool.start_test_action",
690                            begin_time):
691            ad.log.info("Send TTFF start_test_action successfully.")
692            break
693    else:
694        check_currrent_focus_app(ad)
695        raise signals.TestError("Fail to send TTFF start_test_action.")
696
697
698def gnss_tracking_via_gtw_gpstool(ad, criteria, type="gnss", testtime=60):
699    """Start GNSS/FLP tracking tests for input testtime on GTW_GPSTool.
700
701    Args:
702        ad: An AndroidDevice object.
703        criteria: Criteria for current TTFF.
704        type: Different API for location fix. Use gnss/flp/nmea
705        testtime: Tracking test time for minutes. Default set to 60 minutes.
706    """
707    process_gnss_by_gtw_gpstool(ad, criteria, type)
708    ad.log.info("Start %s tracking test for %d minutes" % (type.upper(),
709                                                           testtime))
710    begin_time = get_current_epoch_time()
711    while get_current_epoch_time() - begin_time < testtime * 60 * 1000 :
712        if not ad.is_adb_logcat_on:
713            ad.start_adb_logcat()
714        crash_result = ad.search_logcat("Force finishing activity "
715                                        "com.android.gpstool/.GPSTool",
716                                        begin_time)
717        if crash_result:
718            raise signals.TestError("GPSTool crashed. Abort test.")
719    ad.log.info("Successfully tested for %d minutes" % testtime)
720    start_gnss_by_gtw_gpstool(ad, False, type)
721
722
723def parse_gtw_gpstool_log(ad, true_position, type="gnss"):
724    """Process GNSS/FLP API logs from GTW GPSTool and output track_data to
725    test_run_info for ACTS plugin to parse and display on MobileHarness as
726    Property.
727
728    Args:
729        ad: An AndroidDevice object.
730        true_position: Coordinate as [latitude, longitude] to calculate
731        position error.
732        type: Different API for location fix. Use gnss/flp/nmea
733    """
734    test_logfile = {}
735    track_data = {}
736    history_top4_cn = 0
737    history_cn = 0
738    l5flag = "false"
739    file_count = int(ad.adb.shell("find %s -type f -iname *.txt | wc -l"
740                                  % GNSSSTATUS_LOG_PATH))
741    if file_count != 1:
742        ad.log.error("%d API logs exist." % file_count)
743    dir = ad.adb.shell("ls %s" % GNSSSTATUS_LOG_PATH).split()
744    for path_key in dir:
745        if fnmatch.fnmatch(path_key, "*.txt"):
746            logpath = posixpath.join(GNSSSTATUS_LOG_PATH, path_key)
747            out = ad.adb.shell("wc -c %s" % logpath)
748            file_size = int(out.split(" ")[0])
749            if file_size < 2000:
750                ad.log.info("Skip log %s due to log size %d bytes" %
751                            (path_key, file_size))
752                continue
753            test_logfile = logpath
754    if not test_logfile:
755        raise signals.TestError("Failed to get test log file in device.")
756    lines = ad.adb.shell("cat %s" % test_logfile).split("\n")
757    for line in lines:
758        if "History Avg Top4" in line:
759            history_top4_cn = float(line.split(":")[-1].strip())
760        if "History Avg" in line:
761            history_cn = float(line.split(":")[-1].strip())
762        if "L5 used in fix" in line:
763            l5flag = line.split(":")[-1].strip()
764        if "Latitude" in line:
765            track_lat = float(line.split(":")[-1].strip())
766        if "Longitude" in line:
767            track_long = float(line.split(":")[-1].strip())
768        if "Time" in line:
769            track_utc = line.split("Time:")[-1].strip()
770            if track_utc in track_data.keys():
771                continue
772            track_pe = calculate_position_error(ad, track_lat, track_long,
773                                                true_position)
774            track_data[track_utc] = TRACK_REPORT(track_l5flag=l5flag,
775                                                 track_pe=track_pe,
776                                                 track_top4cn=history_top4_cn,
777                                                 track_cn=history_cn)
778    ad.log.debug(track_data)
779    prop_basename = "TestResult %s_tracking_" % type.upper()
780    time_list = sorted(track_data.keys())
781    l5flag_list = [track_data[key].track_l5flag for key in time_list]
782    pe_list = [float(track_data[key].track_pe) for key in time_list]
783    top4cn_list = [float(track_data[key].track_top4cn) for key in time_list]
784    cn_list = [float(track_data[key].track_cn) for key in time_list]
785    ad.log.info(prop_basename+"StartTime %s" % time_list[0].replace(" ", "-"))
786    ad.log.info(prop_basename+"EndTime %s" % time_list[-1].replace(" ", "-"))
787    ad.log.info(prop_basename+"TotalFixPoints %d" % len(time_list))
788    ad.log.info(prop_basename+"L5FixRate "+'{percent:.2%}'.format(
789        percent=l5flag_list.count("true")/len(l5flag_list)))
790    ad.log.info(prop_basename+"AvgDis %.1f" % (sum(pe_list)/len(pe_list)))
791    ad.log.info(prop_basename+"MaxDis %.1f" % max(pe_list))
792    ad.log.info(prop_basename+"AvgTop4Signal %.1f" % top4cn_list[-1])
793    ad.log.info(prop_basename+"AvgSignal %.1f" % cn_list[-1])
794
795
796def process_ttff_by_gtw_gpstool(ad, begin_time, true_position, type="gnss"):
797    """Process TTFF and record results in ttff_data.
798
799    Args:
800        ad: An AndroidDevice object.
801        begin_time: test begin time.
802        true_position: Coordinate as [latitude, longitude] to calculate
803        position error.
804        type: Different API for location fix. Use gnss/flp/nmea
805
806    Returns:
807        ttff_data: A dict of all TTFF data.
808    """
809    ttff_data = {}
810    ttff_loop_time = get_current_epoch_time()
811    while True:
812        if get_current_epoch_time() - ttff_loop_time >= 120000:
813            raise signals.TestError("Fail to search specific GPSService "
814                                    "message in logcat. Abort test.")
815        if not ad.is_adb_logcat_on:
816            ad.start_adb_logcat()
817        stop_gps_results = ad.search_logcat("stop gps test", begin_time)
818        if stop_gps_results:
819            ad.send_keycode("HOME")
820            break
821        crash_result = ad.search_logcat("Force finishing activity "
822                                        "com.android.gpstool/.GPSTool",
823                                        begin_time)
824        if crash_result:
825            raise signals.TestError("GPSTool crashed. Abort test.")
826        logcat_results = ad.search_logcat("write TTFF log", begin_time)
827        if logcat_results:
828            ttff_log = logcat_results[-1]["log_message"].split()
829            ttff_loop = int(ttff_log[8].split(":")[-1])
830            if ttff_loop in ttff_data.keys():
831                continue
832            ttff_loop_time = get_current_epoch_time()
833            ttff_sec = float(ttff_log[11])
834            if ttff_sec != 0.0:
835                ttff_cn = float(ttff_log[18].strip("]"))
836                if type == "gnss":
837                    gnss_results = ad.search_logcat("GPSService: Check item",
838                                                    begin_time)
839                    if gnss_results:
840                        ad.log.debug(gnss_results[-1]["log_message"])
841                        gnss_location_log = \
842                            gnss_results[-1]["log_message"].split()
843                        ttff_lat = float(
844                            gnss_location_log[8].split("=")[-1].strip(","))
845                        ttff_lon = float(
846                            gnss_location_log[9].split("=")[-1].strip(","))
847                elif type == "flp":
848                    flp_results = ad.search_logcat("GPSService: FLP Location",
849                                                   begin_time)
850                    if flp_results:
851                        ad.log.debug(flp_results[-1]["log_message"])
852                        flp_location_log = \
853                            flp_results[-1]["log_message"].split()
854                        ttff_lat = float(flp_location_log[8].split(",")[0])
855                        ttff_lon = float(flp_location_log[8].split(",")[1])
856            else:
857                ttff_cn = float(ttff_log[19].strip("]"))
858                ttff_lat = 0.0
859                ttff_lon = 0.0
860            ad.log.debug("TTFF Loop %d - (Lat, Lon) = (%s, %s)" % (ttff_loop,
861                                                                   ttff_lat,
862                                                                   ttff_lon))
863            ttff_pe = calculate_position_error(ad, ttff_lat, ttff_lon,
864                                               true_position)
865            ttff_data[ttff_loop] = TTFF_REPORT(ttff_loop=ttff_loop,
866                                               ttff_sec=ttff_sec,
867                                               ttff_pe=ttff_pe,
868                                               ttff_cn=ttff_cn)
869            ad.log.info("Loop %d = %.1f seconds, "
870                        "Position Error = %.1f meters, "
871                        "Average Signal = %.1f dbHz"
872                        % (ttff_loop, ttff_sec, ttff_pe, ttff_cn))
873    return ttff_data
874
875
876def check_ttff_data(ad, ttff_data, ttff_mode, criteria):
877    """Verify all TTFF results from ttff_data.
878
879    Args:
880        ad: An AndroidDevice object.
881        ttff_data: TTFF data of secs, position error and signal strength.
882        ttff_mode: TTFF Test mode for current test item.
883        criteria: Criteria for current test item.
884
885    Returns:
886        True: All TTFF results are within criteria.
887        False: One or more TTFF results exceed criteria or Timeout.
888    """
889    ad.log.info("%d iterations of TTFF %s tests finished."
890                % (len(ttff_data.keys()), ttff_mode))
891    ad.log.info("%s PASS criteria is %d seconds" % (ttff_mode, criteria))
892    ad.log.debug("%s TTFF data: %s" % (ttff_mode, ttff_data))
893    ttff_property_key_and_value(ad, ttff_data, ttff_mode)
894    if len(ttff_data.keys()) == 0:
895        ad.log.error("GTW_GPSTool didn't process TTFF properly.")
896        return False
897    elif any(float(ttff_data[key].ttff_sec) == 0.0 for key in ttff_data.keys()):
898        ad.log.error("One or more TTFF %s Timeout" % ttff_mode)
899        return False
900    elif any(float(ttff_data[key].ttff_sec) >= criteria for key in
901             ttff_data.keys()):
902        ad.log.error("One or more TTFF %s are over test criteria %d seconds"
903                     % (ttff_mode, criteria))
904        return False
905    ad.log.info("All TTFF %s are within test criteria %d seconds."
906                % (ttff_mode, criteria))
907    return True
908
909
910def ttff_property_key_and_value(ad, ttff_data, ttff_mode):
911    """Output ttff_data to test_run_info for ACTS plugin to parse and display
912    on MobileHarness as Property.
913
914    Args:
915        ad: An AndroidDevice object.
916        ttff_data: TTFF data of secs, position error and signal strength.
917        ttff_mode: TTFF Test mode for current test item.
918    """
919    prop_basename = "TestResult "+ttff_mode.replace(" ", "_")+"_TTFF_"
920    sec_list = [float(ttff_data[key].ttff_sec) for key in ttff_data.keys()]
921    pe_list = [float(ttff_data[key].ttff_pe) for key in ttff_data.keys()]
922    cn_list = [float(ttff_data[key].ttff_cn) for key in ttff_data.keys()]
923    timeoutcount = sec_list.count(0.0)
924    if len(sec_list) == timeoutcount:
925        avgttff = 9527
926    else:
927        avgttff = sum(sec_list)/(len(sec_list) - timeoutcount)
928    if timeoutcount != 0:
929        maxttff = 9527
930    else:
931        maxttff = max(sec_list)
932    avgdis = sum(pe_list)/len(pe_list)
933    maxdis = max(pe_list)
934    avgcn = sum(cn_list)/len(cn_list)
935    ad.log.info(prop_basename+"AvgTime %.1f" % avgttff)
936    ad.log.info(prop_basename+"MaxTime %.1f" % maxttff)
937    ad.log.info(prop_basename+"TimeoutCount %d" % timeoutcount)
938    ad.log.info(prop_basename+"AvgDis %.1f" % avgdis)
939    ad.log.info(prop_basename+"MaxDis %.1f" % maxdis)
940    ad.log.info(prop_basename+"AvgSignal %.1f" % avgcn)
941
942
943def calculate_position_error(ad, latitude, longitude, true_position):
944    """Use haversine formula to calculate position error base on true location
945    coordinate.
946
947    Args:
948        ad: An AndroidDevice object.
949        latitude: latitude of location fixed in the present.
950        longitude: longitude of location fixed in the present.
951        true_position: [latitude, longitude] of true location coordinate.
952
953    Returns:
954        position_error of location fixed in the present.
955    """
956    radius = 6371009
957    dlat = math.radians(latitude - true_position[0])
958    dlon = math.radians(longitude - true_position[1])
959    a = math.sin(dlat/2) * math.sin(dlat/2) + \
960        math.cos(math.radians(true_position[0])) * \
961        math.cos(math.radians(latitude)) * math.sin(dlon/2) * math.sin(dlon/2)
962    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
963    return radius * c
964
965
966def launch_google_map(ad):
967    """Launch Google Map via intent.
968
969    Args:
970        ad: An AndroidDevice object.
971    """
972    ad.log.info("Launch Google Map.")
973    try:
974        ad.adb.shell("am start -S -n com.google.android.apps.maps/"
975                     "com.google.android.maps.MapsActivity")
976        ad.send_keycode("BACK")
977        ad.force_stop_apk("com.google.android.apps.maps")
978        ad.adb.shell("am start -S -n com.google.android.apps.maps/"
979                     "com.google.android.maps.MapsActivity")
980    except Exception as e:
981        ad.log.error(e)
982        raise signals.TestError("Failed to launch google map.")
983    check_currrent_focus_app(ad)
984
985
986def check_currrent_focus_app(ad):
987    """Check to see current focused window and app.
988
989    Args:
990        ad: An AndroidDevice object.
991    """
992    time.sleep(1)
993    current = ad.adb.shell(
994        "dumpsys window | grep -E 'mCurrentFocus|mFocusedApp'")
995    ad.log.debug("\n"+current)
996
997
998def check_location_api(ad, retries):
999    """Verify if GnssLocationProvider API reports location.
1000
1001    Args:
1002        ad: An AndroidDevice object.
1003        retries: Retry time.
1004
1005    Returns:
1006        True: GnssLocationProvider API reports location.
1007        otherwise return False.
1008    """
1009    for i in range(retries):
1010        begin_time = get_current_epoch_time()
1011        ad.log.info("Try to get location report from GnssLocationProvider API "
1012                    "- attempt %d" % (i+1))
1013        while get_current_epoch_time() - begin_time <= 30000:
1014            logcat_results = ad.search_logcat("REPORT_LOCATION", begin_time)
1015            if logcat_results:
1016                ad.log.info("%s" % logcat_results[-1]["log_message"])
1017                ad.log.info("GnssLocationProvider reports location "
1018                            "successfully.")
1019                return True
1020        if not ad.is_adb_logcat_on:
1021            ad.start_adb_logcat()
1022    ad.log.error("GnssLocationProvider is unable to report location.")
1023    return False
1024
1025def check_network_location(ad, retries, location_type, criteria=30):
1026    """Verify if NLP reports location after requesting via GPSTool.
1027
1028    Args:
1029        ad: An AndroidDevice object.
1030        retries: Retry time.
1031        location_type: neworkLocationType of cell or wifi.
1032        criteria: expected nlp return time, default 30 seconds
1033
1034    Returns:
1035        True: NLP reports location.
1036        otherwise return False.
1037    """
1038    criteria = criteria * 1000
1039    for i in range(retries):
1040        time.sleep(1)
1041        begin_time = get_current_epoch_time()
1042        ad.log.info("Try to get NLP status - attempt %d" % (i+1))
1043        ad.adb.shell(
1044            "am start -S -n com.android.gpstool/.GPSTool --es mode nlp")
1045        while get_current_epoch_time() - begin_time <= criteria:
1046            logcat_results = ad.search_logcat("LocationManagerService: "
1047                                              "incoming location: Location",
1048                                              begin_time)
1049            if logcat_results:
1050                for logcat_result in logcat_results:
1051                    if location_type in logcat_result["log_message"]:
1052                        ad.log.info(logcat_result["log_message"])
1053                        ad.send_keycode("BACK")
1054                        return True
1055        if not ad.is_adb_logcat_on:
1056            ad.start_adb_logcat()
1057        ad.send_keycode("BACK")
1058    ad.log.error("Unable to report network location \"%s\"." % location_type)
1059    return False
1060
1061
1062def set_attenuator_gnss_signal(ad, attenuator, atten_value):
1063    """Set attenuation value for different GNSS signal.
1064
1065    Args:
1066        ad: An AndroidDevice object.
1067        attenuator: The attenuator object.
1068        atten_value: attenuation value
1069    """
1070    try:
1071        ad.log.info(
1072            "Set attenuation value to \"%d\" for GNSS signal." % atten_value)
1073        attenuator[0].set_atten(atten_value)
1074    except Exception as e:
1075        ad.log.error(e)
1076
1077
1078def set_battery_saver_mode(ad, state):
1079    """Enable or diable battery saver mode via adb.
1080
1081    Args:
1082        ad: An AndroidDevice object.
1083        state: True is enable Battery Saver mode. False is disable.
1084    """
1085    ad.root_adb()
1086    if state:
1087        ad.log.info("Enable Battery Saver mode.")
1088        ad.adb.shell("cmd battery unplug")
1089        ad.adb.shell("settings put global low_power 1")
1090    else:
1091        ad.log.info("Disable Battery Saver mode.")
1092        ad.adb.shell("settings put global low_power 0")
1093        ad.adb.shell("cmd battery reset")
1094
1095
1096def set_gnss_qxdm_mask(ad, masks):
1097    """Find defined gnss qxdm mask and set as default logging mask.
1098
1099    Args:
1100        ad: An AndroidDevice object.
1101        masks: Defined gnss qxdm mask.
1102    """
1103    try:
1104        for mask in masks:
1105            if not tutils.find_qxdm_log_mask(ad, mask):
1106                continue
1107            tutils.set_qxdm_logger_command(ad, mask)
1108            break
1109    except Exception as e:
1110        ad.log.error(e)
1111        raise signals.TestError("Failed to set any QXDM masks.")
1112
1113
1114def start_youtube_video(ad, url=None, retries=0):
1115    """Start youtube video and verify if audio is in music state.
1116
1117    Args:
1118        ad: An AndroidDevice object.
1119        url: Youtube video url.
1120        retries: Retry times if audio is not in music state.
1121
1122    Returns:
1123        True if youtube video is playing normally.
1124        False if youtube video is not playing properly.
1125    """
1126    for i in range(retries):
1127        ad.log.info("Open an youtube video - attempt %d" % (i+1))
1128        ad.adb.shell("am start -a android.intent.action.VIEW -d \"%s\"" % url)
1129        time.sleep(2)
1130        out = ad.adb.shell(
1131            "dumpsys activity | grep NewVersionAvailableActivity")
1132        if out:
1133            ad.log.info("Skip Youtube New Version Update.")
1134            ad.send_keycode("BACK")
1135        if tutils.wait_for_state(ad.droid.audioIsMusicActive, True, 15, 1):
1136            ad.log.info("Started a video in youtube, audio is in MUSIC state")
1137            return True
1138        ad.log.info("Force-Stop youtube and reopen youtube again.")
1139        ad.force_stop_apk("com.google.android.youtube")
1140    check_currrent_focus_app(ad)
1141    raise signals.TestError("Started a video in youtube, "
1142                            "but audio is not in MUSIC state")
1143
1144
1145def get_baseband_and_gms_version(ad, extra_msg=""):
1146    """Get current radio baseband and GMSCore version of AndroidDevice object.
1147
1148    Args:
1149        ad: An AndroidDevice object.
1150    """
1151    try:
1152        build_version = ad.adb.getprop("ro.build.id")
1153        baseband_version = ad.adb.getprop("gsm.version.baseband")
1154        gms_version = ad.adb.shell(
1155            "dumpsys package com.google.android.gms | grep versionName"
1156        ).split("\n")[0].split("=")[1]
1157        mpss_version = ad.adb.shell("cat /sys/devices/soc0/images | grep MPSS "
1158                                    "| cut -d ':' -f 3")
1159        if not extra_msg:
1160            ad.log.info("TestResult Build_Version %s" % build_version)
1161            ad.log.info("TestResult Baseband_Version %s" % baseband_version)
1162            ad.log.info(
1163                "TestResult GMS_Version %s" % gms_version.replace(" ", ""))
1164            ad.log.info("TestResult MPSS_Version %s" % mpss_version)
1165        else:
1166            ad.log.info(
1167                "%s, Baseband_Version = %s" % (extra_msg, baseband_version))
1168    except Exception as e:
1169        ad.log.error(e)
1170
1171
1172def start_toggle_gnss_by_gtw_gpstool(ad, iteration):
1173    """Send toggle gnss off/on start_test_action
1174
1175    Args:
1176        ad: An AndroidDevice object.
1177        iteration: Iteration of toggle gnss off/on cycles.
1178    """
1179    msg_list = []
1180    begin_time = get_current_epoch_time()
1181    try:
1182        for i in range(1, 4):
1183            ad.adb.shell("am start -S -n com.android.gpstool/.GPSTool "
1184                         "--es mode toggle --es cycle %d" % iteration)
1185            time.sleep(1)
1186            if ad.search_logcat("cmp=com.android.gpstool/.ToggleGPS",
1187                                begin_time):
1188                ad.log.info("Send ToggleGPS start_test_action successfully.")
1189                break
1190        else:
1191            check_currrent_focus_app(ad)
1192            raise signals.TestError("Fail to send ToggleGPS "
1193                                    "start_test_action within 3 attempts.")
1194        time.sleep(2)
1195        test_start = ad.search_logcat("GPSTool_ToggleGPS: startService",
1196                                      begin_time)
1197        if test_start:
1198            ad.log.info(test_start[-1]["log_message"].split(":")[-1].strip())
1199        else:
1200            raise signals.TestError("Fail to start toggle GPS off/on test.")
1201        # Every iteration is expected to finish within 4 minutes.
1202        while get_current_epoch_time() - begin_time <= iteration * 240000:
1203            crash_end = ad.search_logcat("Force finishing activity "
1204                                         "com.android.gpstool/.GPSTool",
1205                                         begin_time)
1206            if crash_end:
1207                raise signals.TestError("GPSTool crashed. Abort test.")
1208            toggle_results = ad.search_logcat("GPSTool : msg", begin_time)
1209            if toggle_results:
1210                for toggle_result in toggle_results:
1211                    msg = toggle_result["log_message"]
1212                    if not msg in msg_list:
1213                        ad.log.info(msg.split(":")[-1].strip())
1214                        msg_list.append(msg)
1215                    if "timeout" in msg:
1216                        raise signals.TestFailure("Fail to get location fixed "
1217                                                  "within 60 seconds.")
1218                    if "Test end" in msg:
1219                        raise signals.TestPass("Completed quick toggle GNSS "
1220                                               "off/on test.")
1221        raise signals.TestFailure("Fail to finish toggle GPS off/on test "
1222                                  "within %d minutes" % (iteration * 4))
1223    finally:
1224        ad.send_keycode("HOME")
1225