• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2#
3#   Copyright 2020 - 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
24import tempfile
25from collections import namedtuple
26
27from acts import utils
28from acts import asserts
29from acts import signals
30from acts.libs.proc import job
31from acts.controllers.android_device import list_adb_devices
32from acts.controllers.android_device import list_fastboot_devices
33from acts.controllers.android_device import DEFAULT_QXDM_LOG_PATH
34from acts.controllers.android_device import SL4A_APK_NAME
35from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
36from acts_contrib.test_utils.tel import tel_test_utils as tutils
37from acts_contrib.test_utils.instrumentation.device.command.instrumentation_command_builder import InstrumentationCommandBuilder
38from acts_contrib.test_utils.instrumentation.device.command.instrumentation_command_builder import InstrumentationTestCommandBuilder
39from acts.utils import get_current_epoch_time
40from acts.utils import epoch_to_human_time
41
42WifiEnums = wutils.WifiEnums
43PULL_TIMEOUT = 300
44GNSSSTATUS_LOG_PATH = (
45    "/storage/emulated/0/Android/data/com.android.gpstool/files/")
46QXDM_MASKS = ["GPS.cfg", "GPS-general.cfg", "default.cfg"]
47TTFF_REPORT = namedtuple(
48    "TTFF_REPORT", "utc_time ttff_loop ttff_sec ttff_pe ttff_ant_cn "
49                   "ttff_base_cn")
50TRACK_REPORT = namedtuple(
51    "TRACK_REPORT", "l5flag pe ant_top4cn ant_cn base_top4cn base_cn")
52LOCAL_PROP_FILE_CONTENTS = """\
53log.tag.LocationManagerService=VERBOSE
54log.tag.GnssLocationProvider=VERBOSE
55log.tag.GnssMeasurementsProvider=VERBOSE
56log.tag.GpsNetInitiatedHandler=VERBOSE
57log.tag.GnssNetInitiatedHandler=VERBOSE
58log.tag.GnssNetworkConnectivityHandler=VERBOSE
59log.tag.ConnectivityService=VERBOSE
60log.tag.ConnectivityManager=VERBOSE
61log.tag.GnssVisibilityControl=VERBOSE
62log.tag.NtpTimeHelper=VERBOSE
63log.tag.NtpTrustedTime=VERBOSE
64log.tag.GnssPsdsDownloader=VERBOSE
65log.tag.Gnss=VERBOSE
66log.tag.GnssConfiguration=VERBOSE"""
67TEST_PACKAGE_NAME = "com.google.android.apps.maps"
68LOCATION_PERMISSIONS = [
69    "android.permission.ACCESS_FINE_LOCATION",
70    "android.permission.ACCESS_COARSE_LOCATION"
71]
72GNSSTOOL_PACKAGE_NAME = "com.android.gpstool"
73GNSSTOOL_PERMISSIONS = [
74    "android.permission.ACCESS_FINE_LOCATION",
75    "android.permission.READ_EXTERNAL_STORAGE",
76    "android.permission.ACCESS_COARSE_LOCATION",
77    "android.permission.CALL_PHONE",
78    "android.permission.WRITE_CONTACTS",
79    "android.permission.CAMERA",
80    "android.permission.WRITE_EXTERNAL_STORAGE",
81    "android.permission.READ_CONTACTS",
82    "android.permission.ACCESS_BACKGROUND_LOCATION"
83]
84DISABLE_LTO_FILE_CONTENTS = """\
85LONGTERM_PSDS_SERVER_1="http://"
86LONGTERM_PSDS_SERVER_2="http://"
87LONGTERM_PSDS_SERVER_3="http://"
88NORMAL_PSDS_SERVER="http://"
89REALTIME_PSDS_SERVER="http://"
90"""
91
92
93class GnssTestUtilsError(Exception):
94    pass
95
96
97def remount_device(ad):
98    """Remount device file system to read and write.
99
100    Args:
101        ad: An AndroidDevice object.
102    """
103    for retries in range(5):
104        ad.root_adb()
105        if ad.adb.getprop("ro.boot.veritymode") == "enforcing":
106            ad.adb.disable_verity()
107            reboot(ad)
108        remount_result = ad.adb.remount()
109        ad.log.info("Attempt %d - %s" % (retries + 1, remount_result))
110        if "remount succeeded" in remount_result:
111            break
112
113
114def reboot(ad):
115    """Reboot device and check if mobile data is available.
116
117    Args:
118        ad: An AndroidDevice object.
119    """
120    ad.log.info("Reboot device to make changes take effect.")
121    ad.reboot()
122    ad.unlock_screen(password=None)
123    if not int(ad.adb.shell("settings get global mobile_data")) == 1:
124        set_mobile_data(ad, True)
125    utils.sync_device_time(ad)
126
127
128def enable_gnss_verbose_logging(ad):
129    """Enable GNSS VERBOSE Logging and persistent logcat.
130
131    Args:
132        ad: An AndroidDevice object.
133    """
134    remount_device(ad)
135    ad.log.info("Enable GNSS VERBOSE Logging and persistent logcat.")
136    if check_chipset_vendor_by_qualcomm(ad):
137        ad.adb.shell("echo -e '\nDEBUG_LEVEL = 5' >> /vendor/etc/gps.conf")
138    else:
139        ad.adb.shell("echo LogEnabled=true >> /data/vendor/gps/libgps.conf")
140        ad.adb.shell("chown gps.system /data/vendor/gps/libgps.conf")
141    ad.adb.shell("echo %r >> /data/local.prop" % LOCAL_PROP_FILE_CONTENTS)
142    ad.adb.shell("chmod 644 /data/local.prop")
143    ad.adb.shell("setprop persist.logd.logpersistd.size 20000")
144    ad.adb.shell("setprop persist.logd.size 16777216")
145    ad.adb.shell("setprop persist.vendor.radio.adb_log_on 1")
146    ad.adb.shell("setprop persist.logd.logpersistd logcatd")
147    ad.adb.shell("setprop log.tag.copresGcore VERBOSE")
148    ad.adb.shell("sync")
149
150
151def get_am_flags(value):
152    """Returns the (value, type) flags for a given python value."""
153    if type(value) is bool:
154        return str(value).lower(), 'boolean'
155    elif type(value) is str:
156        return value, 'string'
157    raise ValueError("%s should be either 'boolean' or 'string'" % value)
158
159
160def enable_compact_and_particle_fusion_log(ad):
161    """Enable CompactLog, FLP particle fusion log and disable gms
162    location-based quake monitoring.
163
164    Args:
165        ad: An AndroidDevice object.
166    """
167    ad.root_adb()
168    ad.log.info("Enable FLP flags and Disable GMS location-based quake "
169                "monitoring.")
170    overrides = {
171        'compact_log_enabled': True,
172        'flp_use_particle_fusion': True,
173        'flp_particle_fusion_extended_bug_report': True,
174        'flp_event_log_size': '86400',
175        'proks_config': '28',
176        'flp_particle_fusion_bug_report_window_sec': '86400',
177        'flp_particle_fusion_bug_report_max_buffer_size': '86400',
178        'seismic_data_collection': False,
179        'Ealert__enable': False,
180    }
181    for flag, python_value in overrides.items():
182        value, type = get_am_flags(python_value)
183        cmd = ("am broadcast -a com.google.android.gms.phenotype.FLAG_OVERRIDE "
184               "--es package com.google.android.location --es user \* "
185               "--esa flags %s --esa values %s --esa types %s "
186               "com.google.android.gms" % (flag, value, type))
187        ad.adb.shell(cmd)
188    ad.adb.shell("am force-stop com.google.android.gms")
189    ad.adb.shell("am broadcast -a com.google.android.gms.INITIALIZE")
190
191
192def disable_xtra_throttle(ad):
193    """Disable XTRA throttle will have no limit to download XTRA data.
194
195    Args:
196        ad: An AndroidDevice object.
197    """
198    remount_device(ad)
199    ad.log.info("Disable XTRA Throttle.")
200    ad.adb.shell("echo -e '\nXTRA_TEST_ENABLED=1' >> /vendor/etc/gps.conf")
201    ad.adb.shell("echo -e '\nXTRA_THROTTLE_ENABLED=0' >> /vendor/etc/gps.conf")
202
203
204def enable_supl_mode(ad):
205    """Enable SUPL back on for next test item.
206
207    Args:
208        ad: An AndroidDevice object.
209    """
210    remount_device(ad)
211    ad.log.info("Enable SUPL mode.")
212    ad.adb.shell("echo -e '\nSUPL_MODE=1' >> /etc/gps_debug.conf")
213    if not check_chipset_vendor_by_qualcomm(ad):
214        lto_mode(ad, True)
215    else:
216        reboot(ad)
217
218
219def disable_supl_mode(ad):
220    """Kill SUPL to test XTRA/LTO only test item.
221
222    Args:
223        ad: An AndroidDevice object.
224    """
225    remount_device(ad)
226    ad.log.info("Disable SUPL mode.")
227    ad.adb.shell("echo -e '\nSUPL_MODE=0' >> /etc/gps_debug.conf")
228    if not check_chipset_vendor_by_qualcomm(ad):
229        lto_mode(ad, True)
230    else:
231        reboot(ad)
232
233
234def kill_xtra_daemon(ad):
235    """Kill XTRA daemon to test SUPL only test item.
236
237    Args:
238        ad: An AndroidDevice object.
239    """
240    ad.root_adb()
241    if check_chipset_vendor_by_qualcomm(ad):
242        ad.log.info("Disable XTRA-daemon until next reboot.")
243        ad.adb.shell("killall xtra-daemon", ignore_status=True)
244    else:
245        lto_mode(ad, False)
246
247
248def disable_private_dns_mode(ad):
249    """Due to b/118365122, it's better to disable private DNS mode while
250       testing. 8.8.8.8 private dns sever is unstable now, sometimes server
251       will not response dns query suddenly.
252
253    Args:
254        ad: An AndroidDevice object.
255    """
256    tutils.get_operator_name(ad.log, ad, subId=None)
257    if ad.adb.shell("settings get global private_dns_mode") != "off":
258        ad.log.info("Disable Private DNS mode.")
259        ad.adb.shell("settings put global private_dns_mode off")
260
261
262def _init_device(ad):
263    """Init GNSS test devices.
264
265    Args:
266        ad: An AndroidDevice object.
267    """
268    enable_gnss_verbose_logging(ad)
269    enable_compact_and_particle_fusion_log(ad)
270    if check_chipset_vendor_by_qualcomm(ad):
271        disable_xtra_throttle(ad)
272    enable_supl_mode(ad)
273    ad.adb.shell("settings put system screen_off_timeout 1800000")
274    wutils.wifi_toggle_state(ad, False)
275    ad.log.info("Setting Bluetooth state to False")
276    ad.droid.bluetoothToggleState(False)
277    check_location_service(ad)
278    set_wifi_and_bt_scanning(ad, True)
279    disable_private_dns_mode(ad)
280    reboot(ad)
281    init_gtw_gpstool(ad)
282
283
284def connect_to_wifi_network(ad, network):
285    """Connection logic for open and psk wifi networks.
286
287    Args:
288        ad: An AndroidDevice object.
289        network: Dictionary with network info.
290    """
291    SSID = network[WifiEnums.SSID_KEY]
292    ad.ed.clear_all_events()
293    wutils.reset_wifi(ad)
294    wutils.start_wifi_connection_scan_and_ensure_network_found(ad, SSID)
295    wutils.wifi_connect(ad, network, num_of_tries=5)
296
297
298def set_wifi_and_bt_scanning(ad, state=True):
299    """Set Wi-Fi and Bluetooth scanning on/off in Settings -> Location
300
301    Args:
302        ad: An AndroidDevice object.
303        state: True to turn on "Wi-Fi and Bluetooth scanning".
304            False to turn off "Wi-Fi and Bluetooth scanning".
305    """
306    ad.root_adb()
307    if state:
308        ad.adb.shell("settings put global wifi_scan_always_enabled 1")
309        ad.adb.shell("settings put global ble_scan_always_enabled 1")
310        ad.log.info("Wi-Fi and Bluetooth scanning are enabled")
311    else:
312        ad.adb.shell("settings put global wifi_scan_always_enabled 0")
313        ad.adb.shell("settings put global ble_scan_always_enabled 0")
314        ad.log.info("Wi-Fi and Bluetooth scanning are disabled")
315
316
317def check_location_service(ad):
318    """Set location service on.
319       Verify if location service is available.
320
321    Args:
322        ad: An AndroidDevice object.
323    """
324    remount_device(ad)
325    utils.set_location_service(ad, True)
326    location_mode = int(ad.adb.shell("settings get secure location_mode"))
327    ad.log.info("Current Location Mode >> %d" % location_mode)
328    if location_mode != 3:
329        raise signals.TestError("Failed to turn Location on")
330
331
332def clear_logd_gnss_qxdm_log(ad):
333    """Clear /data/misc/logd,
334    /storage/emulated/0/Android/data/com.android.gpstool/files and
335    /data/vendor/radio/diag_logs/logs from previous test item then reboot.
336
337    Args:
338        ad: An AndroidDevice object.
339    """
340    remount_device(ad)
341    ad.log.info("Clear Logd, GNSS and PixelLogger Log from previous test item.")
342    ad.adb.shell("rm -rf /data/misc/logd", ignore_status=True)
343    ad.adb.shell(
344        'find %s -name "*.txt" -type f -delete' % GNSSSTATUS_LOG_PATH,
345        ignore_status=True)
346    if check_chipset_vendor_by_qualcomm(ad):
347        diag_logs = (
348            "/sdcard/Android/data/com.android.pixellogger/files/logs/diag_logs")
349        ad.adb.shell("rm -rf %s" % diag_logs, ignore_status=True)
350        output_path = posixpath.join(DEFAULT_QXDM_LOG_PATH, "logs")
351    else:
352        output_path = ("/sdcard/Android/data/com.android.pixellogger/files"
353                       "/logs/gps/")
354    ad.adb.shell("rm -rf %s" % output_path, ignore_status=True)
355    reboot(ad)
356
357
358def get_gnss_qxdm_log(ad, qdb_path):
359    """Get /storage/emulated/0/Android/data/com.android.gpstool/files and
360    /data/vendor/radio/diag_logs/logs for test item.
361
362    Args:
363        ad: An AndroidDevice object.
364        qdb_path: The path of qdsp6m.qdb on different projects.
365    """
366    log_path = ad.device_log_path
367    os.makedirs(log_path, exist_ok=True)
368    gnss_log_name = "gnssstatus_log_%s_%s" % (ad.model, ad.serial)
369    gnss_log_path = posixpath.join(log_path, gnss_log_name)
370    os.makedirs(gnss_log_path, exist_ok=True)
371    ad.log.info("Pull GnssStatus Log to %s" % gnss_log_path)
372    ad.adb.pull("%s %s" % (GNSSSTATUS_LOG_PATH+".", gnss_log_path),
373                timeout=PULL_TIMEOUT, ignore_status=True)
374    shutil.make_archive(gnss_log_path, "zip", gnss_log_path)
375    shutil.rmtree(gnss_log_path)
376    if check_chipset_vendor_by_qualcomm(ad):
377        output_path = (
378            "/sdcard/Android/data/com.android.pixellogger/files/logs/diag_logs")
379    else:
380        output_path = (
381            "/sdcard/Android/data/com.android.pixellogger/files/logs/gps/")
382    qxdm_log_name = "PixelLogger_%s_%s" % (ad.model, ad.serial)
383    qxdm_log_path = posixpath.join(log_path, qxdm_log_name)
384    os.makedirs(qxdm_log_path, exist_ok=True)
385    ad.log.info("Pull PixelLogger Log %s to %s" % (output_path,
386                                                   qxdm_log_path))
387    ad.adb.pull("%s %s" % (output_path, qxdm_log_path),
388                timeout=PULL_TIMEOUT, ignore_status=True)
389    if check_chipset_vendor_by_qualcomm(ad):
390        for path in qdb_path:
391            output = ad.adb.pull("%s %s" % (path, qxdm_log_path),
392                                 timeout=PULL_TIMEOUT, ignore_status=True)
393            if "No such file or directory" in output:
394                continue
395            break
396    shutil.make_archive(qxdm_log_path, "zip", qxdm_log_path)
397    shutil.rmtree(qxdm_log_path)
398
399
400def set_mobile_data(ad, state):
401    """Set mobile data on or off and check mobile data state.
402
403    Args:
404        ad: An AndroidDevice object.
405        state: True to enable mobile data. False to disable mobile data.
406    """
407    ad.root_adb()
408    if state:
409        ad.log.info("Enable mobile data.")
410        ad.adb.shell("svc data enable")
411    else:
412        ad.log.info("Disable mobile data.")
413        ad.adb.shell("svc data disable")
414    time.sleep(5)
415    out = int(ad.adb.shell("settings get global mobile_data"))
416    if state and out == 1:
417        ad.log.info("Mobile data is enabled and set to %d" % out)
418    elif not state and out == 0:
419        ad.log.info("Mobile data is disabled and set to %d" % out)
420    else:
421        ad.log.error("Mobile data is at unknown state and set to %d" % out)
422
423
424def gnss_trigger_modem_ssr_by_adb(ad, dwelltime=60):
425    """Trigger modem SSR crash by adb and verify if modem crash and recover
426    successfully.
427
428    Args:
429        ad: An AndroidDevice object.
430        dwelltime: Waiting time for modem reset. Default is 60 seconds.
431
432    Returns:
433        True if success.
434        False if failed.
435    """
436    begin_time = get_current_epoch_time()
437    ad.root_adb()
438    cmds = ("echo restart > /sys/kernel/debug/msm_subsys/modem",
439            r"echo 'at+cfun=1,1\r' > /dev/at_mdm0")
440    for cmd in cmds:
441        ad.log.info("Triggering modem SSR crash by %s" % cmd)
442        output = ad.adb.shell(cmd, ignore_status=True)
443        if "No such file or directory" in output:
444            continue
445        break
446    time.sleep(dwelltime)
447    ad.send_keycode("HOME")
448    logcat_results = ad.search_logcat("SSRObserver", begin_time)
449    if logcat_results:
450        for ssr in logcat_results:
451            if "mSubsystem='modem', mCrashReason" in ssr["log_message"]:
452                ad.log.debug(ssr["log_message"])
453                ad.log.info("Triggering modem SSR crash successfully.")
454                return True
455        raise signals.TestError("Failed to trigger modem SSR crash")
456    raise signals.TestError("No SSRObserver found in logcat")
457
458
459def gnss_trigger_modem_ssr_by_mds(ad, dwelltime=60):
460    """Trigger modem SSR crash by mds tool and verify if modem crash and recover
461    successfully.
462
463    Args:
464        ad: An AndroidDevice object.
465        dwelltime: Waiting time for modem reset. Default is 60 seconds.
466    """
467    mds_check = ad.adb.shell("pm path com.google.mdstest")
468    if not mds_check:
469        raise signals.TestError("MDS Tool is not properly installed.")
470    ad.root_adb()
471    cmd = ('am instrument -w -e request "4b 25 03 00" '
472           '"com.google.mdstest/com.google.mdstest.instrument'
473           '.ModemCommandInstrumentation"')
474    ad.log.info("Triggering modem SSR crash by MDS")
475    output = ad.adb.shell(cmd, ignore_status=True)
476    ad.log.debug(output)
477    time.sleep(dwelltime)
478    ad.send_keycode("HOME")
479    if "SUCCESS" in output:
480        ad.log.info("Triggering modem SSR crash by MDS successfully.")
481    else:
482        raise signals.TestError(
483            "Failed to trigger modem SSR crash by MDS. \n%s" % output)
484
485
486def check_xtra_download(ad, begin_time):
487    """Verify XTRA download success log message in logcat.
488
489    Args:
490        ad: An AndroidDevice object.
491        begin_time: test begin time
492
493    Returns:
494        True: xtra_download if XTRA downloaded and injected successfully
495        otherwise return False.
496    """
497    ad.send_keycode("HOME")
498    if check_chipset_vendor_by_qualcomm(ad):
499        xtra_results = ad.search_logcat("XTRA download success. "
500                                        "inject data into modem", begin_time)
501        if xtra_results:
502            ad.log.debug("%s" % xtra_results[-1]["log_message"])
503            ad.log.info("XTRA downloaded and injected successfully.")
504            return True
505        ad.log.error("XTRA downloaded FAIL.")
506    else:
507        lto_results = ad.search_logcat("GnssPsdsAidl: injectPsdsData: "
508                                       "psdsType: 1", begin_time)
509        if lto_results:
510            ad.log.debug("%s" % lto_results[-1]["log_message"])
511            ad.log.info("LTO downloaded and injected successfully.")
512            return True
513        ad.log.error("LTO downloaded and inject FAIL.")
514    return False
515
516
517def pull_package_apk(ad, package_name):
518    """Pull apk of given package_name from device.
519
520    Args:
521        ad: An AndroidDevice object.
522        package_name: Package name of apk to pull.
523
524    Returns:
525        The temp path of pulled apk.
526    """
527    apk_path = None
528    out = ad.adb.shell("pm path %s" % package_name)
529    result = re.search(r"package:(.*)", out)
530    if not result:
531        tutils.abort_all_tests(ad.log, "Couldn't find apk of %s" % package_name)
532    else:
533        apk_source = result.group(1)
534        ad.log.info("Get apk of %s from %s" % (package_name, apk_source))
535        apk_path = tempfile.mkdtemp()
536        ad.pull_files([apk_source], apk_path)
537    return apk_path
538
539
540def pull_gnss_cfg_file(ad, file):
541    """Pull given gnss cfg file from device.
542
543    Args:
544        ad: An AndroidDevice object.
545        file: CFG file in device to pull.
546
547    Returns:
548        The temp path of pulled gnss cfg file in host.
549    """
550    ad.root_adb()
551    host_dest = tempfile.mkdtemp()
552    ad.pull_files(file, host_dest)
553    for path_key in os.listdir(host_dest):
554        if fnmatch.fnmatch(path_key, "*.cfg"):
555            gnss_cfg_file = os.path.join(host_dest, path_key)
556            break
557    else:
558        raise signals.TestError("No cfg file is found in %s" % host_dest)
559    return gnss_cfg_file
560
561
562def reinstall_package_apk(ad, package_name, apk_path):
563    """Reinstall apk of given package_name.
564
565    Args:
566        ad: An AndroidDevice object.
567        package_name: Package name of apk.
568        apk_path: The temp path of pulled apk.
569    """
570    for path_key in os.listdir(apk_path):
571        if fnmatch.fnmatch(path_key, "*.apk"):
572            apk_path = os.path.join(apk_path, path_key)
573            break
574    else:
575        raise signals.TestError("No apk is found in %s" % apk_path)
576    ad.log.info("Re-install %s with path: %s" % (package_name, apk_path))
577    ad.adb.shell("settings put global verifier_verify_adb_installs 0")
578    ad.adb.install("-r -d -g --user 0 %s" % apk_path)
579    package_check = ad.adb.shell("pm path %s" % package_name)
580    if not package_check:
581        tutils.abort_all_tests(
582            ad.log, "%s is not properly re-installed." % package_name)
583    ad.log.info("%s is re-installed successfully." % package_name)
584
585
586def init_gtw_gpstool(ad):
587    """Init GTW_GPSTool apk.
588
589    Args:
590        ad: An AndroidDevice object.
591    """
592    remount_device(ad)
593    gpstool_path = pull_package_apk(ad, "com.android.gpstool")
594    reinstall_package_apk(ad, "com.android.gpstool", gpstool_path)
595
596
597def fastboot_factory_reset(ad):
598    """Factory reset the device in fastboot mode.
599       Pull sl4a apk from device. Terminate all sl4a sessions,
600       Reboot the device to bootloader,
601       factory reset the device by fastboot.
602       Reboot the device. wait for device to complete booting
603       Re-install and start an sl4a session.
604
605    Args:
606        ad: An AndroidDevice object.
607
608    Returns:
609        True if factory reset process complete.
610    """
611    status = True
612    skip_setup_wizard = True
613    gnss_cfg_path = "/vendor/etc/mdlog"
614    default_gnss_cfg = "/vendor/etc/mdlog/DEFAULT+SECURITY+FULLDPL+GPS.cfg"
615    sl4a_path = pull_package_apk(ad, SL4A_APK_NAME)
616    gpstool_path = pull_package_apk(ad, "com.android.gpstool")
617    mds_path = pull_package_apk(ad, "com.google.mdstest")
618    if check_chipset_vendor_by_qualcomm(ad):
619        gnss_cfg_file = pull_gnss_cfg_file(ad, default_gnss_cfg)
620    stop_pixel_logger(ad)
621    ad.stop_services()
622    attempts = 3
623    for i in range(1, attempts + 1):
624        try:
625            if ad.serial in list_adb_devices():
626                ad.log.info("Reboot to bootloader")
627                ad.adb.reboot("bootloader", ignore_status=True)
628                time.sleep(10)
629            if ad.serial in list_fastboot_devices():
630                ad.log.info("Factory reset in fastboot")
631                ad.fastboot._w(timeout=300, ignore_status=True)
632                time.sleep(30)
633                ad.log.info("Reboot in fastboot")
634                ad.fastboot.reboot()
635            ad.wait_for_boot_completion()
636            ad.root_adb()
637            if ad.skip_sl4a:
638                break
639            if ad.is_sl4a_installed():
640                break
641            reinstall_package_apk(ad, SL4A_APK_NAME, sl4a_path)
642            reinstall_package_apk(ad, "com.android.gpstool", gpstool_path)
643            reinstall_package_apk(ad, "com.google.mdstest", mds_path)
644            if check_chipset_vendor_by_qualcomm(ad):
645                ad.push_system_file(gnss_cfg_file, gnss_cfg_path)
646            time.sleep(10)
647            break
648        except Exception as e:
649            ad.log.error(e)
650            if i == attempts:
651                tutils.abort_all_tests(ad.log, str(e))
652            time.sleep(5)
653    try:
654        ad.start_adb_logcat()
655    except Exception as e:
656        ad.log.error(e)
657    if skip_setup_wizard:
658        ad.exit_setup_wizard()
659    if ad.skip_sl4a:
660        return status
661    tutils.bring_up_sl4a(ad)
662    return status
663
664
665def clear_aiding_data_by_gtw_gpstool(ad):
666    """Launch GTW GPSTool and Clear all GNSS aiding data.
667       Wait 5 seconds for GTW GPStool to clear all GNSS aiding
668       data properly.
669
670    Args:
671        ad: An AndroidDevice object.
672    """
673    if not check_chipset_vendor_by_qualcomm(ad):
674        delete_lto_file(ad)
675    ad.log.info("Launch GTW GPSTool and Clear all GNSS aiding data")
676    ad.adb.shell("am start -S -n com.android.gpstool/.GPSTool --es mode clear")
677    time.sleep(10)
678
679
680def start_gnss_by_gtw_gpstool(ad,
681                              state,
682                              type="gnss",
683                              bgdisplay=False,
684                              freq=0,
685                              lowpower=False,
686                              meas=False):
687    """Start or stop GNSS on GTW_GPSTool.
688
689    Args:
690        ad: An AndroidDevice object.
691        state: True to start GNSS. False to Stop GNSS.
692        type: Different API for location fix. Use gnss/flp/nmea
693        bgdisplay: true to run GTW when Display off. false to not run GTW when
694          Display off.
695        freq: An integer to set location update frequency.
696        meas: A Boolean to set GNSS measurement registration.
697        lowpower: A boolean to set GNSS LowPowerMode.
698    """
699    cmd = "am start -S -n com.android.gpstool/.GPSTool --es mode gps"
700    if not state:
701        ad.log.info("Stop %s on GTW_GPSTool." % type)
702        cmd = "am broadcast -a com.android.gpstool.stop_gps_action"
703    else:
704        options = ("--es type {} --ei freq {} --ez BG {} --ez meas {} --ez "
705                   "lowpower {}").format(type, freq, bgdisplay, meas, lowpower)
706        cmd = cmd + " " + options
707    ad.adb.shell(cmd)
708    time.sleep(3)
709
710
711def process_gnss_by_gtw_gpstool(ad,
712                                criteria,
713                                type="gnss",
714                                clear_data=True,
715                                meas_flag=False):
716    """Launch GTW GPSTool and Clear all GNSS aiding data
717       Start GNSS tracking on GTW_GPSTool.
718
719    Args:
720        ad: An AndroidDevice object.
721        criteria: Criteria for current test item.
722        type: Different API for location fix. Use gnss/flp/nmea
723        clear_data: True to clear GNSS aiding data. False is not to. Default
724        set to True.
725        meas_flag: True to enable GnssMeasurement. False is not to. Default
726        set to False.
727
728    Returns:
729        True: First fix TTFF are within criteria.
730        False: First fix TTFF exceed criteria.
731    """
732    retries = 3
733    for i in range(retries):
734        if not ad.is_adb_logcat_on:
735            ad.start_adb_logcat()
736        check_adblog_functionality(ad)
737        check_location_runtime_permissions(
738            ad, GNSSTOOL_PACKAGE_NAME, GNSSTOOL_PERMISSIONS)
739        begin_time = get_current_epoch_time()
740        if clear_data:
741            clear_aiding_data_by_gtw_gpstool(ad)
742        ad.log.info("Start %s on GTW_GPSTool - attempt %d" % (type.upper(),
743                                                              i+1))
744        start_gnss_by_gtw_gpstool(ad, state=True, type=type, meas=meas_flag)
745        for _ in range(10 + criteria):
746            logcat_results = ad.search_logcat("First fixed", begin_time)
747            if logcat_results:
748                ad.log.debug(logcat_results[-1]["log_message"])
749                first_fixed = int(logcat_results[-1]["log_message"].split()[-1])
750                ad.log.info("%s First fixed = %.3f seconds" %
751                            (type.upper(), first_fixed/1000))
752                if (first_fixed/1000) <= criteria:
753                    return True
754                start_gnss_by_gtw_gpstool(ad, state=False, type=type)
755                raise signals.TestFailure("Fail to get %s location fixed "
756                                          "within %d seconds criteria."
757                                          % (type.upper(), criteria))
758            time.sleep(1)
759        check_current_focus_app(ad)
760        start_gnss_by_gtw_gpstool(ad, state=False, type=type)
761    raise signals.TestFailure("Fail to get %s location fixed within %d "
762                              "attempts." % (type.upper(), retries))
763
764def start_ttff_by_gtw_gpstool(ad, ttff_mode, iteration, aid_data=False):
765    """Identify which TTFF mode for different test items.
766
767    Args:
768        ad: An AndroidDevice object.
769        ttff_mode: TTFF Test mode for current test item.
770        iteration: Iteration of TTFF cycles.
771        aid_data: Boolean for identify aid_data existed or not
772    """
773    begin_time = get_current_epoch_time()
774    if (ttff_mode == "hs" or ttff_mode == "ws") and not aid_data:
775        ad.log.info("Wait 5 minutes to start TTFF %s..." % ttff_mode.upper())
776        time.sleep(300)
777    if ttff_mode == "cs":
778        ad.log.info("Start TTFF Cold Start...")
779        time.sleep(3)
780    for i in range(1, 4):
781        ad.adb.shell("am broadcast -a com.android.gpstool.ttff_action "
782                     "--es ttff %s --es cycle %d" % (ttff_mode, iteration))
783        time.sleep(1)
784        if ad.search_logcat("act=com.android.gpstool.start_test_action",
785                            begin_time):
786            ad.log.info("Send TTFF start_test_action successfully.")
787            break
788    else:
789        check_current_focus_app(ad)
790        raise signals.TestError("Fail to send TTFF start_test_action.")
791
792
793def gnss_tracking_via_gtw_gpstool(ad,
794                                  criteria,
795                                  type="gnss",
796                                  testtime=60,
797                                  meas_flag=False):
798    """Start GNSS/FLP tracking tests for input testtime on GTW_GPSTool.
799
800    Args:
801        ad: An AndroidDevice object.
802        criteria: Criteria for current TTFF.
803        type: Different API for location fix. Use gnss/flp/nmea
804        testtime: Tracking test time for minutes. Default set to 60 minutes.
805        meas_flag: True to enable GnssMeasurement. False is not to. Default
806        set to False.
807    """
808    gnss_crash_list = [".*Fatal signal.*gnss",
809                       ".*Fatal signal.*xtra",
810                       ".*F DEBUG.*gnss"]
811    process_gnss_by_gtw_gpstool(
812        ad, criteria=criteria, type=type, meas_flag=meas_flag)
813    ad.log.info("Start %s tracking test for %d minutes" % (type.upper(),
814                                                           testtime))
815    begin_time = get_current_epoch_time()
816    while get_current_epoch_time() - begin_time < testtime * 60 * 1000:
817        if not ad.is_adb_logcat_on:
818            ad.start_adb_logcat()
819        for attr in gnss_crash_list:
820            gnss_crash_result = ad.adb.shell(
821                "logcat -d | grep -E -i '%s'" % attr)
822            if gnss_crash_result:
823                start_gnss_by_gtw_gpstool(ad, state=False, type=type)
824                raise signals.TestFailure(
825                    "Test failed due to GNSS HAL crashed. \n%s" %
826                    gnss_crash_result)
827        gpstool_crash_result = ad.search_logcat("Force finishing activity "
828                                                "com.android.gpstool/.GPSTool",
829                                                begin_time)
830        if gpstool_crash_result:
831            raise signals.TestError("GPSTool crashed. Abort test.")
832    ad.log.info("Successfully tested for %d minutes" % testtime)
833    start_gnss_by_gtw_gpstool(ad, state=False, type=type)
834
835
836def parse_gtw_gpstool_log(ad, true_position, type="gnss"):
837    """Process GNSS/FLP API logs from GTW GPSTool and output track_data to
838    test_run_info for ACTS plugin to parse and display on MobileHarness as
839    Property.
840
841    Args:
842        ad: An AndroidDevice object.
843        true_position: Coordinate as [latitude, longitude] to calculate
844        position error.
845        type: Different API for location fix. Use gnss/flp/nmea
846    """
847    test_logfile = {}
848    track_data = {}
849    ant_top4_cn = 0
850    ant_cn = 0
851    base_top4_cn = 0
852    base_cn = 0
853    track_lat = 0
854    track_long = 0
855    l5flag = "false"
856    file_count = int(ad.adb.shell("find %s -type f -iname *.txt | wc -l"
857                                  % GNSSSTATUS_LOG_PATH))
858    if file_count != 1:
859        ad.log.error("%d API logs exist." % file_count)
860    dir_file = ad.adb.shell("ls %s" % GNSSSTATUS_LOG_PATH).split()
861    for path_key in dir_file:
862        if fnmatch.fnmatch(path_key, "*.txt"):
863            logpath = posixpath.join(GNSSSTATUS_LOG_PATH, path_key)
864            out = ad.adb.shell("wc -c %s" % logpath)
865            file_size = int(out.split(" ")[0])
866            if file_size < 2000:
867                ad.log.info("Skip log %s due to log size %d bytes" %
868                            (path_key, file_size))
869                continue
870            test_logfile = logpath
871    if not test_logfile:
872        raise signals.TestError("Failed to get test log file in device.")
873    lines = ad.adb.shell("cat %s" % test_logfile).split("\n")
874    for line in lines:
875        if "Antenna_History Avg Top4" in line:
876            ant_top4_cn = float(line.split(":")[-1].strip())
877        elif "Antenna_History Avg" in line:
878            ant_cn = float(line.split(":")[-1].strip())
879        elif "Baseband_History Avg Top4" in line:
880            base_top4_cn = float(line.split(":")[-1].strip())
881        elif "Baseband_History Avg" in line:
882            base_cn = float(line.split(":")[-1].strip())
883        elif "L5 used in fix" in line:
884            l5flag = line.split(":")[-1].strip()
885        elif "Latitude" in line:
886            track_lat = float(line.split(":")[-1].strip())
887        elif "Longitude" in line:
888            track_long = float(line.split(":")[-1].strip())
889        elif "Time" in line:
890            track_utc = line.split("Time:")[-1].strip()
891            if track_utc in track_data.keys():
892                continue
893            pe = calculate_position_error(track_lat, track_long, true_position)
894            track_data[track_utc] = TRACK_REPORT(l5flag=l5flag,
895                                                 pe=pe,
896                                                 ant_top4cn=ant_top4_cn,
897                                                 ant_cn=ant_cn,
898                                                 base_top4cn=base_top4_cn,
899                                                 base_cn=base_cn)
900    ad.log.debug(track_data)
901    prop_basename = "TestResult %s_tracking_" % type.upper()
902    time_list = sorted(track_data.keys())
903    l5flag_list = [track_data[key].l5flag for key in time_list]
904    pe_list = [float(track_data[key].pe) for key in time_list]
905    ant_top4cn_list = [float(track_data[key].ant_top4cn) for key in time_list]
906    ant_cn_list = [float(track_data[key].ant_cn) for key in time_list]
907    base_top4cn_list = [float(track_data[key].base_top4cn) for key in time_list]
908    base_cn_list = [float(track_data[key].base_cn) for key in time_list]
909    ad.log.info(prop_basename+"StartTime %s" % time_list[0].replace(" ", "-"))
910    ad.log.info(prop_basename+"EndTime %s" % time_list[-1].replace(" ", "-"))
911    ad.log.info(prop_basename+"TotalFixPoints %d" % len(time_list))
912    ad.log.info(prop_basename+"L5FixRate "+'{percent:.2%}'.format(
913        percent=l5flag_list.count("true")/len(l5flag_list)))
914    ad.log.info(prop_basename+"AvgDis %.1f" % (sum(pe_list)/len(pe_list)))
915    ad.log.info(prop_basename+"MaxDis %.1f" % max(pe_list))
916    ad.log.info(prop_basename+"Ant_AvgTop4Signal %.1f" % ant_top4cn_list[-1])
917    ad.log.info(prop_basename+"Ant_AvgSignal %.1f" % ant_cn_list[-1])
918    ad.log.info(prop_basename+"Base_AvgTop4Signal %.1f" % base_top4cn_list[-1])
919    ad.log.info(prop_basename+"Base_AvgSignal %.1f" % base_cn_list[-1])
920
921
922def process_ttff_by_gtw_gpstool(ad, begin_time, true_position, type="gnss"):
923    """Process TTFF and record results in ttff_data.
924
925    Args:
926        ad: An AndroidDevice object.
927        begin_time: test begin time.
928        true_position: Coordinate as [latitude, longitude] to calculate
929        position error.
930        type: Different API for location fix. Use gnss/flp/nmea
931
932    Returns:
933        ttff_data: A dict of all TTFF data.
934    """
935    ttff_lat = 0
936    ttff_lon = 0
937    utc_time = epoch_to_human_time(get_current_epoch_time())
938    ttff_data = {}
939    ttff_loop_time = get_current_epoch_time()
940    while True:
941        if get_current_epoch_time() - ttff_loop_time >= 120000:
942            raise signals.TestError("Fail to search specific GPSService "
943                                    "message in logcat. Abort test.")
944        if not ad.is_adb_logcat_on:
945            ad.start_adb_logcat()
946        logcat_results = ad.search_logcat("write TTFF log", ttff_loop_time)
947        if logcat_results:
948            ttff_loop_time = get_current_epoch_time()
949            ttff_log = logcat_results[-1]["log_message"].split()
950            ttff_loop = int(ttff_log[8].split(":")[-1])
951            ttff_sec = float(ttff_log[11])
952            if ttff_sec != 0.0:
953                ttff_ant_cn = float(ttff_log[18].strip("]"))
954                ttff_base_cn = float(ttff_log[25].strip("]"))
955                if type == "gnss":
956                    gnss_results = ad.search_logcat("GPSService: Check item",
957                                                    begin_time)
958                    if gnss_results:
959                        ad.log.debug(gnss_results[-1]["log_message"])
960                        gnss_location_log = \
961                            gnss_results[-1]["log_message"].split()
962                        ttff_lat = float(
963                            gnss_location_log[8].split("=")[-1].strip(","))
964                        ttff_lon = float(
965                            gnss_location_log[9].split("=")[-1].strip(","))
966                        loc_time = int(
967                            gnss_location_log[10].split("=")[-1].strip(","))
968                        utc_time = epoch_to_human_time(loc_time)
969                elif type == "flp":
970                    flp_results = ad.search_logcat("GPSService: FLP Location",
971                                                   begin_time)
972                    if flp_results:
973                        ad.log.debug(flp_results[-1]["log_message"])
974                        flp_location_log = flp_results[-1][
975                            "log_message"].split()
976                        ttff_lat = float(flp_location_log[8].split(",")[0])
977                        ttff_lon = float(flp_location_log[8].split(",")[1])
978                        utc_time = epoch_to_human_time(get_current_epoch_time())
979            else:
980                ttff_ant_cn = float(ttff_log[19].strip("]"))
981                ttff_base_cn = float(ttff_log[26].strip("]"))
982                ttff_lat = 0
983                ttff_lon = 0
984                utc_time = epoch_to_human_time(get_current_epoch_time())
985            ad.log.debug("TTFF Loop %d - (Lat, Lon) = (%s, %s)" % (ttff_loop,
986                                                                   ttff_lat,
987                                                                   ttff_lon))
988            ttff_pe = calculate_position_error(
989                ttff_lat, ttff_lon, true_position)
990            ttff_data[ttff_loop] = TTFF_REPORT(utc_time=utc_time,
991                                               ttff_loop=ttff_loop,
992                                               ttff_sec=ttff_sec,
993                                               ttff_pe=ttff_pe,
994                                               ttff_ant_cn=ttff_ant_cn,
995                                               ttff_base_cn=ttff_base_cn)
996            ad.log.info("UTC Time = %s, Loop %d = %.1f seconds, "
997                        "Position Error = %.1f meters, "
998                        "Antenna Average Signal = %.1f dbHz, "
999                        "Baseband Average Signal = %.1f dbHz" % (utc_time,
1000                                                                 ttff_loop,
1001                                                                 ttff_sec,
1002                                                                 ttff_pe,
1003                                                                 ttff_ant_cn,
1004                                                                 ttff_base_cn))
1005        stop_gps_results = ad.search_logcat("stop gps test", begin_time)
1006        if stop_gps_results:
1007            ad.send_keycode("HOME")
1008            break
1009        crash_result = ad.search_logcat("Force finishing activity "
1010                                        "com.android.gpstool/.GPSTool",
1011                                        begin_time)
1012        if crash_result:
1013            raise signals.TestError("GPSTool crashed. Abort test.")
1014        # wait 5 seconds to avoid logs not writing into logcat yet
1015        time.sleep(5)
1016    return ttff_data
1017
1018
1019def check_ttff_data(ad, ttff_data, ttff_mode, criteria):
1020    """Verify all TTFF results from ttff_data.
1021
1022    Args:
1023        ad: An AndroidDevice object.
1024        ttff_data: TTFF data of secs, position error and signal strength.
1025        ttff_mode: TTFF Test mode for current test item.
1026        criteria: Criteria for current test item.
1027
1028    Returns:
1029        True: All TTFF results are within criteria.
1030        False: One or more TTFF results exceed criteria or Timeout.
1031    """
1032    ad.log.info("%d iterations of TTFF %s tests finished."
1033                % (len(ttff_data.keys()), ttff_mode))
1034    ad.log.info("%s PASS criteria is %d seconds" % (ttff_mode, criteria))
1035    ad.log.debug("%s TTFF data: %s" % (ttff_mode, ttff_data))
1036    ttff_property_key_and_value(ad, ttff_data, ttff_mode)
1037    if len(ttff_data.keys()) == 0:
1038        ad.log.error("GTW_GPSTool didn't process TTFF properly.")
1039        return False
1040    elif any(float(ttff_data[key].ttff_sec) == 0.0 for key in ttff_data.keys()):
1041        ad.log.error("One or more TTFF %s Timeout" % ttff_mode)
1042        return False
1043    elif any(float(ttff_data[key].ttff_sec) >= criteria for key in
1044             ttff_data.keys()):
1045        ad.log.error("One or more TTFF %s are over test criteria %d seconds"
1046                     % (ttff_mode, criteria))
1047        return False
1048    ad.log.info("All TTFF %s are within test criteria %d seconds."
1049                % (ttff_mode, criteria))
1050    return True
1051
1052
1053def ttff_property_key_and_value(ad, ttff_data, ttff_mode):
1054    """Output ttff_data to test_run_info for ACTS plugin to parse and display
1055    on MobileHarness as Property.
1056
1057    Args:
1058        ad: An AndroidDevice object.
1059        ttff_data: TTFF data of secs, position error and signal strength.
1060        ttff_mode: TTFF Test mode for current test item.
1061    """
1062    prop_basename = "TestResult "+ttff_mode.replace(" ", "_")+"_TTFF_"
1063    sec_list = [float(ttff_data[key].ttff_sec) for key in ttff_data.keys()]
1064    pe_list = [float(ttff_data[key].ttff_pe) for key in ttff_data.keys()]
1065    ant_cn_list = [float(ttff_data[key].ttff_ant_cn) for key in
1066                   ttff_data.keys()]
1067    base_cn_list = [float(ttff_data[key].ttff_base_cn) for key in
1068                    ttff_data.keys()]
1069    timeoutcount = sec_list.count(0.0)
1070    if len(sec_list) == timeoutcount:
1071        avgttff = 9527
1072    else:
1073        avgttff = sum(sec_list)/(len(sec_list) - timeoutcount)
1074    if timeoutcount != 0:
1075        maxttff = 9527
1076    else:
1077        maxttff = max(sec_list)
1078    avgdis = sum(pe_list)/len(pe_list)
1079    maxdis = max(pe_list)
1080    ant_avgcn = sum(ant_cn_list)/len(ant_cn_list)
1081    base_avgcn = sum(base_cn_list)/len(base_cn_list)
1082    ad.log.info(prop_basename+"AvgTime %.1f" % avgttff)
1083    ad.log.info(prop_basename+"MaxTime %.1f" % maxttff)
1084    ad.log.info(prop_basename+"TimeoutCount %d" % timeoutcount)
1085    ad.log.info(prop_basename+"AvgDis %.1f" % avgdis)
1086    ad.log.info(prop_basename+"MaxDis %.1f" % maxdis)
1087    ad.log.info(prop_basename+"Ant_AvgSignal %.1f" % ant_avgcn)
1088    ad.log.info(prop_basename+"Base_AvgSignal %.1f" % base_avgcn)
1089
1090
1091def calculate_position_error(latitude, longitude, true_position):
1092    """Use haversine formula to calculate position error base on true location
1093    coordinate.
1094
1095    Args:
1096        latitude: latitude of location fixed in the present.
1097        longitude: longitude of location fixed in the present.
1098        true_position: [latitude, longitude] of true location coordinate.
1099
1100    Returns:
1101        position_error of location fixed in the present.
1102    """
1103    radius = 6371009
1104    dlat = math.radians(latitude - true_position[0])
1105    dlon = math.radians(longitude - true_position[1])
1106    a = math.sin(dlat/2) * math.sin(dlat/2) + \
1107        math.cos(math.radians(true_position[0])) * \
1108        math.cos(math.radians(latitude)) * math.sin(dlon/2) * math.sin(dlon/2)
1109    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
1110    return radius * c
1111
1112
1113def launch_google_map(ad):
1114    """Launch Google Map via intent.
1115
1116    Args:
1117        ad: An AndroidDevice object.
1118    """
1119    ad.log.info("Launch Google Map.")
1120    try:
1121        ad.adb.shell("am start -S -n com.google.android.apps.maps/"
1122                     "com.google.android.maps.MapsActivity")
1123        ad.send_keycode("BACK")
1124        ad.force_stop_apk("com.google.android.apps.maps")
1125        ad.adb.shell("am start -S -n com.google.android.apps.maps/"
1126                     "com.google.android.maps.MapsActivity")
1127    except Exception as e:
1128        ad.log.error(e)
1129        raise signals.TestError("Failed to launch google map.")
1130    check_current_focus_app(ad)
1131
1132
1133def check_current_focus_app(ad):
1134    """Check to see current focused window and app.
1135
1136    Args:
1137        ad: An AndroidDevice object.
1138    """
1139    time.sleep(1)
1140    current = ad.adb.shell(
1141        "dumpsys window | grep -E 'mCurrentFocus|mFocusedApp'")
1142    ad.log.debug("\n"+current)
1143
1144
1145def check_location_api(ad, retries):
1146    """Verify if GnssLocationProvider API reports location.
1147
1148    Args:
1149        ad: An AndroidDevice object.
1150        retries: Retry time.
1151
1152    Returns:
1153        True: GnssLocationProvider API reports location.
1154        otherwise return False.
1155    """
1156    for i in range(retries):
1157        begin_time = get_current_epoch_time()
1158        ad.log.info("Try to get location report from GnssLocationProvider API "
1159                    "- attempt %d" % (i+1))
1160        while get_current_epoch_time() - begin_time <= 30000:
1161            logcat_results = ad.search_logcat("REPORT_LOCATION", begin_time)
1162            if logcat_results:
1163                ad.log.info("%s" % logcat_results[-1]["log_message"])
1164                ad.log.info("GnssLocationProvider reports location "
1165                            "successfully.")
1166                return True
1167        if not ad.is_adb_logcat_on:
1168            ad.start_adb_logcat()
1169    ad.log.error("GnssLocationProvider is unable to report location.")
1170    return False
1171
1172def check_network_location(ad, retries, location_type, criteria=30):
1173    """Verify if NLP reports location after requesting via GPSTool.
1174
1175    Args:
1176        ad: An AndroidDevice object.
1177        retries: Retry time.
1178        location_type: cell or wifi.
1179        criteria: expected nlp return time, default 30 seconds
1180
1181    Returns:
1182        True: NLP reports location.
1183        otherwise return False.
1184    """
1185    criteria = criteria * 1000
1186    search_pattern = ("GPSTool : networkLocationType = %s" % location_type)
1187    for i in range(retries):
1188        begin_time = get_current_epoch_time()
1189        ad.log.info("Try to get NLP status - attempt %d" % (i+1))
1190        ad.adb.shell(
1191            "am start -S -n com.android.gpstool/.GPSTool --es mode nlp")
1192        while get_current_epoch_time() - begin_time <= criteria:
1193            # Search pattern in 1 second interval
1194            time.sleep(1)
1195            result = ad.search_logcat(search_pattern, begin_time)
1196            if result:
1197                ad.log.info("Pattern Found: %s." % result[-1]["log_message"])
1198                ad.send_keycode("BACK")
1199                return True
1200        if not ad.is_adb_logcat_on:
1201            ad.start_adb_logcat()
1202        ad.send_keycode("BACK")
1203    ad.log.error("Unable to report network location \"%s\"." % location_type)
1204    return False
1205
1206
1207def set_attenuator_gnss_signal(ad, attenuator, atten_value):
1208    """Set attenuation value for different GNSS signal.
1209
1210    Args:
1211        ad: An AndroidDevice object.
1212        attenuator: The attenuator object.
1213        atten_value: attenuation value
1214    """
1215    try:
1216        ad.log.info(
1217            "Set attenuation value to \"%d\" for GNSS signal." % atten_value)
1218        attenuator[0].set_atten(atten_value)
1219    except Exception as e:
1220        ad.log.error(e)
1221
1222
1223def set_battery_saver_mode(ad, state):
1224    """Enable or disable battery saver mode via adb.
1225
1226    Args:
1227        ad: An AndroidDevice object.
1228        state: True is enable Battery Saver mode. False is disable.
1229    """
1230    ad.root_adb()
1231    if state:
1232        ad.log.info("Enable Battery Saver mode.")
1233        ad.adb.shell("cmd battery unplug")
1234        ad.adb.shell("settings put global low_power 1")
1235    else:
1236        ad.log.info("Disable Battery Saver mode.")
1237        ad.adb.shell("settings put global low_power 0")
1238        ad.adb.shell("cmd battery reset")
1239
1240
1241def set_gnss_qxdm_mask(ad, masks):
1242    """Find defined gnss qxdm mask and set as default logging mask.
1243
1244    Args:
1245        ad: An AndroidDevice object.
1246        masks: Defined gnss qxdm mask.
1247    """
1248    try:
1249        for mask in masks:
1250            if not tutils.find_qxdm_log_mask(ad, mask):
1251                continue
1252            tutils.set_qxdm_logger_command(ad, mask)
1253            break
1254    except Exception as e:
1255        ad.log.error(e)
1256        raise signals.TestError("Failed to set any QXDM masks.")
1257
1258
1259def start_youtube_video(ad, url=None, retries=0):
1260    """Start youtube video and verify if audio is in music state.
1261
1262    Args:
1263        ad: An AndroidDevice object.
1264        url: Youtube video url.
1265        retries: Retry times if audio is not in music state.
1266
1267    Returns:
1268        True if youtube video is playing normally.
1269        False if youtube video is not playing properly.
1270    """
1271    for i in range(retries):
1272        ad.log.info("Open an youtube video - attempt %d" % (i+1))
1273        cmd = ("am start -n com.google.android.youtube/"
1274               "com.google.android.youtube.UrlActivity -d \"%s\"" % url)
1275        ad.adb.shell(cmd)
1276        time.sleep(2)
1277        out = ad.adb.shell(
1278            "dumpsys activity | grep NewVersionAvailableActivity")
1279        if out:
1280            ad.log.info("Skip Youtube New Version Update.")
1281            ad.send_keycode("BACK")
1282        if tutils.wait_for_state(ad.droid.audioIsMusicActive, True, 15, 1):
1283            ad.log.info("Started a video in youtube, audio is in MUSIC state")
1284            return True
1285        ad.log.info("Force-Stop youtube and reopen youtube again.")
1286        ad.force_stop_apk("com.google.android.youtube")
1287    check_current_focus_app(ad)
1288    raise signals.TestError("Started a video in youtube, "
1289                            "but audio is not in MUSIC state")
1290
1291
1292def get_baseband_and_gms_version(ad, extra_msg=""):
1293    """Get current radio baseband and GMSCore version of AndroidDevice object.
1294
1295    Args:
1296        ad: An AndroidDevice object.
1297        extra_msg: Extra message before or after the change.
1298    """
1299    try:
1300        build_version = ad.adb.getprop("ro.build.id")
1301        baseband_version = ad.adb.getprop("gsm.version.baseband")
1302        gms_version = ad.adb.shell(
1303            "dumpsys package com.google.android.gms | grep versionName"
1304        ).split("\n")[0].split("=")[1]
1305        mpss_version = ad.adb.shell("cat /sys/devices/soc0/images | grep MPSS "
1306                                    "| cut -d ':' -f 3")
1307        if not extra_msg:
1308            ad.log.info("TestResult Build_Version %s" % build_version)
1309            ad.log.info("TestResult Baseband_Version %s" % baseband_version)
1310            ad.log.info(
1311                "TestResult GMS_Version %s" % gms_version.replace(" ", ""))
1312            ad.log.info("TestResult MPSS_Version %s" % mpss_version)
1313        else:
1314            ad.log.info(
1315                "%s, Baseband_Version = %s" % (extra_msg, baseband_version))
1316    except Exception as e:
1317        ad.log.error(e)
1318
1319
1320def start_toggle_gnss_by_gtw_gpstool(ad, iteration):
1321    """Send toggle gnss off/on start_test_action
1322
1323    Args:
1324        ad: An AndroidDevice object.
1325        iteration: Iteration of toggle gnss off/on cycles.
1326    """
1327    msg_list = []
1328    begin_time = get_current_epoch_time()
1329    try:
1330        for i in range(1, 4):
1331            ad.adb.shell("am start -S -n com.android.gpstool/.GPSTool "
1332                         "--es mode toggle --es cycle %d" % iteration)
1333            time.sleep(1)
1334            if ad.search_logcat("cmp=com.android.gpstool/.ToggleGPS",
1335                                begin_time):
1336                ad.log.info("Send ToggleGPS start_test_action successfully.")
1337                break
1338        else:
1339            check_current_focus_app(ad)
1340            raise signals.TestError("Fail to send ToggleGPS "
1341                                    "start_test_action within 3 attempts.")
1342        time.sleep(2)
1343        test_start = ad.search_logcat("GPSTool_ToggleGPS: startService",
1344                                      begin_time)
1345        if test_start:
1346            ad.log.info(test_start[-1]["log_message"].split(":")[-1].strip())
1347        else:
1348            raise signals.TestError("Fail to start toggle GPS off/on test.")
1349        # Every iteration is expected to finish within 4 minutes.
1350        while get_current_epoch_time() - begin_time <= iteration * 240000:
1351            crash_end = ad.search_logcat("Force finishing activity "
1352                                         "com.android.gpstool/.GPSTool",
1353                                         begin_time)
1354            if crash_end:
1355                raise signals.TestError("GPSTool crashed. Abort test.")
1356            toggle_results = ad.search_logcat("GPSTool : msg", begin_time)
1357            if toggle_results:
1358                for toggle_result in toggle_results:
1359                    msg = toggle_result["log_message"]
1360                    if not msg in msg_list:
1361                        ad.log.info(msg.split(":")[-1].strip())
1362                        msg_list.append(msg)
1363                    if "timeout" in msg:
1364                        raise signals.TestFailure("Fail to get location fixed "
1365                                                  "within 60 seconds.")
1366                    if "Test end" in msg:
1367                        raise signals.TestPass("Completed quick toggle GNSS "
1368                                               "off/on test.")
1369        raise signals.TestFailure("Fail to finish toggle GPS off/on test "
1370                                  "within %d minutes" % (iteration * 4))
1371    finally:
1372        ad.send_keycode("HOME")
1373
1374
1375def grant_location_permission(ad, option):
1376    """Grant or revoke location related permission.
1377
1378    Args:
1379        ad: An AndroidDevice object.
1380        option: Boolean to grant or revoke location related permissions.
1381    """
1382    action = "grant" if option else "revoke"
1383    for permission in LOCATION_PERMISSIONS:
1384        ad.log.info(
1385            "%s permission:%s on %s" % (action, permission, TEST_PACKAGE_NAME))
1386        ad.adb.shell("pm %s %s %s" % (action, TEST_PACKAGE_NAME, permission))
1387
1388
1389def check_location_runtime_permissions(ad, package, permissions):
1390    """Check if runtime permissions are granted on selected package.
1391
1392    Args:
1393        ad: An AndroidDevice object.
1394        package: Apk package name to check.
1395        permissions: A list of permissions to be granted.
1396    """
1397    for _ in range(3):
1398        location_runtime_permission = ad.adb.shell(
1399            "dumpsys package %s | grep ACCESS_FINE_LOCATION" % package)
1400        if "true" not in location_runtime_permission:
1401            ad.log.info("ACCESS_FINE_LOCATION is NOT granted on %s" % package)
1402            for permission in permissions:
1403                ad.log.debug("Grant %s on %s" % (permission, package))
1404                ad.adb.shell("pm grant %s %s" % (package, permission))
1405        else:
1406            ad.log.info("ACCESS_FINE_LOCATION is granted on %s" % package)
1407            break
1408    else:
1409        raise signals.TestError(
1410            "Fail to grant ACCESS_FINE_LOCATION on %s" % package)
1411
1412
1413def install_mdstest_app(ad, mdsapp):
1414    """
1415        Install MDS test app in DUT
1416
1417        Args:
1418            ad: An Android Device Object
1419            mdsapp: Installation path of MDSTest app
1420    """
1421    if not ad.is_apk_installed("com.google.mdstest"):
1422        ad.adb.install("-r %s" % mdsapp, timeout=300, ignore_status=True)
1423
1424
1425def write_modemconfig(ad, mdsapp, nvitem_dict, modemparfile):
1426    """
1427        Modify the NV items using modem_tool.par
1428        Note: modem_tool.par
1429
1430        Args:
1431            ad:  An Android Device Object
1432            mdsapp: Installation path of MDSTest app
1433            nvitem_dict: dictionary of NV items and values.
1434            modemparfile: modem_tool.par path.
1435    """
1436    ad.log.info("Verify MDSTest app installed in DUT")
1437    install_mdstest_app(ad, mdsapp)
1438    os.system("chmod 777 %s" % modemparfile)
1439    for key, value in nvitem_dict.items():
1440        if key.isdigit():
1441            op_name = "WriteEFS"
1442        else:
1443            op_name = "WriteNV"
1444        ad.log.info("Modifying the NV{!r} using {}".format(key, op_name))
1445        job.run("{} --op {} --item {} --data '{}'".
1446                format(modemparfile, op_name, key, value))
1447        time.sleep(2)
1448
1449
1450def verify_modemconfig(ad, nvitem_dict, modemparfile):
1451    """
1452        Verify the NV items using modem_tool.par
1453        Note: modem_tool.par
1454
1455        Args:
1456            ad:  An Android Device Object
1457            nvitem_dict: dictionary of NV items and values
1458            modemparfile: modem_tool.par path.
1459    """
1460    os.system("chmod 777 %s" % modemparfile)
1461    for key, value in nvitem_dict.items():
1462        if key.isdigit():
1463            op_name = "ReadEFS"
1464        else:
1465            op_name = "ReadNV"
1466        # Sleeptime to avoid Modem communication error
1467        time.sleep(5)
1468        result = job.run(
1469            "{} --op {} --item {}".format(modemparfile, op_name, key))
1470        output = str(result.stdout)
1471        ad.log.info("Actual Value for NV{!r} is {!r}".format(key, output))
1472        if not value.casefold() in output:
1473            ad.log.error("NV Value is wrong {!r} in {!r}".format(value, result))
1474            raise ValueError(
1475                "could not find {!r} in {!r}".format(value, result))
1476
1477
1478def check_ttff_pe(ad, ttff_data, ttff_mode, pe_criteria):
1479    """Verify all TTFF results from ttff_data.
1480
1481    Args:
1482        ad: An AndroidDevice object.
1483        ttff_data: TTFF data of secs, position error and signal strength.
1484        ttff_mode: TTFF Test mode for current test item.
1485        pe_criteria: Criteria for current test item.
1486
1487    """
1488    ad.log.info("%d iterations of TTFF %s tests finished.",
1489                (len(ttff_data.keys()), ttff_mode))
1490    ad.log.info("%s PASS criteria is %f meters", (ttff_mode, pe_criteria))
1491    ad.log.debug("%s TTFF data: %s", (ttff_mode, ttff_data))
1492
1493    if len(ttff_data.keys()) == 0:
1494        ad.log.error("GTW_GPSTool didn't process TTFF properly.")
1495        raise signals.TestFailure("GTW_GPSTool didn't process TTFF properly.")
1496
1497    elif any(float(ttff_data[key].ttff_pe) >= pe_criteria for key in
1498             ttff_data.keys()):
1499        ad.log.error("One or more TTFF %s are over test criteria %f meters",
1500                     (ttff_mode, pe_criteria))
1501        raise signals.TestFailure("GTW_GPSTool didn't process TTFF properly.")
1502    ad.log.info("All TTFF %s are within test criteria %f meters.",
1503                (ttff_mode, pe_criteria))
1504
1505
1506def check_adblog_functionality(ad):
1507    """Restart adb logcat if system can't write logs into file after checking
1508    adblog file size.
1509
1510    Args:
1511        ad: An AndroidDevice object.
1512    """
1513    logcat_path = os.path.join(ad.device_log_path, "adblog_%s_debug.txt" %
1514                               ad.serial)
1515    if not os.path.exists(logcat_path):
1516        raise signals.TestError("Logcat file %s does not exist." % logcat_path)
1517    original_log_size = os.path.getsize(logcat_path)
1518    ad.log.debug("Original adblog size is %d" % original_log_size)
1519    time.sleep(.5)
1520    current_log_size = os.path.getsize(logcat_path)
1521    ad.log.debug("Current adblog size is %d" % current_log_size)
1522    if current_log_size == original_log_size:
1523        ad.log.warn("System can't write logs into file. Restart adb "
1524                    "logcat process now.")
1525        ad.stop_adb_logcat()
1526        ad.start_adb_logcat()
1527
1528
1529def build_instrumentation_call(package,
1530                               runner,
1531                               test_methods=None,
1532                               options=None):
1533    """Build an instrumentation call for the tests
1534
1535    Args:
1536        package: A string to identify test package.
1537        runner: A string to identify test runner.
1538        test_methods: A dictionary contains {class_name, test_method}.
1539        options: A dictionary constant {key, value} param for test.
1540
1541    Returns:
1542        An instrumentation call command.
1543    """
1544    if test_methods is None:
1545        test_methods = {}
1546        cmd_builder = InstrumentationCommandBuilder()
1547    else:
1548        cmd_builder = InstrumentationTestCommandBuilder()
1549    if options is None:
1550        options = {}
1551    cmd_builder.set_manifest_package(package)
1552    cmd_builder.set_runner(runner)
1553    cmd_builder.add_flag("-w")
1554    for class_name, test_method in test_methods.items():
1555        cmd_builder.add_test_method(class_name, test_method)
1556    for option_key, option_value in options.items():
1557        cmd_builder.add_key_value_param(option_key, option_value)
1558    return cmd_builder.build()
1559
1560
1561def check_chipset_vendor_by_qualcomm(ad):
1562    """Check if cipset vendor is by Qualcomm.
1563
1564    Args:
1565        ad: An AndroidDevice object.
1566
1567    Returns:
1568        True if it's by Qualcomm. False irf not.
1569    """
1570    ad.root_adb()
1571    soc = str(ad.adb.shell("getprop gsm.version.ril-impl"))
1572    ad.log.debug("SOC = %s" % soc)
1573    return "Qualcomm" in soc
1574
1575
1576def delete_lto_file(ad):
1577    """Delete downloaded LTO files.
1578
1579    Args:
1580        ad: An AndroidDevice object.
1581    """
1582    remount_device(ad)
1583    status = ad.adb.shell("rm -rf /data/vendor/gps/lto*")
1584    ad.log.info("Delete downloaded LTO files.\n%s" % status)
1585
1586
1587def lto_mode(ad, state):
1588    """Enable or Disable LTO mode.
1589
1590    Args:
1591        ad: An AndroidDevice object.
1592        state: True to enable. False to disable.
1593    """
1594    server_list = ["LONGTERM_PSDS_SERVER_1",
1595                   "LONGTERM_PSDS_SERVER_2",
1596                   "LONGTERM_PSDS_SERVER_3",
1597                   "NORMAL_PSDS_SERVER",
1598                   "REALTIME_PSDS_SERVER"]
1599    delete_lto_file(ad)
1600    tmp_path = tempfile.mkdtemp()
1601    ad.pull_files("/etc/gps_debug.conf", tmp_path)
1602    gps_conf_path = os.path.join(tmp_path, "gps_debug.conf")
1603    gps_conf_file = open(gps_conf_path, "r")
1604    lines = gps_conf_file.readlines()
1605    gps_conf_file.close()
1606    fout = open(gps_conf_path, "w")
1607    if state:
1608        for line in lines:
1609            for server in server_list:
1610                if server in line:
1611                    line = line.replace(line, "")
1612            fout.write(line)
1613        fout.close()
1614        ad.push_system_file(gps_conf_path, "/etc/gps_debug.conf")
1615        ad.log.info("Push back modified gps_debug.conf")
1616        ad.log.info("LTO/RTO/RTI enabled")
1617    else:
1618        ad.adb.shell("echo %r >> /etc/gps_debug.conf" %
1619                     DISABLE_LTO_FILE_CONTENTS)
1620        ad.log.info("LTO/RTO/RTI disabled")
1621    reboot(ad)
1622
1623
1624def start_pixel_logger(ad, max_log_size_mb=100, max_number_of_files=500):
1625    """adb to start pixel logger for GNSS logging.
1626
1627    Args:
1628        ad: An AndroidDevice object.
1629        max_log_size_mb: Determines when to create a new log file if current
1630            one reaches the size limit.
1631        max_number_of_files: Determines how many log files can be saved on DUT.
1632    """
1633    retries = 3
1634    start_timeout_sec = 60
1635    default_gnss_cfg = "/vendor/etc/mdlog/DEFAULT+SECURITY+FULLDPL+GPS.cfg"
1636    if check_chipset_vendor_by_qualcomm(ad):
1637        start_cmd = ("am start-foreground-service -a com.android.pixellogger"
1638                     ".service.logging.LoggingService.ACTION_START_LOGGING "
1639                     "-e intent_key_cfg_path '%s' "
1640                     "--ei intent_key_max_log_size_mb %d "
1641                     "--ei intent_key_max_number_of_files %d" % (
1642            default_gnss_cfg, max_log_size_mb, max_number_of_files))
1643    else:
1644        start_cmd = ("am startservice -a com.android.pixellogger."
1645                     "service.logging.LoggingService.ACTION_START_LOGGING "
1646                     "-e intent_logger brcm_gps")
1647    for attempt in range(retries):
1648        begin_time = get_current_epoch_time()
1649        ad.log.info("Start Pixel Logger. - Attempt %d" % (attempt + 1))
1650        ad.adb.shell(start_cmd)
1651        while get_current_epoch_time() - begin_time <= start_timeout_sec * 1000:
1652            if not ad.is_adb_logcat_on:
1653                ad.start_adb_logcat()
1654            if check_chipset_vendor_by_qualcomm(ad):
1655                start_result = ad.search_logcat("Start logging", begin_time)
1656            else:
1657                start_result = ad.search_logcat("startRecording", begin_time)
1658            if start_result:
1659                ad.log.info("Pixel Logger starts recording successfully.")
1660                return True
1661        ad.force_stop_apk("com.android.pixellogger")
1662    else:
1663        ad.log.warn("Pixel Logger fails to start recording in %d seconds "
1664                    "within %d attempts." % (start_timeout_sec, retries))
1665
1666
1667def stop_pixel_logger(ad):
1668    """adb to stop pixel logger for GNSS logging.
1669
1670    Args:
1671        ad: An AndroidDevice object.
1672    """
1673    retries = 3
1674    stop_timeout_sec = 300
1675    if check_chipset_vendor_by_qualcomm(ad):
1676        stop_cmd = ("am start-foreground-service -a com.android.pixellogger"
1677                    ".service.logging.LoggingService.ACTION_STOP_LOGGING")
1678    else:
1679        stop_cmd = ("am startservice -a com.android.pixellogger."
1680                    "service.logging.LoggingService.ACTION_STOP_LOGGING "
1681                    "-e intent_logger brcm_gps")
1682    for attempt in range(retries):
1683        begin_time = get_current_epoch_time()
1684        ad.log.info("Stop Pixel Logger. - Attempt %d" % (attempt + 1))
1685        ad.adb.shell(stop_cmd)
1686        while get_current_epoch_time() - begin_time <= stop_timeout_sec * 1000:
1687            if not ad.is_adb_logcat_on:
1688                ad.start_adb_logcat()
1689            stop_result = ad.search_logcat(
1690                "LoggingService: Stopping service", begin_time)
1691            if stop_result:
1692                ad.log.info("Pixel Logger stops successfully.")
1693                return True
1694        ad.force_stop_apk("com.android.pixellogger")
1695    else:
1696        ad.log.warn("Pixel Logger fails to stop in %d seconds within %d "
1697                    "attempts." % (stop_timeout_sec, retries))
1698
1699
1700def launch_eecoexer(ad):
1701    """adb to stop pixel logger for GNSS logging.
1702
1703    Args:
1704        ad: An AndroidDevice object.
1705    """
1706    launch_cmd = ("am start -a android.intent.action.MAIN -n"
1707                  "com.google.eecoexer"
1708                  "/.MainActivity")
1709    ad.adb.shell(launch_cmd)
1710    try:
1711        ad.log.info("Launch EEcoexer.")
1712    except Exception as e:
1713        ad.log.error(e)
1714        raise signals.TestError("Failed to launch EEcoexer.")
1715
1716
1717def excute_eecoexer_function(ad, eecoexer_args):
1718    """adb to stop pixel logger for GNSS logging.
1719
1720    Args:
1721        ad: An AndroidDevice object.
1722        eecoexer_args: EEcoexer function arguments
1723    """
1724    enqueue_cmd = ("am broadcast -a com.google.eecoexer.action.LISTENER"
1725                   " --es sms_body ENQUEUE,{}".format(eecoexer_args))
1726    exe_cmd = ("am broadcast -a com.google.eecoexer.action.LISTENER"
1727               " --es sms_body EXECUTE")
1728    ad.log.info("EEcoexer Add Enqueue: {}".format(eecoexer_args))
1729    ad.adb.shell(enqueue_cmd)
1730    ad.log.info("EEcoexer Excute.")
1731    ad.adb.shell(exe_cmd)
1732
1733
1734def restart_gps_daemons(ad):
1735    """Restart GPS daemons by killing services of gpsd, lhd and scd.
1736
1737    Args:
1738        ad: An AndroidDevice object.
1739    """
1740    gps_daemons_list = ["gpsd", "lhd", "scd"]
1741    ad.root_adb()
1742    for service in gps_daemons_list:
1743        begin_time = get_current_epoch_time()
1744        time.sleep(3)
1745        ad.log.info("Kill GPS daemon \"%s\"" % service)
1746        ad.adb.shell("killall %s" % service)
1747        # Wait 3 seconds for daemons and services to start.
1748        time.sleep(3)
1749        restart_services = ad.search_logcat("starting service", begin_time)
1750        for restart_service in restart_services:
1751            if service in restart_service["log_message"]:
1752                ad.log.info(restart_service["log_message"])
1753                ad.log.info(
1754                    "GPS daemon \"%s\" restarts successfully." % service)
1755                break
1756        else:
1757            raise signals.TestError("Unable to restart \"%s\"" % service)
1758