• 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
25import zipfile
26from collections import namedtuple
27from datetime import datetime
28from xml.etree import ElementTree
29
30from acts import utils
31from acts import asserts
32from acts import signals
33from acts.libs.proc import job
34from acts.controllers.android_device import list_adb_devices
35from acts.controllers.android_device import list_fastboot_devices
36from acts.controllers.android_device import DEFAULT_QXDM_LOG_PATH
37from acts.controllers.android_device import SL4A_APK_NAME
38from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
39from acts_contrib.test_utils.tel import tel_logging_utils as tlutils
40from acts_contrib.test_utils.tel import tel_test_utils as tutils
41from acts_contrib.test_utils.instrumentation.device.command.instrumentation_command_builder import InstrumentationCommandBuilder
42from acts_contrib.test_utils.instrumentation.device.command.instrumentation_command_builder import InstrumentationTestCommandBuilder
43from acts.utils import get_current_epoch_time
44from acts.utils import epoch_to_human_time
45from acts_contrib.test_utils.gnss.gnss_defines import BCM_GPS_XML_PATH
46from acts_contrib.test_utils.gnss.gnss_defines import BCM_NVME_STO_PATH
47
48WifiEnums = wutils.WifiEnums
49PULL_TIMEOUT = 300
50GNSSSTATUS_LOG_PATH = (
51    "/storage/emulated/0/Android/data/com.android.gpstool/files/")
52QXDM_MASKS = ["GPS.cfg", "GPS-general.cfg", "default.cfg"]
53TTFF_REPORT = namedtuple(
54    "TTFF_REPORT", "utc_time ttff_loop ttff_sec ttff_pe ttff_ant_cn "
55                   "ttff_base_cn ttff_haccu")
56TRACK_REPORT = namedtuple(
57    "TRACK_REPORT", "l5flag pe ant_top4cn ant_cn base_top4cn base_cn")
58LOCAL_PROP_FILE_CONTENTS = """\
59log.tag.LocationManagerService=VERBOSE
60log.tag.GnssLocationProvider=VERBOSE
61log.tag.GnssMeasurementsProvider=VERBOSE
62log.tag.GpsNetInitiatedHandler=VERBOSE
63log.tag.GnssNetInitiatedHandler=VERBOSE
64log.tag.GnssNetworkConnectivityHandler=VERBOSE
65log.tag.ConnectivityService=VERBOSE
66log.tag.ConnectivityManager=VERBOSE
67log.tag.GnssVisibilityControl=VERBOSE
68log.tag.NtpTimeHelper=VERBOSE
69log.tag.NtpTrustedTime=VERBOSE
70log.tag.GnssPsdsDownloader=VERBOSE
71log.tag.Gnss=VERBOSE
72log.tag.GnssConfiguration=VERBOSE"""
73TEST_PACKAGE_NAME = "com.google.android.apps.maps"
74LOCATION_PERMISSIONS = [
75    "android.permission.ACCESS_FINE_LOCATION",
76    "android.permission.ACCESS_COARSE_LOCATION"
77]
78GNSSTOOL_PACKAGE_NAME = "com.android.gpstool"
79GNSSTOOL_PERMISSIONS = [
80    "android.permission.ACCESS_FINE_LOCATION",
81    "android.permission.READ_EXTERNAL_STORAGE",
82    "android.permission.ACCESS_COARSE_LOCATION",
83    "android.permission.CALL_PHONE",
84    "android.permission.WRITE_CONTACTS",
85    "android.permission.CAMERA",
86    "android.permission.WRITE_EXTERNAL_STORAGE",
87    "android.permission.READ_CONTACTS",
88    "android.permission.ACCESS_BACKGROUND_LOCATION"
89]
90DISABLE_LTO_FILE_CONTENTS = """\
91LONGTERM_PSDS_SERVER_1="http://"
92LONGTERM_PSDS_SERVER_2="http://"
93LONGTERM_PSDS_SERVER_3="http://"
94NORMAL_PSDS_SERVER="http://"
95REALTIME_PSDS_SERVER="http://"
96"""
97DISABLE_LTO_FILE_CONTENTS_R = """\
98XTRA_SERVER_1="http://"
99XTRA_SERVER_2="http://"
100XTRA_SERVER_3="http://"
101"""
102
103
104class GnssTestUtilsError(Exception):
105    pass
106
107
108def remount_device(ad):
109    """Remount device file system to read and write.
110
111    Args:
112        ad: An AndroidDevice object.
113    """
114    for retries in range(5):
115        ad.root_adb()
116        if ad.adb.getprop("ro.boot.veritymode") == "enforcing":
117            ad.adb.disable_verity()
118            reboot(ad)
119        remount_result = ad.adb.remount()
120        ad.log.info("Attempt %d - %s" % (retries + 1, remount_result))
121        if "remount succeeded" in remount_result:
122            break
123
124
125def reboot(ad):
126    """Reboot device and check if mobile data is available.
127
128    Args:
129        ad: An AndroidDevice object.
130    """
131    ad.log.info("Reboot device to make changes take effect.")
132    ad.reboot()
133    ad.unlock_screen(password=None)
134    if not is_mobile_data_on(ad):
135        set_mobile_data(ad, True)
136    utils.sync_device_time(ad)
137
138
139def enable_gnss_verbose_logging(ad):
140    """Enable GNSS VERBOSE Logging and persistent logcat.
141
142    Args:
143        ad: An AndroidDevice object.
144    """
145    remount_device(ad)
146    ad.log.info("Enable GNSS VERBOSE Logging and persistent logcat.")
147    if check_chipset_vendor_by_qualcomm(ad):
148        ad.adb.shell("echo -e '\nDEBUG_LEVEL = 5' >> /vendor/etc/gps.conf")
149    else:
150        ad.adb.shell("echo LogEnabled=true >> /data/vendor/gps/libgps.conf")
151        ad.adb.shell("chown gps.system /data/vendor/gps/libgps.conf")
152    ad.adb.shell("echo %r >> /data/local.prop" % LOCAL_PROP_FILE_CONTENTS)
153    ad.adb.shell("chmod 644 /data/local.prop")
154    ad.adb.shell("setprop persist.logd.logpersistd.size 20000")
155    ad.adb.shell("setprop persist.logd.size 16777216")
156    ad.adb.shell("setprop persist.vendor.radio.adb_log_on 1")
157    ad.adb.shell("setprop persist.logd.logpersistd logcatd")
158    ad.adb.shell("setprop log.tag.copresGcore VERBOSE")
159    ad.adb.shell("sync")
160
161
162def get_am_flags(value):
163    """Returns the (value, type) flags for a given python value."""
164    if type(value) is bool:
165        return str(value).lower(), 'boolean'
166    elif type(value) is str:
167        return value, 'string'
168    raise ValueError("%s should be either 'boolean' or 'string'" % value)
169
170
171def enable_compact_and_particle_fusion_log(ad):
172    """Enable CompactLog, FLP particle fusion log and disable gms
173    location-based quake monitoring.
174
175    Args:
176        ad: An AndroidDevice object.
177    """
178    ad.root_adb()
179    ad.log.info("Enable FLP flags and Disable GMS location-based quake "
180                "monitoring.")
181    overrides = {
182        'compact_log_enabled': True,
183        'flp_use_particle_fusion': True,
184        'flp_particle_fusion_extended_bug_report': True,
185        'flp_event_log_size': '86400',
186        'proks_config': '28',
187        'flp_particle_fusion_bug_report_window_sec': '86400',
188        'flp_particle_fusion_bug_report_max_buffer_size': '86400',
189        'seismic_data_collection': False,
190        'Ealert__enable': False,
191    }
192    for flag, python_value in overrides.items():
193        value, type = get_am_flags(python_value)
194        cmd = ("am broadcast -a com.google.android.gms.phenotype.FLAG_OVERRIDE "
195               "--es package com.google.android.location --es user \* "
196               "--esa flags %s --esa values %s --esa types %s "
197               "com.google.android.gms" % (flag, value, type))
198        ad.adb.shell(cmd, ignore_status=True)
199    ad.adb.shell("am force-stop com.google.android.gms")
200    ad.adb.shell("am broadcast -a com.google.android.gms.INITIALIZE")
201
202
203def disable_xtra_throttle(ad):
204    """Disable XTRA throttle will have no limit to download XTRA data.
205
206    Args:
207        ad: An AndroidDevice object.
208    """
209    remount_device(ad)
210    ad.log.info("Disable XTRA Throttle.")
211    ad.adb.shell("echo -e '\nXTRA_TEST_ENABLED=1' >> /vendor/etc/gps.conf")
212    ad.adb.shell("echo -e '\nXTRA_THROTTLE_ENABLED=0' >> /vendor/etc/gps.conf")
213
214
215def enable_supl_mode(ad):
216    """Enable SUPL back on for next test item.
217
218    Args:
219        ad: An AndroidDevice object.
220    """
221    remount_device(ad)
222    ad.log.info("Enable SUPL mode.")
223    ad.adb.shell("echo -e '\nSUPL_MODE=1' >> /etc/gps_debug.conf")
224    if is_device_wearable(ad):
225        lto_mode_wearable(ad, True)
226    elif not check_chipset_vendor_by_qualcomm(ad):
227        lto_mode(ad, True)
228    else:
229        reboot(ad)
230
231
232def disable_supl_mode(ad):
233    """Kill SUPL to test XTRA/LTO only test item.
234
235    Args:
236        ad: An AndroidDevice object.
237    """
238    remount_device(ad)
239    ad.log.info("Disable SUPL mode.")
240    ad.adb.shell("echo -e '\nSUPL_MODE=0' >> /etc/gps_debug.conf")
241    if is_device_wearable(ad):
242        lto_mode_wearable(ad, True)
243    elif not check_chipset_vendor_by_qualcomm(ad):
244        lto_mode(ad, True)
245    else:
246        reboot(ad)
247
248
249def kill_xtra_daemon(ad):
250    """Kill XTRA daemon to test SUPL only test item.
251
252    Args:
253        ad: An AndroidDevice object.
254    """
255    ad.root_adb()
256    if is_device_wearable(ad):
257        lto_mode_wearable(ad, False)
258    elif check_chipset_vendor_by_qualcomm(ad):
259        ad.log.info("Disable XTRA-daemon until next reboot.")
260        ad.adb.shell("killall xtra-daemon", ignore_status=True)
261    else:
262        lto_mode(ad, False)
263
264
265def disable_private_dns_mode(ad):
266    """Due to b/118365122, it's better to disable private DNS mode while
267       testing. 8.8.8.8 private dns sever is unstable now, sometimes server
268       will not response dns query suddenly.
269
270    Args:
271        ad: An AndroidDevice object.
272    """
273    tutils.get_operator_name(ad.log, ad, subId=None)
274    if ad.adb.shell("settings get global private_dns_mode") != "off":
275        ad.log.info("Disable Private DNS mode.")
276        ad.adb.shell("settings put global private_dns_mode off")
277
278
279def _init_device(ad):
280    """Init GNSS test devices.
281
282    Args:
283        ad: An AndroidDevice object.
284    """
285    enable_gnss_verbose_logging(ad)
286    enable_compact_and_particle_fusion_log(ad)
287    prepare_gps_overlay(ad)
288    if check_chipset_vendor_by_qualcomm(ad):
289        disable_xtra_throttle(ad)
290    enable_supl_mode(ad)
291    if is_device_wearable(ad):
292        ad.adb.shell("settings put global stay_on_while_plugged_in 7")
293    else:
294        ad.adb.shell("settings put system screen_off_timeout 1800000")
295    wutils.wifi_toggle_state(ad, False)
296    ad.log.info("Setting Bluetooth state to False")
297    ad.droid.bluetoothToggleState(False)
298    check_location_service(ad)
299    set_wifi_and_bt_scanning(ad, True)
300    disable_private_dns_mode(ad)
301    reboot(ad)
302    init_gtw_gpstool(ad)
303    if not is_mobile_data_on(ad):
304        set_mobile_data(ad, True)
305
306
307def prepare_gps_overlay(ad):
308    """Set pixellogger gps log mask to
309    resolve gps logs unreplayable from brcm vendor
310    """
311    if not check_chipset_vendor_by_qualcomm(ad):
312        overlay_file = "/data/vendor/gps/overlay/gps_overlay.xml"
313        xml_file = generate_gps_overlay_xml(ad)
314        try:
315            ad.log.info("Push gps_overlay to device")
316            ad.adb.push(xml_file, overlay_file)
317            ad.adb.shell(f"chmod 777 {overlay_file}")
318        finally:
319            xml_folder = os.path.abspath(os.path.join(xml_file, os.pardir))
320            shutil.rmtree(xml_folder)
321
322
323def generate_gps_overlay_xml(ad):
324    """For r11 devices, the overlay setting is 'Replayable default'
325    For other brcm devices, the setting is 'Replayable debug'
326
327    Returns:
328        path to the xml file
329    """
330    root_attrib = {
331        "xmlns": "http://www.glpals.com/",
332        "xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
333        "xsi:schemaLocation": "http://www.glpals.com/ glconfig.xsd",
334    }
335    sub_attrib = {"EnableOnChipStopNotification": "true"}
336    if not is_device_wearable(ad):
337        sub_attrib["LogPriMask"] = "LOG_DEBUG"
338        sub_attrib["LogFacMask"] = "LOG_GLLIO | LOG_GLLAPI | LOG_NMEA | LOG_RAWDATA"
339        sub_attrib["OnChipLogPriMask"] = "LOG_DEBUG"
340        sub_attrib["OnChipLogFacMask"] = "LOG_GLLIO | LOG_GLLAPI | LOG_NMEA | LOG_RAWDATA"
341
342    temp_path = tempfile.mkdtemp()
343    xml_file = os.path.join(temp_path, "gps_overlay.xml")
344
345    root = ElementTree.Element('glgps')
346    for key, value in root_attrib.items():
347        root.attrib[key] = value
348
349    ad.log.debug("Sub attrib is %s", sub_attrib)
350
351    sub = ElementTree.SubElement(root, 'gll')
352    for key, value in sub_attrib.items():
353        sub.attrib[key] = value
354
355    xml = ElementTree.ElementTree(root)
356    xml.write(xml_file, xml_declaration=True, encoding="utf-8", method="xml")
357    return xml_file
358
359
360def connect_to_wifi_network(ad, network):
361    """Connection logic for open and psk wifi networks.
362
363    Args:
364        ad: An AndroidDevice object.
365        network: Dictionary with network info.
366    """
367    SSID = network[WifiEnums.SSID_KEY]
368    ad.ed.clear_all_events()
369    wutils.reset_wifi(ad)
370    wutils.start_wifi_connection_scan_and_ensure_network_found(ad, SSID)
371    wutils.wifi_connect(ad, network, num_of_tries=5)
372
373
374def set_wifi_and_bt_scanning(ad, state=True):
375    """Set Wi-Fi and Bluetooth scanning on/off in Settings -> Location
376
377    Args:
378        ad: An AndroidDevice object.
379        state: True to turn on "Wi-Fi and Bluetooth scanning".
380            False to turn off "Wi-Fi and Bluetooth scanning".
381    """
382    ad.root_adb()
383    if state:
384        ad.adb.shell("settings put global wifi_scan_always_enabled 1")
385        ad.adb.shell("settings put global ble_scan_always_enabled 1")
386        ad.log.info("Wi-Fi and Bluetooth scanning are enabled")
387    else:
388        ad.adb.shell("settings put global wifi_scan_always_enabled 0")
389        ad.adb.shell("settings put global ble_scan_always_enabled 0")
390        ad.log.info("Wi-Fi and Bluetooth scanning are disabled")
391
392
393def check_location_service(ad):
394    """Set location service on.
395       Verify if location service is available.
396
397    Args:
398        ad: An AndroidDevice object.
399    """
400    remount_device(ad)
401    utils.set_location_service(ad, True)
402    ad.adb.shell("cmd location set-location-enabled true")
403    location_mode = int(ad.adb.shell("settings get secure location_mode"))
404    ad.log.info("Current Location Mode >> %d" % location_mode)
405    if location_mode != 3:
406        raise signals.TestError("Failed to turn Location on")
407
408
409def clear_logd_gnss_qxdm_log(ad):
410    """Clear /data/misc/logd,
411    /storage/emulated/0/Android/data/com.android.gpstool/files and
412    /data/vendor/radio/diag_logs/logs from previous test item then reboot.
413
414    Args:
415        ad: An AndroidDevice object.
416    """
417    remount_device(ad)
418    ad.log.info("Clear Logd, GNSS and PixelLogger Log from previous test item.")
419    ad.adb.shell("rm -rf /data/misc/logd", ignore_status=True)
420    ad.adb.shell(
421        'find %s -name "*.txt" -type f -delete' % GNSSSTATUS_LOG_PATH,
422        ignore_status=True)
423    if check_chipset_vendor_by_qualcomm(ad):
424        diag_logs = (
425            "/sdcard/Android/data/com.android.pixellogger/files/logs/diag_logs")
426        ad.adb.shell("rm -rf %s" % diag_logs, ignore_status=True)
427        output_path = posixpath.join(DEFAULT_QXDM_LOG_PATH, "logs")
428    else:
429        output_path = ("/sdcard/Android/data/com.android.pixellogger/files"
430                       "/logs/gps/")
431    ad.adb.shell("rm -rf %s" % output_path, ignore_status=True)
432    reboot(ad)
433
434
435def get_gnss_qxdm_log(ad, qdb_path=None):
436    """Get /storage/emulated/0/Android/data/com.android.gpstool/files and
437    /data/vendor/radio/diag_logs/logs for test item.
438
439    Args:
440        ad: An AndroidDevice object.
441        qdb_path: The path of qdsp6m.qdb on different projects.
442    """
443    log_path = ad.device_log_path
444    os.makedirs(log_path, exist_ok=True)
445    gnss_log_name = "gnssstatus_log_%s_%s" % (ad.model, ad.serial)
446    gnss_log_path = posixpath.join(log_path, gnss_log_name)
447    os.makedirs(gnss_log_path, exist_ok=True)
448    ad.log.info("Pull GnssStatus Log to %s" % gnss_log_path)
449    ad.adb.pull("%s %s" % (GNSSSTATUS_LOG_PATH + ".", gnss_log_path),
450                timeout=PULL_TIMEOUT, ignore_status=True)
451    shutil.make_archive(gnss_log_path, "zip", gnss_log_path)
452    shutil.rmtree(gnss_log_path, ignore_errors=True)
453    if check_chipset_vendor_by_qualcomm(ad):
454        output_path = (
455            "/sdcard/Android/data/com.android.pixellogger/files/logs/"
456            "diag_logs/.")
457    else:
458        output_path = (
459            "/sdcard/Android/data/com.android.pixellogger/files/logs/gps/.")
460    qxdm_log_name = "PixelLogger_%s_%s" % (ad.model, ad.serial)
461    qxdm_log_path = posixpath.join(log_path, qxdm_log_name)
462    os.makedirs(qxdm_log_path, exist_ok=True)
463    ad.log.info("Pull PixelLogger Log %s to %s" % (output_path,
464                                                   qxdm_log_path))
465    ad.adb.pull("%s %s" % (output_path, qxdm_log_path),
466                timeout=PULL_TIMEOUT, ignore_status=True)
467    if check_chipset_vendor_by_qualcomm(ad):
468        for path in qdb_path:
469            output = ad.adb.pull("%s %s" % (path, qxdm_log_path),
470                                 timeout=PULL_TIMEOUT, ignore_status=True)
471            if "No such file or directory" in output:
472                continue
473            break
474    shutil.make_archive(qxdm_log_path, "zip", qxdm_log_path)
475    shutil.rmtree(qxdm_log_path, ignore_errors=True)
476
477
478def set_mobile_data(ad, state):
479    """Set mobile data on or off and check mobile data state.
480
481    Args:
482        ad: An AndroidDevice object.
483        state: True to enable mobile data. False to disable mobile data.
484    """
485    ad.root_adb()
486    if state:
487        if is_device_wearable(ad):
488            ad.log.info("Enable wearable mobile data.")
489            ad.adb.shell("settings put global cell_on 1")
490        else:
491            ad.log.info("Enable mobile data via RPC call.")
492            ad.droid.telephonyToggleDataConnection(True)
493    else:
494        if is_device_wearable(ad):
495            ad.log.info("Disable wearable mobile data.")
496            ad.adb.shell("settings put global cell_on 0")
497        else:
498            ad.log.info("Disable mobile data via RPC call.")
499            ad.droid.telephonyToggleDataConnection(False)
500    time.sleep(5)
501    ret_val = is_mobile_data_on(ad)
502    if state and ret_val:
503        ad.log.info("Mobile data is enabled and set to %s" % ret_val)
504    elif not state and not ret_val:
505        ad.log.info("Mobile data is disabled and set to %s" % ret_val)
506    else:
507        ad.log.error("Mobile data is at unknown state and set to %s" % ret_val)
508
509
510def gnss_trigger_modem_ssr_by_adb(ad, dwelltime=60):
511    """Trigger modem SSR crash by adb and verify if modem crash and recover
512    successfully.
513
514    Args:
515        ad: An AndroidDevice object.
516        dwelltime: Waiting time for modem reset. Default is 60 seconds.
517
518    Returns:
519        True if success.
520        False if failed.
521    """
522    begin_time = get_current_epoch_time()
523    ad.root_adb()
524    cmds = ("echo restart > /sys/kernel/debug/msm_subsys/modem",
525            r"echo 'at+cfun=1,1\r' > /dev/at_mdm0")
526    for cmd in cmds:
527        ad.log.info("Triggering modem SSR crash by %s" % cmd)
528        output = ad.adb.shell(cmd, ignore_status=True)
529        if "No such file or directory" in output:
530            continue
531        break
532    time.sleep(dwelltime)
533    ad.send_keycode("HOME")
534    logcat_results = ad.search_logcat("SSRObserver", begin_time)
535    if logcat_results:
536        for ssr in logcat_results:
537            if "mSubsystem='modem', mCrashReason" in ssr["log_message"]:
538                ad.log.debug(ssr["log_message"])
539                ad.log.info("Triggering modem SSR crash successfully.")
540                return True
541        raise signals.TestError("Failed to trigger modem SSR crash")
542    raise signals.TestError("No SSRObserver found in logcat")
543
544
545def gnss_trigger_modem_ssr_by_mds(ad, dwelltime=60):
546    """Trigger modem SSR crash by mds tool and verify if modem crash and recover
547    successfully.
548
549    Args:
550        ad: An AndroidDevice object.
551        dwelltime: Waiting time for modem reset. Default is 60 seconds.
552    """
553    mds_check = ad.adb.shell("pm path com.google.mdstest")
554    if not mds_check:
555        raise signals.TestError("MDS Tool is not properly installed.")
556    ad.root_adb()
557    cmd = ('am instrument -w -e request "4b 25 03 00" '
558           '"com.google.mdstest/com.google.mdstest.instrument'
559           '.ModemCommandInstrumentation"')
560    ad.log.info("Triggering modem SSR crash by MDS")
561    output = ad.adb.shell(cmd, ignore_status=True)
562    ad.log.debug(output)
563    time.sleep(dwelltime)
564    ad.send_keycode("HOME")
565    if "SUCCESS" in output:
566        ad.log.info("Triggering modem SSR crash by MDS successfully.")
567    else:
568        raise signals.TestError(
569            "Failed to trigger modem SSR crash by MDS. \n%s" % output)
570
571
572def check_xtra_download(ad, begin_time):
573    """Verify XTRA download success log message in logcat.
574
575    Args:
576        ad: An AndroidDevice object.
577        begin_time: test begin time
578
579    Returns:
580        True: xtra_download if XTRA downloaded and injected successfully
581        otherwise return False.
582    """
583    ad.send_keycode("HOME")
584    if check_chipset_vendor_by_qualcomm(ad):
585        xtra_results = ad.search_logcat("XTRA download success. "
586                                        "inject data into modem", begin_time)
587        if xtra_results:
588            ad.log.debug("%s" % xtra_results[-1]["log_message"])
589            ad.log.info("XTRA downloaded and injected successfully.")
590            return True
591        ad.log.error("XTRA downloaded FAIL.")
592    elif is_device_wearable(ad):
593        lto_results = ad.adb.shell("ls -al /data/vendor/gps/lto*")
594        if "lto2.dat" in lto_results:
595            ad.log.info("LTO downloaded and injected successfully.")
596            return True
597    else:
598        lto_results = ad.search_logcat("GnssPsdsAidl: injectPsdsData: "
599                                       "psdsType: 1", begin_time)
600        if lto_results:
601            ad.log.debug("%s" % lto_results[-1]["log_message"])
602            ad.log.info("LTO downloaded and injected successfully.")
603            return True
604        ad.log.error("LTO downloaded and inject FAIL.")
605    return False
606
607
608def pull_package_apk(ad, package_name):
609    """Pull apk of given package_name from device.
610
611    Args:
612        ad: An AndroidDevice object.
613        package_name: Package name of apk to pull.
614
615    Returns:
616        The temp path of pulled apk.
617    """
618    out = ad.adb.shell("pm path %s" % package_name)
619    result = re.search(r"package:(.*)", out)
620    if not result:
621        raise signals.TestError("Couldn't find apk of %s" % package_name)
622    else:
623        apk_source = result.group(1)
624        ad.log.info("Get apk of %s from %s" % (package_name, apk_source))
625        apk_path = tempfile.mkdtemp()
626        ad.pull_files([apk_source], apk_path)
627    return apk_path
628
629
630def pull_gnss_cfg_file(ad, file):
631    """Pull given gnss cfg file from device.
632
633    Args:
634        ad: An AndroidDevice object.
635        file: CFG file in device to pull.
636
637    Returns:
638        The temp path of pulled gnss cfg file in host.
639    """
640    ad.root_adb()
641    host_dest = tempfile.mkdtemp()
642    ad.pull_files(file, host_dest)
643    for path_key in os.listdir(host_dest):
644        if fnmatch.fnmatch(path_key, "*.cfg"):
645            gnss_cfg_file = os.path.join(host_dest, path_key)
646            break
647    else:
648        raise signals.TestError("No cfg file is found in %s" % host_dest)
649    return gnss_cfg_file
650
651
652def reinstall_package_apk(ad, package_name, apk_path):
653    """Reinstall apk of given package_name.
654
655    Args:
656        ad: An AndroidDevice object.
657        package_name: Package name of apk.
658        apk_path: The temp path of pulled apk.
659    """
660    for path_key in os.listdir(apk_path):
661        if fnmatch.fnmatch(path_key, "*.apk"):
662            apk_path = os.path.join(apk_path, path_key)
663            break
664    else:
665        raise signals.TestError("No apk is found in %s" % apk_path)
666    ad.log.info("Re-install %s with path: %s" % (package_name, apk_path))
667    ad.adb.shell("settings put global verifier_verify_adb_installs 0")
668    ad.adb.install("-r -d -g --user 0 %s" % apk_path)
669    package_check = ad.adb.shell("pm path %s" % package_name)
670    if not package_check:
671        tutils.abort_all_tests(
672            ad.log, "%s is not properly re-installed." % package_name)
673    ad.log.info("%s is re-installed successfully." % package_name)
674
675
676def init_gtw_gpstool(ad):
677    """Init GTW_GPSTool apk.
678
679    Args:
680        ad: An AndroidDevice object.
681    """
682    remount_device(ad)
683    gpstool_path = pull_package_apk(ad, "com.android.gpstool")
684    reinstall_package_apk(ad, "com.android.gpstool", gpstool_path)
685    shutil.rmtree(gpstool_path, ignore_errors=True)
686
687
688def fastboot_factory_reset(ad, state=True):
689    """Factory reset the device in fastboot mode.
690       Pull sl4a apk from device. Terminate all sl4a sessions,
691       Reboot the device to bootloader,
692       factory reset the device by fastboot.
693       Reboot the device. wait for device to complete booting
694       Re-install and start an sl4a session.
695
696    Args:
697        ad: An AndroidDevice object.
698        State: True for exit_setup_wizard, False for not exit_setup_wizard.
699
700    Returns:
701        True if factory reset process complete.
702    """
703    status = True
704    mds_path = ""
705    gnss_cfg_file = ""
706    gnss_cfg_path = "/vendor/etc/mdlog"
707    default_gnss_cfg = "/vendor/etc/mdlog/DEFAULT+SECURITY+FULLDPL+GPS.cfg"
708    sl4a_path = pull_package_apk(ad, SL4A_APK_NAME)
709    gpstool_path = pull_package_apk(ad, "com.android.gpstool")
710    if check_chipset_vendor_by_qualcomm(ad):
711        mds_path = pull_package_apk(ad, "com.google.mdstest")
712        gnss_cfg_file = pull_gnss_cfg_file(ad, default_gnss_cfg)
713    stop_pixel_logger(ad)
714    ad.stop_services()
715    for i in range(1, 4):
716        try:
717            if ad.serial in list_adb_devices():
718                ad.log.info("Reboot to bootloader")
719                ad.adb.reboot("bootloader", ignore_status=True)
720                time.sleep(10)
721            if ad.serial in list_fastboot_devices():
722                ad.log.info("Factory reset in fastboot")
723                ad.fastboot._w(timeout=300, ignore_status=True)
724                time.sleep(30)
725                ad.log.info("Reboot in fastboot")
726                ad.fastboot.reboot()
727            ad.wait_for_boot_completion()
728            ad.root_adb()
729            if ad.skip_sl4a:
730                break
731            if ad.is_sl4a_installed():
732                break
733            if is_device_wearable(ad):
734                ad.log.info("Wait 5 mins for wearable projects system busy time.")
735                time.sleep(300)
736            reinstall_package_apk(ad, SL4A_APK_NAME, sl4a_path)
737            reinstall_package_apk(ad, "com.android.gpstool", gpstool_path)
738            if check_chipset_vendor_by_qualcomm(ad):
739                reinstall_package_apk(ad, "com.google.mdstest", mds_path)
740                ad.push_system_file(gnss_cfg_file, gnss_cfg_path)
741            time.sleep(10)
742            break
743        except Exception as e:
744            ad.log.error(e)
745            if i == attempts:
746                tutils.abort_all_tests(ad.log, str(e))
747            time.sleep(5)
748    try:
749        ad.start_adb_logcat()
750    except Exception as e:
751        ad.log.error(e)
752    if state:
753        ad.exit_setup_wizard()
754    if ad.skip_sl4a:
755        return status
756    tutils.bring_up_sl4a(ad)
757    for path in [sl4a_path, gpstool_path, mds_path, gnss_cfg_file]:
758        shutil.rmtree(path, ignore_errors=True)
759    return status
760
761
762def clear_aiding_data_by_gtw_gpstool(ad):
763    """Launch GTW GPSTool and Clear all GNSS aiding data.
764       Wait 5 seconds for GTW GPStool to clear all GNSS aiding
765       data properly.
766
767    Args:
768        ad: An AndroidDevice object.
769    """
770    if not check_chipset_vendor_by_qualcomm(ad):
771        delete_lto_file(ad)
772    ad.log.info("Launch GTW GPSTool and Clear all GNSS aiding data")
773    ad.adb.shell("am start -S -n com.android.gpstool/.GPSTool --es mode clear")
774    time.sleep(10)
775
776
777def start_gnss_by_gtw_gpstool(ad,
778                              state,
779                              type="gnss",
780                              bgdisplay=False,
781                              freq=0,
782                              lowpower=False,
783                              meas=False):
784    """Start or stop GNSS on GTW_GPSTool.
785
786    Args:
787        ad: An AndroidDevice object.
788        state: True to start GNSS. False to Stop GNSS.
789        type: Different API for location fix. Use gnss/flp/nmea
790        bgdisplay: true to run GTW when Display off. false to not run GTW when
791          Display off.
792        freq: An integer to set location update frequency.
793        meas: A Boolean to set GNSS measurement registration.
794        lowpower: A boolean to set GNSS LowPowerMode.
795    """
796    cmd = "am start -S -n com.android.gpstool/.GPSTool --es mode gps"
797    if not state:
798        ad.log.info("Stop %s on GTW_GPSTool." % type)
799        cmd = "am broadcast -a com.android.gpstool.stop_gps_action"
800    else:
801        options = ("--es type {} --ei freq {} --ez BG {} --ez meas {} --ez "
802                   "lowpower {}").format(type, freq, bgdisplay, meas, lowpower)
803        cmd = cmd + " " + options
804    ad.adb.shell(cmd)
805    time.sleep(3)
806
807
808def process_gnss_by_gtw_gpstool(ad,
809                                criteria,
810                                type="gnss",
811                                clear_data=True,
812                                meas_flag=False):
813    """Launch GTW GPSTool and Clear all GNSS aiding data
814       Start GNSS tracking on GTW_GPSTool.
815
816    Args:
817        ad: An AndroidDevice object.
818        criteria: Criteria for current test item.
819        type: Different API for location fix. Use gnss/flp/nmea
820        clear_data: True to clear GNSS aiding data. False is not to. Default
821        set to True.
822        meas_flag: True to enable GnssMeasurement. False is not to. Default
823        set to False.
824
825    Returns:
826        True: First fix TTFF are within criteria.
827        False: First fix TTFF exceed criteria.
828    """
829    retries = 3
830    for i in range(retries):
831        if not ad.is_adb_logcat_on:
832            ad.start_adb_logcat()
833        check_adblog_functionality(ad)
834        check_location_runtime_permissions(
835            ad, GNSSTOOL_PACKAGE_NAME, GNSSTOOL_PERMISSIONS)
836        begin_time = get_current_epoch_time()
837        if clear_data:
838            clear_aiding_data_by_gtw_gpstool(ad)
839        ad.log.info("Start %s on GTW_GPSTool - attempt %d" % (type.upper(),
840                                                              i+1))
841        start_gnss_by_gtw_gpstool(ad, state=True, type=type, meas=meas_flag)
842        for _ in range(10 + criteria):
843            logcat_results = ad.search_logcat("First fixed", begin_time)
844            if logcat_results:
845                ad.log.debug(logcat_results[-1]["log_message"])
846                first_fixed = int(logcat_results[-1]["log_message"].split()[-1])
847                ad.log.info("%s First fixed = %.3f seconds" %
848                            (type.upper(), first_fixed/1000))
849                if (first_fixed/1000) <= criteria:
850                    return True
851                start_gnss_by_gtw_gpstool(ad, state=False, type=type)
852                raise signals.TestFailure("Fail to get %s location fixed "
853                                          "within %d seconds criteria."
854                                          % (type.upper(), criteria))
855            time.sleep(1)
856        check_current_focus_app(ad)
857        start_gnss_by_gtw_gpstool(ad, state=False, type=type)
858    raise signals.TestFailure("Fail to get %s location fixed within %d "
859                              "attempts." % (type.upper(), retries))
860
861
862def start_ttff_by_gtw_gpstool(ad,
863                              ttff_mode,
864                              iteration,
865                              aid_data=False,
866                              raninterval=False,
867                              mininterval=10,
868                              maxinterval=40,
869                              hot_warm_sleep=300):
870    """Identify which TTFF mode for different test items.
871
872    Args:
873        ad: An AndroidDevice object.
874        ttff_mode: TTFF Test mode for current test item.
875        iteration: Iteration of TTFF cycles.
876        aid_data: Boolean for identify aid_data existed or not
877        raninterval: Boolean for identify random interval of TTFF in enable or not.
878        mininterval: Minimum value of random interval pool. The unit is second.
879        maxinterval: Maximum value of random interval pool. The unit is second.
880        hot_warm_sleep: Wait time for acquiring Almanac.
881    """
882    begin_time = get_current_epoch_time()
883    if (ttff_mode == "hs" or ttff_mode == "ws") and not aid_data:
884        ad.log.info("Wait {} seconds to start TTFF {}...".format(
885            hot_warm_sleep, ttff_mode.upper()))
886        time.sleep(hot_warm_sleep)
887    if ttff_mode == "cs":
888        ad.log.info("Start TTFF Cold Start...")
889        time.sleep(3)
890    elif ttff_mode == "csa":
891        ad.log.info("Start TTFF CSWith Assist...")
892        time.sleep(3)
893    for i in range(1, 4):
894        ad.adb.shell("am broadcast -a com.android.gpstool.ttff_action "
895                     "--es ttff {} --es cycle {}  --ez raninterval {} "
896                     "--ei mininterval {} --ei maxinterval {}".format(
897                         ttff_mode, iteration, raninterval, mininterval,
898                         maxinterval))
899        time.sleep(1)
900        if ad.search_logcat("act=com.android.gpstool.start_test_action",
901                            begin_time):
902            ad.log.info("Send TTFF start_test_action successfully.")
903            break
904    else:
905        check_current_focus_app(ad)
906        raise signals.TestError("Fail to send TTFF start_test_action.")
907
908
909def gnss_tracking_via_gtw_gpstool(ad,
910                                  criteria,
911                                  type="gnss",
912                                  testtime=60,
913                                  meas_flag=False):
914    """Start GNSS/FLP tracking tests for input testtime on GTW_GPSTool.
915
916    Args:
917        ad: An AndroidDevice object.
918        criteria: Criteria for current TTFF.
919        type: Different API for location fix. Use gnss/flp/nmea
920        testtime: Tracking test time for minutes. Default set to 60 minutes.
921        meas_flag: True to enable GnssMeasurement. False is not to. Default
922        set to False.
923    """
924    process_gnss_by_gtw_gpstool(
925        ad, criteria=criteria, type=type, meas_flag=meas_flag)
926    ad.log.info("Start %s tracking test for %d minutes" % (type.upper(),
927                                                           testtime))
928    begin_time = get_current_epoch_time()
929    while get_current_epoch_time() - begin_time < testtime * 60 * 1000:
930        detect_crash_during_tracking(ad, begin_time, type)
931    ad.log.info("Successfully tested for %d minutes" % testtime)
932    start_gnss_by_gtw_gpstool(ad, state=False, type=type)
933
934
935def parse_gtw_gpstool_log(ad, true_position, type="gnss"):
936    """Process GNSS/FLP API logs from GTW GPSTool and output track_data to
937    test_run_info for ACTS plugin to parse and display on MobileHarness as
938    Property.
939
940    Args:
941        ad: An AndroidDevice object.
942        true_position: Coordinate as [latitude, longitude] to calculate
943        position error.
944        type: Different API for location fix. Use gnss/flp/nmea
945    """
946    test_logfile = {}
947    track_data = {}
948    ant_top4_cn = 0
949    ant_cn = 0
950    base_top4_cn = 0
951    base_cn = 0
952    track_lat = 0
953    track_long = 0
954    l5flag = "false"
955    file_count = int(ad.adb.shell("find %s -type f -iname *.txt | wc -l"
956                                  % GNSSSTATUS_LOG_PATH))
957    if file_count != 1:
958        ad.log.error("%d API logs exist." % file_count)
959    dir_file = ad.adb.shell("ls %s" % GNSSSTATUS_LOG_PATH).split()
960    for path_key in dir_file:
961        if fnmatch.fnmatch(path_key, "*.txt"):
962            logpath = posixpath.join(GNSSSTATUS_LOG_PATH, path_key)
963            out = ad.adb.shell("wc -c %s" % logpath)
964            file_size = int(out.split(" ")[0])
965            if file_size < 2000:
966                ad.log.info("Skip log %s due to log size %d bytes" %
967                            (path_key, file_size))
968                continue
969            test_logfile = logpath
970    if not test_logfile:
971        raise signals.TestError("Failed to get test log file in device.")
972    lines = ad.adb.shell("cat %s" % test_logfile).split("\n")
973    for line in lines:
974        if "Antenna_History Avg Top4" in line:
975            ant_top4_cn = float(line.split(":")[-1].strip())
976        elif "Antenna_History Avg" in line:
977            ant_cn = float(line.split(":")[-1].strip())
978        elif "Baseband_History Avg Top4" in line:
979            base_top4_cn = float(line.split(":")[-1].strip())
980        elif "Baseband_History Avg" in line:
981            base_cn = float(line.split(":")[-1].strip())
982        elif "L5 used in fix" in line:
983            l5flag = line.split(":")[-1].strip()
984        elif "Latitude" in line:
985            track_lat = float(line.split(":")[-1].strip())
986        elif "Longitude" in line:
987            track_long = float(line.split(":")[-1].strip())
988        elif "Time" in line:
989            track_utc = line.split("Time:")[-1].strip()
990            if track_utc in track_data.keys():
991                continue
992            pe = calculate_position_error(track_lat, track_long, true_position)
993            track_data[track_utc] = TRACK_REPORT(l5flag=l5flag,
994                                                 pe=pe,
995                                                 ant_top4cn=ant_top4_cn,
996                                                 ant_cn=ant_cn,
997                                                 base_top4cn=base_top4_cn,
998                                                 base_cn=base_cn)
999    ad.log.debug(track_data)
1000    prop_basename = "TestResult %s_tracking_" % type.upper()
1001    time_list = sorted(track_data.keys())
1002    l5flag_list = [track_data[key].l5flag for key in time_list]
1003    pe_list = [float(track_data[key].pe) for key in time_list]
1004    ant_top4cn_list = [float(track_data[key].ant_top4cn) for key in time_list]
1005    ant_cn_list = [float(track_data[key].ant_cn) for key in time_list]
1006    base_top4cn_list = [float(track_data[key].base_top4cn) for key in time_list]
1007    base_cn_list = [float(track_data[key].base_cn) for key in time_list]
1008    ad.log.info(prop_basename+"StartTime %s" % time_list[0].replace(" ", "-"))
1009    ad.log.info(prop_basename+"EndTime %s" % time_list[-1].replace(" ", "-"))
1010    ad.log.info(prop_basename+"TotalFixPoints %d" % len(time_list))
1011    ad.log.info(prop_basename+"L5FixRate "+'{percent:.2%}'.format(
1012        percent=l5flag_list.count("true")/len(l5flag_list)))
1013    ad.log.info(prop_basename+"AvgDis %.1f" % (sum(pe_list)/len(pe_list)))
1014    ad.log.info(prop_basename+"MaxDis %.1f" % max(pe_list))
1015    ad.log.info(prop_basename+"Ant_AvgTop4Signal %.1f" % ant_top4cn_list[-1])
1016    ad.log.info(prop_basename+"Ant_AvgSignal %.1f" % ant_cn_list[-1])
1017    ad.log.info(prop_basename+"Base_AvgTop4Signal %.1f" % base_top4cn_list[-1])
1018    ad.log.info(prop_basename+"Base_AvgSignal %.1f" % base_cn_list[-1])
1019
1020
1021def process_ttff_by_gtw_gpstool(ad, begin_time, true_position, type="gnss"):
1022    """Process TTFF and record results in ttff_data.
1023
1024    Args:
1025        ad: An AndroidDevice object.
1026        begin_time: test begin time.
1027        true_position: Coordinate as [latitude, longitude] to calculate
1028        position error.
1029        type: Different API for location fix. Use gnss/flp/nmea
1030
1031    Returns:
1032        ttff_data: A dict of all TTFF data.
1033    """
1034    ttff_lat = 0
1035    ttff_lon = 0
1036    utc_time = epoch_to_human_time(get_current_epoch_time())
1037    ttff_data = {}
1038    ttff_loop_time = get_current_epoch_time()
1039    while True:
1040        if get_current_epoch_time() - ttff_loop_time >= 120000:
1041            raise signals.TestError("Fail to search specific GPSService "
1042                                    "message in logcat. Abort test.")
1043        if not ad.is_adb_logcat_on:
1044            ad.start_adb_logcat()
1045        logcat_results = ad.search_logcat("write TTFF log", ttff_loop_time)
1046        if logcat_results:
1047            ttff_loop_time = get_current_epoch_time()
1048            ttff_log = logcat_results[-1]["log_message"].split()
1049            ttff_loop = int(ttff_log[8].split(":")[-1])
1050            ttff_sec = float(ttff_log[11])
1051            if ttff_sec != 0.0:
1052                ttff_ant_cn = float(ttff_log[18].strip("]"))
1053                ttff_base_cn = float(ttff_log[25].strip("]"))
1054                if type == "gnss":
1055                    gnss_results = ad.search_logcat("GPSService: Check item",
1056                                                    begin_time)
1057                    if gnss_results:
1058                        ad.log.debug(gnss_results[-1]["log_message"])
1059                        gnss_location_log = \
1060                            gnss_results[-1]["log_message"].split()
1061                        ttff_lat = float(
1062                            gnss_location_log[8].split("=")[-1].strip(","))
1063                        ttff_lon = float(
1064                            gnss_location_log[9].split("=")[-1].strip(","))
1065                        loc_time = int(
1066                            gnss_location_log[10].split("=")[-1].strip(","))
1067                        utc_time = epoch_to_human_time(loc_time)
1068                        ttff_haccu = float(
1069                            gnss_location_log[11].split("=")[-1].strip(","))
1070                elif type == "flp":
1071                    flp_results = ad.search_logcat("GPSService: FLP Location",
1072                                                   begin_time)
1073                    if flp_results:
1074                        ad.log.debug(flp_results[-1]["log_message"])
1075                        flp_location_log = flp_results[-1][
1076                            "log_message"].split()
1077                        ttff_lat = float(flp_location_log[8].split(",")[0])
1078                        ttff_lon = float(flp_location_log[8].split(",")[1])
1079                        ttff_haccu = float(flp_location_log[9].split("=")[1])
1080                        utc_time = epoch_to_human_time(get_current_epoch_time())
1081            else:
1082                ttff_ant_cn = float(ttff_log[19].strip("]"))
1083                ttff_base_cn = float(ttff_log[26].strip("]"))
1084                ttff_lat = 0
1085                ttff_lon = 0
1086                ttff_haccu = 0
1087                utc_time = epoch_to_human_time(get_current_epoch_time())
1088            ad.log.debug("TTFF Loop %d - (Lat, Lon) = (%s, %s)" % (ttff_loop,
1089                                                                   ttff_lat,
1090                                                                   ttff_lon))
1091            ttff_pe = calculate_position_error(
1092                ttff_lat, ttff_lon, true_position)
1093            ttff_data[ttff_loop] = TTFF_REPORT(utc_time=utc_time,
1094                                               ttff_loop=ttff_loop,
1095                                               ttff_sec=ttff_sec,
1096                                               ttff_pe=ttff_pe,
1097                                               ttff_ant_cn=ttff_ant_cn,
1098                                               ttff_base_cn=ttff_base_cn,
1099                                               ttff_haccu=ttff_haccu)
1100            ad.log.info("UTC Time = %s, Loop %d = %.1f seconds, "
1101                        "Position Error = %.1f meters, "
1102                        "Antenna Average Signal = %.1f dbHz, "
1103                        "Baseband Average Signal = %.1f dbHz, "
1104                        "Horizontal Accuracy = %.1f meters" % (utc_time,
1105                                                                 ttff_loop,
1106                                                                 ttff_sec,
1107                                                                 ttff_pe,
1108                                                                 ttff_ant_cn,
1109                                                                 ttff_base_cn,
1110                                                                 ttff_haccu))
1111        stop_gps_results = ad.search_logcat("stop gps test", begin_time)
1112        if stop_gps_results:
1113            ad.send_keycode("HOME")
1114            break
1115        crash_result = ad.search_logcat("Force finishing activity "
1116                                        "com.android.gpstool/.GPSTool",
1117                                        begin_time)
1118        if crash_result:
1119            raise signals.TestError("GPSTool crashed. Abort test.")
1120        # wait 5 seconds to avoid logs not writing into logcat yet
1121        time.sleep(5)
1122    return ttff_data
1123
1124
1125def check_ttff_data(ad, ttff_data, ttff_mode, criteria):
1126    """Verify all TTFF results from ttff_data.
1127
1128    Args:
1129        ad: An AndroidDevice object.
1130        ttff_data: TTFF data of secs, position error and signal strength.
1131        ttff_mode: TTFF Test mode for current test item.
1132        criteria: Criteria for current test item.
1133
1134    Returns:
1135        True: All TTFF results are within criteria.
1136        False: One or more TTFF results exceed criteria or Timeout.
1137    """
1138    ad.log.info("%d iterations of TTFF %s tests finished."
1139                % (len(ttff_data.keys()), ttff_mode))
1140    ad.log.info("%s PASS criteria is %d seconds" % (ttff_mode, criteria))
1141    ad.log.debug("%s TTFF data: %s" % (ttff_mode, ttff_data))
1142    ttff_property_key_and_value(ad, ttff_data, ttff_mode)
1143    if len(ttff_data.keys()) == 0:
1144        ad.log.error("GTW_GPSTool didn't process TTFF properly.")
1145        return False
1146    elif any(float(ttff_data[key].ttff_sec) == 0.0 for key in ttff_data.keys()):
1147        ad.log.error("One or more TTFF %s Timeout" % ttff_mode)
1148        return False
1149    elif any(float(ttff_data[key].ttff_sec) >= criteria for key in
1150             ttff_data.keys()):
1151        ad.log.error("One or more TTFF %s are over test criteria %d seconds"
1152                     % (ttff_mode, criteria))
1153        return False
1154    ad.log.info("All TTFF %s are within test criteria %d seconds."
1155                % (ttff_mode, criteria))
1156    return True
1157
1158
1159def ttff_property_key_and_value(ad, ttff_data, ttff_mode):
1160    """Output ttff_data to test_run_info for ACTS plugin to parse and display
1161    on MobileHarness as Property.
1162
1163    Args:
1164        ad: An AndroidDevice object.
1165        ttff_data: TTFF data of secs, position error and signal strength.
1166        ttff_mode: TTFF Test mode for current test item.
1167    """
1168    prop_basename = "TestResult "+ttff_mode.replace(" ", "_")+"_TTFF_"
1169    sec_list = [float(ttff_data[key].ttff_sec) for key in ttff_data.keys()]
1170    pe_list = [float(ttff_data[key].ttff_pe) for key in ttff_data.keys()]
1171    ant_cn_list = [float(ttff_data[key].ttff_ant_cn) for key in
1172                   ttff_data.keys()]
1173    base_cn_list = [float(ttff_data[key].ttff_base_cn) for key in
1174                    ttff_data.keys()]
1175    haccu_list = [float(ttff_data[key].ttff_haccu) for key in
1176                    ttff_data.keys()]
1177    timeoutcount = sec_list.count(0.0)
1178    if len(sec_list) == timeoutcount:
1179        avgttff = 9527
1180    else:
1181        avgttff = sum(sec_list)/(len(sec_list) - timeoutcount)
1182    if timeoutcount != 0:
1183        maxttff = 9527
1184    else:
1185        maxttff = max(sec_list)
1186    avgdis = sum(pe_list)/len(pe_list)
1187    maxdis = max(pe_list)
1188    ant_avgcn = sum(ant_cn_list)/len(ant_cn_list)
1189    base_avgcn = sum(base_cn_list)/len(base_cn_list)
1190    avg_haccu = sum(haccu_list)/len(haccu_list)
1191    ad.log.info(prop_basename+"AvgTime %.1f" % avgttff)
1192    ad.log.info(prop_basename+"MaxTime %.1f" % maxttff)
1193    ad.log.info(prop_basename+"TimeoutCount %d" % timeoutcount)
1194    ad.log.info(prop_basename+"AvgDis %.1f" % avgdis)
1195    ad.log.info(prop_basename+"MaxDis %.1f" % maxdis)
1196    ad.log.info(prop_basename+"Ant_AvgSignal %.1f" % ant_avgcn)
1197    ad.log.info(prop_basename+"Base_AvgSignal %.1f" % base_avgcn)
1198    ad.log.info(prop_basename+"Avg_Horizontal_Accuracy %.1f" % avg_haccu)
1199
1200
1201def calculate_position_error(latitude, longitude, true_position):
1202    """Use haversine formula to calculate position error base on true location
1203    coordinate.
1204
1205    Args:
1206        latitude: latitude of location fixed in the present.
1207        longitude: longitude of location fixed in the present.
1208        true_position: [latitude, longitude] of true location coordinate.
1209
1210    Returns:
1211        position_error of location fixed in the present.
1212    """
1213    radius = 6371009
1214    dlat = math.radians(latitude - true_position[0])
1215    dlon = math.radians(longitude - true_position[1])
1216    a = math.sin(dlat/2) * math.sin(dlat/2) + \
1217        math.cos(math.radians(true_position[0])) * \
1218        math.cos(math.radians(latitude)) * math.sin(dlon/2) * math.sin(dlon/2)
1219    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
1220    return radius * c
1221
1222
1223def launch_google_map(ad):
1224    """Launch Google Map via intent.
1225
1226    Args:
1227        ad: An AndroidDevice object.
1228    """
1229    ad.log.info("Launch Google Map.")
1230    try:
1231        if is_device_wearable(ad):
1232            cmd = ("am start -S -n com.google.android.apps.maps/"
1233                   "com.google.android.apps.gmmwearable.MainActivity")
1234        else:
1235            cmd = ("am start -S -n com.google.android.apps.maps/"
1236                   "com.google.android.maps.MapsActivity")
1237        ad.adb.shell(cmd)
1238        ad.send_keycode("BACK")
1239        ad.force_stop_apk("com.google.android.apps.maps")
1240        ad.adb.shell(cmd)
1241    except Exception as e:
1242        ad.log.error(e)
1243        raise signals.TestError("Failed to launch google map.")
1244    check_current_focus_app(ad)
1245
1246
1247def check_current_focus_app(ad):
1248    """Check to see current focused window and app.
1249
1250    Args:
1251        ad: An AndroidDevice object.
1252    """
1253    time.sleep(1)
1254    current = ad.adb.shell(
1255        "dumpsys window | grep -E 'mCurrentFocus|mFocusedApp'")
1256    ad.log.debug("\n"+current)
1257
1258
1259def check_location_api(ad, retries):
1260    """Verify if GnssLocationProvider API reports location.
1261
1262    Args:
1263        ad: An AndroidDevice object.
1264        retries: Retry time.
1265
1266    Returns:
1267        True: GnssLocationProvider API reports location.
1268        otherwise return False.
1269    """
1270    for i in range(retries):
1271        begin_time = get_current_epoch_time()
1272        ad.log.info("Try to get location report from GnssLocationProvider API "
1273                    "- attempt %d" % (i+1))
1274        while get_current_epoch_time() - begin_time <= 30000:
1275            logcat_results = ad.search_logcat("reportLocation", begin_time)
1276            if logcat_results:
1277                ad.log.info("%s" % logcat_results[-1]["log_message"])
1278                ad.log.info("GnssLocationProvider reports location "
1279                            "successfully.")
1280                return True
1281        if not ad.is_adb_logcat_on:
1282            ad.start_adb_logcat()
1283    ad.log.error("GnssLocationProvider is unable to report location.")
1284    return False
1285
1286
1287def check_network_location(ad, retries, location_type, criteria=30):
1288    """Verify if NLP reports location after requesting via GPSTool.
1289
1290    Args:
1291        ad: An AndroidDevice object.
1292        retries: Retry time.
1293        location_type: cell or wifi.
1294        criteria: expected nlp return time, default 30 seconds
1295
1296    Returns:
1297        True: NLP reports location.
1298        otherwise return False.
1299    """
1300    criteria = criteria * 1000
1301    search_pattern = ("GPSTool : networkLocationType = %s" % location_type)
1302    for i in range(retries):
1303        begin_time = get_current_epoch_time()
1304        ad.log.info("Try to get NLP status - attempt %d" % (i+1))
1305        ad.adb.shell(
1306            "am start -S -n com.android.gpstool/.GPSTool --es mode nlp")
1307        while get_current_epoch_time() - begin_time <= criteria:
1308            # Search pattern in 1 second interval
1309            time.sleep(1)
1310            result = ad.search_logcat(search_pattern, begin_time)
1311            if result:
1312                ad.log.info("Pattern Found: %s." % result[-1]["log_message"])
1313                ad.send_keycode("BACK")
1314                return True
1315        if not ad.is_adb_logcat_on:
1316            ad.start_adb_logcat()
1317        ad.send_keycode("BACK")
1318    ad.log.error("Unable to report network location \"%s\"." % location_type)
1319    return False
1320
1321
1322def set_attenuator_gnss_signal(ad, attenuator, atten_value):
1323    """Set attenuation value for different GNSS signal.
1324
1325    Args:
1326        ad: An AndroidDevice object.
1327        attenuator: The attenuator object.
1328        atten_value: attenuation value
1329    """
1330    try:
1331        ad.log.info(
1332            "Set attenuation value to \"%d\" for GNSS signal." % atten_value)
1333        attenuator[0].set_atten(atten_value)
1334    except Exception as e:
1335        ad.log.error(e)
1336
1337
1338def set_battery_saver_mode(ad, state):
1339    """Enable or disable battery saver mode via adb.
1340
1341    Args:
1342        ad: An AndroidDevice object.
1343        state: True is enable Battery Saver mode. False is disable.
1344    """
1345    ad.root_adb()
1346    if state:
1347        ad.log.info("Enable Battery Saver mode.")
1348        ad.adb.shell("cmd battery unplug")
1349        ad.adb.shell("settings put global low_power 1")
1350    else:
1351        ad.log.info("Disable Battery Saver mode.")
1352        ad.adb.shell("settings put global low_power 0")
1353        ad.adb.shell("cmd battery reset")
1354
1355
1356def set_gnss_qxdm_mask(ad, masks):
1357    """Find defined gnss qxdm mask and set as default logging mask.
1358
1359    Args:
1360        ad: An AndroidDevice object.
1361        masks: Defined gnss qxdm mask.
1362    """
1363    try:
1364        for mask in masks:
1365            if not tlutils.find_qxdm_log_mask(ad, mask):
1366                continue
1367            tlutils.set_qxdm_logger_command(ad, mask)
1368            break
1369    except Exception as e:
1370        ad.log.error(e)
1371        raise signals.TestError("Failed to set any QXDM masks.")
1372
1373
1374def start_youtube_video(ad, url=None, retries=0):
1375    """Start youtube video and verify if audio is in music state.
1376
1377    Args:
1378        ad: An AndroidDevice object.
1379        url: Youtube video url.
1380        retries: Retry times if audio is not in music state.
1381
1382    Returns:
1383        True if youtube video is playing normally.
1384        False if youtube video is not playing properly.
1385    """
1386    for i in range(retries):
1387        ad.log.info("Open an youtube video - attempt %d" % (i+1))
1388        cmd = ("am start -n com.google.android.youtube/"
1389               "com.google.android.youtube.UrlActivity -d \"%s\"" % url)
1390        ad.adb.shell(cmd)
1391        time.sleep(2)
1392        out = ad.adb.shell(
1393            "dumpsys activity | grep NewVersionAvailableActivity")
1394        if out:
1395            ad.log.info("Skip Youtube New Version Update.")
1396            ad.send_keycode("BACK")
1397        if tutils.wait_for_state(ad.droid.audioIsMusicActive, True, 15, 1):
1398            ad.log.info("Started a video in youtube, audio is in MUSIC state")
1399            return True
1400        ad.log.info("Force-Stop youtube and reopen youtube again.")
1401        ad.force_stop_apk("com.google.android.youtube")
1402    check_current_focus_app(ad)
1403    raise signals.TestError("Started a video in youtube, "
1404                            "but audio is not in MUSIC state")
1405
1406
1407def get_baseband_and_gms_version(ad, extra_msg=""):
1408    """Get current radio baseband and GMSCore version of AndroidDevice object.
1409
1410    Args:
1411        ad: An AndroidDevice object.
1412        extra_msg: Extra message before or after the change.
1413    """
1414    mpss_version = ""
1415    brcm_gps_version = ""
1416    brcm_sensorhub_version = ""
1417    try:
1418        build_version = ad.adb.getprop("ro.build.id")
1419        baseband_version = ad.adb.getprop("gsm.version.baseband")
1420        gms_version = ad.adb.shell(
1421            "dumpsys package com.google.android.gms | grep versionName"
1422        ).split("\n")[0].split("=")[1]
1423        if check_chipset_vendor_by_qualcomm(ad):
1424            mpss_version = ad.adb.shell(
1425                "cat /sys/devices/soc0/images | grep MPSS | cut -d ':' -f 3")
1426        else:
1427            brcm_gps_version = ad.adb.shell("cat /data/vendor/gps/chip.info")
1428            sensorhub_version = ad.adb.shell(
1429                "cat /vendor/firmware/SensorHub.patch | grep ChangeList")
1430            brcm_sensorhub_version = re.compile(
1431                r'<ChangeList=(\w+)>').search(sensorhub_version).group(1)
1432        if not extra_msg:
1433            ad.log.info("TestResult Build_Version %s" % build_version)
1434            ad.log.info("TestResult Baseband_Version %s" % baseband_version)
1435            ad.log.info(
1436                "TestResult GMS_Version %s" % gms_version.replace(" ", ""))
1437            if check_chipset_vendor_by_qualcomm(ad):
1438                ad.log.info("TestResult MPSS_Version %s" % mpss_version)
1439            else:
1440                ad.log.info("TestResult GPS_Version %s" % brcm_gps_version)
1441                ad.log.info(
1442                    "TestResult SensorHub_Version %s" % brcm_sensorhub_version)
1443        else:
1444            ad.log.info(
1445                "%s, Baseband_Version = %s" % (extra_msg, baseband_version))
1446    except Exception as e:
1447        ad.log.error(e)
1448
1449
1450def start_toggle_gnss_by_gtw_gpstool(ad, iteration):
1451    """Send toggle gnss off/on start_test_action
1452
1453    Args:
1454        ad: An AndroidDevice object.
1455        iteration: Iteration of toggle gnss off/on cycles.
1456    """
1457    msg_list = []
1458    begin_time = get_current_epoch_time()
1459    try:
1460        for i in range(1, 4):
1461            ad.adb.shell("am start -S -n com.android.gpstool/.GPSTool "
1462                         "--es mode toggle --es cycle %d" % iteration)
1463            time.sleep(1)
1464            if is_device_wearable(ad):
1465                # Wait 20 seconds for Wearable low performance time.
1466                time.sleep(20)
1467                if ad.search_logcat("ToggleGPS onResume",
1468                                begin_time):
1469                    ad.log.info("Send ToggleGPS start_test_action successfully.")
1470                    break
1471            elif ad.search_logcat("cmp=com.android.gpstool/.ToggleGPS",
1472                                begin_time):
1473                ad.log.info("Send ToggleGPS start_test_action successfully.")
1474                break
1475        else:
1476            check_current_focus_app(ad)
1477            raise signals.TestError("Fail to send ToggleGPS "
1478                                    "start_test_action within 3 attempts.")
1479        time.sleep(2)
1480        if is_device_wearable(ad):
1481            test_start = ad.search_logcat("GPSService: create toggle GPS log",
1482                                      begin_time)
1483        else:
1484            test_start = ad.search_logcat("GPSTool_ToggleGPS: startService",
1485                                      begin_time)
1486        if test_start:
1487            ad.log.info(test_start[-1]["log_message"].split(":")[-1].strip())
1488        else:
1489            raise signals.TestError("Fail to start toggle GPS off/on test.")
1490        # Every iteration is expected to finish within 4 minutes.
1491        while get_current_epoch_time() - begin_time <= iteration * 240000:
1492            crash_end = ad.search_logcat("Force finishing activity "
1493                                         "com.android.gpstool/.GPSTool",
1494                                         begin_time)
1495            if crash_end:
1496                raise signals.TestError("GPSTool crashed. Abort test.")
1497            toggle_results = ad.search_logcat("GPSTool : msg", begin_time)
1498            if toggle_results:
1499                for toggle_result in toggle_results:
1500                    msg = toggle_result["log_message"]
1501                    if not msg in msg_list:
1502                        ad.log.info(msg.split(":")[-1].strip())
1503                        msg_list.append(msg)
1504                    if "timeout" in msg:
1505                        raise signals.TestFailure("Fail to get location fixed "
1506                                                  "within 60 seconds.")
1507                    if "Test end" in msg:
1508                        raise signals.TestPass("Completed quick toggle GNSS "
1509                                               "off/on test.")
1510        raise signals.TestFailure("Fail to finish toggle GPS off/on test "
1511                                  "within %d minutes" % (iteration * 4))
1512    finally:
1513        ad.send_keycode("HOME")
1514
1515
1516def grant_location_permission(ad, option):
1517    """Grant or revoke location related permission.
1518
1519    Args:
1520        ad: An AndroidDevice object.
1521        option: Boolean to grant or revoke location related permissions.
1522    """
1523    action = "grant" if option else "revoke"
1524    for permission in LOCATION_PERMISSIONS:
1525        ad.log.info(
1526            "%s permission:%s on %s" % (action, permission, TEST_PACKAGE_NAME))
1527        ad.adb.shell("pm %s %s %s" % (action, TEST_PACKAGE_NAME, permission))
1528
1529
1530def check_location_runtime_permissions(ad, package, permissions):
1531    """Check if runtime permissions are granted on selected package.
1532
1533    Args:
1534        ad: An AndroidDevice object.
1535        package: Apk package name to check.
1536        permissions: A list of permissions to be granted.
1537    """
1538    for _ in range(3):
1539        location_runtime_permission = ad.adb.shell(
1540            "dumpsys package %s | grep ACCESS_FINE_LOCATION" % package)
1541        if "true" not in location_runtime_permission:
1542            ad.log.info("ACCESS_FINE_LOCATION is NOT granted on %s" % package)
1543            for permission in permissions:
1544                ad.log.debug("Grant %s on %s" % (permission, package))
1545                ad.adb.shell("pm grant %s %s" % (package, permission))
1546        else:
1547            ad.log.info("ACCESS_FINE_LOCATION is granted on %s" % package)
1548            break
1549    else:
1550        raise signals.TestError(
1551            "Fail to grant ACCESS_FINE_LOCATION on %s" % package)
1552
1553
1554def install_mdstest_app(ad, mdsapp):
1555    """
1556        Install MDS test app in DUT
1557
1558        Args:
1559            ad: An Android Device Object
1560            mdsapp: Installation path of MDSTest app
1561    """
1562    if not ad.is_apk_installed("com.google.mdstest"):
1563        ad.adb.install("-r %s" % mdsapp, timeout=300, ignore_status=True)
1564
1565
1566def write_modemconfig(ad, mdsapp, nvitem_dict, modemparfile):
1567    """
1568        Modify the NV items using modem_tool.par
1569        Note: modem_tool.par
1570
1571        Args:
1572            ad:  An Android Device Object
1573            mdsapp: Installation path of MDSTest app
1574            nvitem_dict: dictionary of NV items and values.
1575            modemparfile: modem_tool.par path.
1576    """
1577    ad.log.info("Verify MDSTest app installed in DUT")
1578    install_mdstest_app(ad, mdsapp)
1579    os.system("chmod 777 %s" % modemparfile)
1580    for key, value in nvitem_dict.items():
1581        if key.isdigit():
1582            op_name = "WriteEFS"
1583        else:
1584            op_name = "WriteNV"
1585        ad.log.info("Modifying the NV{!r} using {}".format(key, op_name))
1586        job.run("{} --op {} --item {} --data '{}'".
1587                format(modemparfile, op_name, key, value))
1588        time.sleep(2)
1589
1590
1591def verify_modemconfig(ad, nvitem_dict, modemparfile):
1592    """
1593        Verify the NV items using modem_tool.par
1594        Note: modem_tool.par
1595
1596        Args:
1597            ad:  An Android Device Object
1598            nvitem_dict: dictionary of NV items and values
1599            modemparfile: modem_tool.par path.
1600    """
1601    os.system("chmod 777 %s" % modemparfile)
1602    for key, value in nvitem_dict.items():
1603        if key.isdigit():
1604            op_name = "ReadEFS"
1605        else:
1606            op_name = "ReadNV"
1607        # Sleeptime to avoid Modem communication error
1608        time.sleep(5)
1609        result = job.run(
1610            "{} --op {} --item {}".format(modemparfile, op_name, key))
1611        output = str(result.stdout)
1612        ad.log.info("Actual Value for NV{!r} is {!r}".format(key, output))
1613        if not value.casefold() in output:
1614            ad.log.error("NV Value is wrong {!r} in {!r}".format(value, result))
1615            raise ValueError(
1616                "could not find {!r} in {!r}".format(value, result))
1617
1618
1619def check_ttff_pe(ad, ttff_data, ttff_mode, pe_criteria):
1620    """Verify all TTFF results from ttff_data.
1621
1622    Args:
1623        ad: An AndroidDevice object.
1624        ttff_data: TTFF data of secs, position error and signal strength.
1625        ttff_mode: TTFF Test mode for current test item.
1626        pe_criteria: Criteria for current test item.
1627
1628    """
1629    ad.log.info("%d iterations of TTFF %s tests finished."
1630                % (len(ttff_data.keys()), ttff_mode))
1631    ad.log.info("%s PASS criteria is %f meters" % (ttff_mode, pe_criteria))
1632    ad.log.debug("%s TTFF data: %s" % (ttff_mode, ttff_data))
1633
1634    if len(ttff_data.keys()) == 0:
1635        ad.log.error("GTW_GPSTool didn't process TTFF properly.")
1636        raise signals.TestFailure("GTW_GPSTool didn't process TTFF properly.")
1637
1638    elif any(float(ttff_data[key].ttff_pe) >= pe_criteria for key in
1639             ttff_data.keys()):
1640        ad.log.error("One or more TTFF %s are over test criteria %f meters"
1641                     % (ttff_mode, pe_criteria))
1642        raise signals.TestFailure("GTW_GPSTool didn't process TTFF properly.")
1643    else:
1644        ad.log.info("All TTFF %s are within test criteria %f meters." % (
1645            ttff_mode, pe_criteria))
1646        return True
1647
1648
1649def check_adblog_functionality(ad):
1650    """Restart adb logcat if system can't write logs into file after checking
1651    adblog file size.
1652
1653    Args:
1654        ad: An AndroidDevice object.
1655    """
1656    logcat_path = os.path.join(ad.device_log_path, "adblog_%s_debug.txt" %
1657                               ad.serial)
1658    if not os.path.exists(logcat_path):
1659        raise signals.TestError("Logcat file %s does not exist." % logcat_path)
1660    original_log_size = os.path.getsize(logcat_path)
1661    ad.log.debug("Original adblog size is %d" % original_log_size)
1662    time.sleep(.5)
1663    current_log_size = os.path.getsize(logcat_path)
1664    ad.log.debug("Current adblog size is %d" % current_log_size)
1665    if current_log_size == original_log_size:
1666        ad.log.warn("System can't write logs into file. Restart adb "
1667                    "logcat process now.")
1668        ad.stop_adb_logcat()
1669        ad.start_adb_logcat()
1670
1671
1672def build_instrumentation_call(package,
1673                               runner,
1674                               test_methods=None,
1675                               options=None):
1676    """Build an instrumentation call for the tests
1677
1678    Args:
1679        package: A string to identify test package.
1680        runner: A string to identify test runner.
1681        test_methods: A dictionary contains {class_name, test_method}.
1682        options: A dictionary constant {key, value} param for test.
1683
1684    Returns:
1685        An instrumentation call command.
1686    """
1687    if test_methods is None:
1688        test_methods = {}
1689        cmd_builder = InstrumentationCommandBuilder()
1690    else:
1691        cmd_builder = InstrumentationTestCommandBuilder()
1692    if options is None:
1693        options = {}
1694    cmd_builder.set_manifest_package(package)
1695    cmd_builder.set_runner(runner)
1696    cmd_builder.add_flag("-w")
1697    for class_name, test_method in test_methods.items():
1698        cmd_builder.add_test_method(class_name, test_method)
1699    for option_key, option_value in options.items():
1700        cmd_builder.add_key_value_param(option_key, option_value)
1701    return cmd_builder.build()
1702
1703
1704def check_chipset_vendor_by_qualcomm(ad):
1705    """Check if chipset vendor is by Qualcomm.
1706
1707    Args:
1708        ad: An AndroidDevice object.
1709
1710    Returns:
1711        True if it's by Qualcomm. False irf not.
1712    """
1713    ad.root_adb()
1714    soc = str(ad.adb.shell("getprop gsm.version.ril-impl"))
1715    ad.log.debug("SOC = %s" % soc)
1716    return "Qualcomm" in soc
1717
1718
1719def delete_lto_file(ad):
1720    """Delete downloaded LTO files.
1721
1722    Args:
1723        ad: An AndroidDevice object.
1724    """
1725    remount_device(ad)
1726    status = ad.adb.shell("rm -rf /data/vendor/gps/lto*")
1727    ad.log.info("Delete downloaded LTO files.\n%s" % status)
1728
1729
1730def lto_mode(ad, state):
1731    """Enable or Disable LTO mode.
1732
1733    Args:
1734        ad: An AndroidDevice object.
1735        state: True to enable. False to disable.
1736    """
1737    server_list = ["LONGTERM_PSDS_SERVER_1",
1738                   "LONGTERM_PSDS_SERVER_2",
1739                   "LONGTERM_PSDS_SERVER_3",
1740                   "NORMAL_PSDS_SERVER",
1741                   "REALTIME_PSDS_SERVER"]
1742    delete_lto_file(ad)
1743    if state:
1744        tmp_path = tempfile.mkdtemp()
1745        ad.pull_files("/etc/gps_debug.conf", tmp_path)
1746        gps_conf_path = os.path.join(tmp_path, "gps_debug.conf")
1747        gps_conf_file = open(gps_conf_path, "r")
1748        lines = gps_conf_file.readlines()
1749        gps_conf_file.close()
1750        fout = open(gps_conf_path, "w")
1751        for line in lines:
1752            for server in server_list:
1753                if server in line:
1754                    line = line.replace(line, "")
1755            fout.write(line)
1756        fout.close()
1757        ad.push_system_file(gps_conf_path, "/etc/gps_debug.conf")
1758        ad.log.info("Push back modified gps_debug.conf")
1759        ad.log.info("LTO/RTO/RTI enabled")
1760        shutil.rmtree(tmp_path, ignore_errors=True)
1761    else:
1762        ad.adb.shell("echo %r >> /etc/gps_debug.conf" %
1763                     DISABLE_LTO_FILE_CONTENTS)
1764        ad.log.info("LTO/RTO/RTI disabled")
1765    reboot(ad)
1766
1767
1768def lto_mode_wearable(ad, state):
1769    """Enable or Disable LTO mode for wearable in Android R release.
1770
1771    Args:
1772        ad: An AndroidDevice object.
1773        state: True to enable. False to disable.
1774    """
1775    rto_enable = '    RtoEnable="true"\n'
1776    rto_disable = '    RtoEnable="false"\n'
1777    rti_enable = '    RtiEnable="true"\n'
1778    rti_disable = '    RtiEnable="false"\n'
1779    sync_lto_enable = '    HttpDirectSyncLto="true"\n'
1780    sync_lto_disable = '    HttpDirectSyncLto="false"\n'
1781    server_list = ["XTRA_SERVER_1", "XTRA_SERVER_2", "XTRA_SERVER_3"]
1782    delete_lto_file(ad)
1783    tmp_path = tempfile.mkdtemp()
1784    ad.pull_files("/vendor/etc/gnss/gps.xml", tmp_path)
1785    gps_xml_path = os.path.join(tmp_path, "gps.xml")
1786    gps_xml_file = open(gps_xml_path, "r")
1787    lines = gps_xml_file.readlines()
1788    gps_xml_file.close()
1789    fout = open(gps_xml_path, "w")
1790    for line in lines:
1791        if state:
1792            if rto_disable in line:
1793                line = line.replace(line, rto_enable)
1794                ad.log.info("RTO enabled")
1795            elif rti_disable in line:
1796                line = line.replace(line, rti_enable)
1797                ad.log.info("RTI enabled")
1798            elif sync_lto_disable in line:
1799                line = line.replace(line, sync_lto_enable)
1800                ad.log.info("LTO sync enabled")
1801        else:
1802            if rto_enable in line:
1803                line = line.replace(line, rto_disable)
1804                ad.log.info("RTO disabled")
1805            elif rti_enable in line:
1806                line = line.replace(line, rti_disable)
1807                ad.log.info("RTI disabled")
1808            elif sync_lto_enable in line:
1809                line = line.replace(line, sync_lto_disable)
1810                ad.log.info("LTO sync disabled")
1811        fout.write(line)
1812    fout.close()
1813    ad.push_system_file(gps_xml_path, "/vendor/etc/gnss/gps.xml")
1814    ad.log.info("Push back modified gps.xml")
1815    shutil.rmtree(tmp_path, ignore_errors=True)
1816    if state:
1817        xtra_tmp_path = tempfile.mkdtemp()
1818        ad.pull_files("/etc/gps_debug.conf", xtra_tmp_path)
1819        gps_conf_path = os.path.join(xtra_tmp_path, "gps_debug.conf")
1820        gps_conf_file = open(gps_conf_path, "r")
1821        lines = gps_conf_file.readlines()
1822        gps_conf_file.close()
1823        fout = open(gps_conf_path, "w")
1824        for line in lines:
1825            for server in server_list:
1826                if server in line:
1827                    line = line.replace(line, "")
1828            fout.write(line)
1829        fout.close()
1830        ad.push_system_file(gps_conf_path, "/etc/gps_debug.conf")
1831        ad.log.info("Push back modified gps_debug.conf")
1832        ad.log.info("LTO/RTO/RTI enabled")
1833        shutil.rmtree(xtra_tmp_path, ignore_errors=True)
1834    else:
1835        ad.adb.shell(
1836            "echo %r >> /etc/gps_debug.conf" % DISABLE_LTO_FILE_CONTENTS_R)
1837        ad.log.info("LTO/RTO/RTI disabled")
1838
1839
1840def start_pixel_logger(ad, max_log_size_mb=100, max_number_of_files=500):
1841    """adb to start pixel logger for GNSS logging.
1842
1843    Args:
1844        ad: An AndroidDevice object.
1845        max_log_size_mb: Determines when to create a new log file if current
1846            one reaches the size limit.
1847        max_number_of_files: Determines how many log files can be saved on DUT.
1848    """
1849    retries = 3
1850    start_timeout_sec = 60
1851    default_gnss_cfg = "/vendor/etc/mdlog/DEFAULT+SECURITY+FULLDPL+GPS.cfg"
1852    if check_chipset_vendor_by_qualcomm(ad):
1853        start_cmd = ("am startservice -a com.android.pixellogger."
1854                     "service.logging.LoggingService.ACTION_START_LOGGING "
1855                     "-e intent_key_cfg_path '%s' "
1856                     "--ei intent_key_max_log_size_mb %d "
1857                     "--ei intent_key_max_number_of_files %d" %
1858                     (default_gnss_cfg, max_log_size_mb, max_number_of_files))
1859    else:
1860        start_cmd = ("am startservice -a com.android.pixellogger."
1861                     "service.logging.LoggingService.ACTION_START_LOGGING "
1862                     "-e intent_logger brcm_gps "
1863                     "--ei intent_key_max_log_size_mb %d "
1864                     "--ei intent_key_max_number_of_files %d" %
1865                     (max_log_size_mb, max_number_of_files))
1866    for attempt in range(retries):
1867        begin_time = get_current_epoch_time() - 3000
1868        ad.log.info("Start Pixel Logger - Attempt %d" % (attempt + 1))
1869        ad.adb.shell(start_cmd)
1870        while get_current_epoch_time() - begin_time <= start_timeout_sec * 1000:
1871            if not ad.is_adb_logcat_on:
1872                ad.start_adb_logcat()
1873            if check_chipset_vendor_by_qualcomm(ad):
1874                start_result = ad.search_logcat(
1875                    "ModemLogger: Start logging", begin_time)
1876            else:
1877                start_result = ad.search_logcat("startRecording", begin_time)
1878            if start_result:
1879                ad.log.info("Pixel Logger starts recording successfully.")
1880                return True
1881        stop_pixel_logger(ad)
1882    else:
1883        ad.log.warn("Pixel Logger fails to start recording in %d seconds "
1884                    "within %d attempts." % (start_timeout_sec, retries))
1885
1886
1887def stop_pixel_logger(ad):
1888    """adb to stop pixel logger for GNSS logging.
1889
1890    Args:
1891        ad: An AndroidDevice object.
1892    """
1893    retries = 3
1894    stop_timeout_sec = 60
1895    zip_timeout_sec = 30
1896    if check_chipset_vendor_by_qualcomm(ad):
1897        stop_cmd = ("am startservice -a com.android.pixellogger."
1898                    "service.logging.LoggingService.ACTION_STOP_LOGGING")
1899    else:
1900        stop_cmd = ("am startservice -a com.android.pixellogger."
1901                    "service.logging.LoggingService.ACTION_STOP_LOGGING "
1902                    "-e intent_logger brcm_gps")
1903    for attempt in range(retries):
1904        begin_time = get_current_epoch_time() - 3000
1905        ad.log.info("Stop Pixel Logger - Attempt %d" % (attempt + 1))
1906        ad.adb.shell(stop_cmd)
1907        while get_current_epoch_time() - begin_time <= stop_timeout_sec * 1000:
1908            if not ad.is_adb_logcat_on:
1909                ad.start_adb_logcat()
1910            stop_result = ad.search_logcat(
1911                "LoggingService: Stopping service", begin_time)
1912            if stop_result:
1913                ad.log.info("Pixel Logger stops successfully.")
1914                zip_end_time = time.time() + zip_timeout_sec
1915                while time.time() < zip_end_time:
1916                    zip_file_created = ad.search_logcat(
1917                        "FileUtil: Zip file has been created", begin_time)
1918                    if zip_file_created:
1919                        ad.log.info("Pixel Logger created zip file "
1920                                    "successfully.")
1921                        return True
1922                else:
1923                    ad.log.warn("Pixel Logger failed to create zip file.")
1924                    return False
1925        ad.force_stop_apk("com.android.pixellogger")
1926    else:
1927        ad.log.warn("Pixel Logger fails to stop in %d seconds within %d "
1928                    "attempts." % (stop_timeout_sec, retries))
1929
1930
1931def launch_eecoexer(ad):
1932    """Launch EEcoexer.
1933
1934    Args:
1935        ad: An AndroidDevice object.
1936    Raise:
1937        signals.TestError if DUT fails to launch EEcoexer
1938    """
1939    launch_cmd = ("am start -a android.intent.action.MAIN -n"
1940                  "com.google.eecoexer"
1941                  "/.MainActivity")
1942    ad.adb.shell(launch_cmd)
1943    try:
1944        ad.log.info("Launch EEcoexer.")
1945    except Exception as e:
1946        ad.log.error(e)
1947        raise signals.TestError("Failed to launch EEcoexer.")
1948
1949
1950def excute_eecoexer_function(ad, eecoexer_args):
1951    """Execute EEcoexer commands.
1952
1953    Args:
1954        ad: An AndroidDevice object.
1955        eecoexer_args: EEcoexer function arguments
1956    """
1957    cat_index = eecoexer_args.split(',')[:2]
1958    cat_index = ','.join(cat_index)
1959    enqueue_cmd = ("am broadcast -a com.google.eecoexer.action.LISTENER"
1960                   " --es sms_body ENQUEUE,{}".format(eecoexer_args))
1961    exe_cmd = ("am broadcast -a com.google.eecoexer.action.LISTENER"
1962               " --es sms_body EXECUTE")
1963    wait_for_cmd = ("am broadcast -a com.google.eecoexer.action.LISTENER"
1964                   " --es sms_body WAIT_FOR_COMPLETE,{}".format(cat_index))
1965    ad.log.info("EEcoexer Add Enqueue: {}".format(eecoexer_args))
1966    ad.adb.shell(enqueue_cmd)
1967    ad.log.info("EEcoexer Excute.")
1968    ad.adb.shell(exe_cmd)
1969    ad.log.info("Wait EEcoexer for complete")
1970    ad.adb.shell(wait_for_cmd)
1971
1972
1973def restart_gps_daemons(ad):
1974    """Restart GPS daemons by killing services of gpsd, lhd and scd.
1975
1976    Args:
1977        ad: An AndroidDevice object.
1978    """
1979    gps_daemons_list = ["gpsd", "lhd", "scd"]
1980    ad.root_adb()
1981    for service in gps_daemons_list:
1982        begin_time = get_current_epoch_time()
1983        time.sleep(3)
1984        ad.log.info("Kill GPS daemon \"%s\"" % service)
1985        ad.adb.shell("killall %s" % service)
1986        # Wait 3 seconds for daemons and services to start.
1987        time.sleep(3)
1988        restart_services = ad.search_logcat("starting service", begin_time)
1989        for restart_service in restart_services:
1990            if service in restart_service["log_message"]:
1991                ad.log.info(restart_service["log_message"])
1992                ad.log.info(
1993                    "GPS daemon \"%s\" restarts successfully." % service)
1994                break
1995        else:
1996            raise signals.TestError("Unable to restart \"%s\"" % service)
1997
1998
1999def is_device_wearable(ad):
2000    """Check device is wearable project or not.
2001
2002    Args:
2003        ad: An AndroidDevice object.
2004    """
2005    package = ad.adb.getprop("ro.cw.home_package_names")
2006    ad.log.debug("[ro.cw.home_package_names]: [%s]" % package)
2007    return "wearable" in package
2008
2009
2010def is_mobile_data_on(ad):
2011    """Check if mobile data of device is on.
2012
2013    Args:
2014        ad: An AndroidDevice object.
2015    """
2016    if is_device_wearable(ad):
2017        cell_on = ad.adb.shell("settings get global cell_on")
2018        ad.log.debug("Current mobile status is %s" % cell_on)
2019        return "1" in cell_on
2020    else:
2021        return ad.droid.telephonyIsDataEnabled()
2022
2023
2024def human_to_epoch_time(human_time):
2025    """Convert human readable time to epoch time.
2026
2027    Args:
2028        human_time: Human readable time. (Ex: 2020-08-04 13:24:28.900)
2029
2030    Returns:
2031        epoch: Epoch time in milliseconds.
2032    """
2033    if "/" in human_time:
2034        human_time.replace("/", "-")
2035    try:
2036        epoch_start = datetime.utcfromtimestamp(0)
2037        if "." in human_time:
2038            epoch_time = datetime.strptime(human_time, "%Y-%m-%d %H:%M:%S.%f")
2039        else:
2040            epoch_time = datetime.strptime(human_time, "%Y-%m-%d %H:%M:%S")
2041        epoch = int((epoch_time - epoch_start).total_seconds() * 1000)
2042        return epoch
2043    except ValueError:
2044        return None
2045
2046
2047def check_dpo_rate_via_gnss_meas(ad, begin_time, dpo_threshold):
2048    """Check DPO engage rate through "HardwareClockDiscontinuityCount" in
2049    GnssMeasurement callback.
2050
2051    Args:
2052        ad: An AndroidDevice object.
2053        begin_time: test begin time.
2054        dpo_threshold: The value to set threshold. (Ex: dpo_threshold = 60)
2055    """
2056    time_regex = r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3})'
2057    dpo_results = ad.search_logcat("HardwareClockDiscontinuityCount",
2058                                   begin_time)
2059    if not dpo_results:
2060        raise signals.TestError(
2061            "No \"HardwareClockDiscontinuityCount\" is found in logs.")
2062    ad.log.info(dpo_results[0]["log_message"])
2063    ad.log.info(dpo_results[-1]["log_message"])
2064    start_time = re.compile(
2065        time_regex).search(dpo_results[0]["log_message"]).group(1)
2066    end_time = re.compile(
2067        time_regex).search(dpo_results[-1]["log_message"]).group(1)
2068    gnss_start_epoch = human_to_epoch_time(start_time)
2069    gnss_stop_epoch = human_to_epoch_time(end_time)
2070    test_time_in_sec = round((gnss_stop_epoch - gnss_start_epoch) / 1000) + 1
2071    first_dpo_count = int(dpo_results[0]["log_message"].split()[-1])
2072    final_dpo_count = int(dpo_results[-1]["log_message"].split()[-1])
2073    dpo_rate = ((final_dpo_count - first_dpo_count)/test_time_in_sec)
2074    dpo_engage_rate = "{percent:.2%}".format(percent=dpo_rate)
2075    ad.log.info("DPO is ON for %d seconds during %d seconds test." % (
2076        final_dpo_count - first_dpo_count, test_time_in_sec))
2077    ad.log.info("TestResult DPO_Engage_Rate " + dpo_engage_rate)
2078    threshold = "{percent:.0%}".format(percent=dpo_threshold / 100)
2079    asserts.assert_true(dpo_rate * 100 > dpo_threshold,
2080                        "DPO only engaged %s in %d seconds test with "
2081                        "threshold %s." % (dpo_engage_rate,
2082                                           test_time_in_sec,
2083                                           threshold))
2084
2085
2086def parse_brcm_nmea_log(ad, nmea_pattern, brcm_error_log_allowlist):
2087    """Parse specific NMEA pattern out of BRCM NMEA log.
2088
2089    Args:
2090        ad: An AndroidDevice object.
2091        nmea_pattern: Specific NMEA pattern to parse.
2092        brcm_error_log_allowlist: Benign error logs to exclude.
2093
2094    Returns:
2095        brcm_log_list: A list of specific NMEA pattern logs.
2096    """
2097    brcm_log_list = []
2098    brcm_log_error_pattern = ["lhd: FS: Start Failsafe dump", "E slog"]
2099    brcm_error_log_list = []
2100    stop_pixel_logger(ad)
2101    pixellogger_path = (
2102        "/sdcard/Android/data/com.android.pixellogger/files/logs/gps/.")
2103    tmp_log_path = tempfile.mkdtemp()
2104    ad.pull_files(pixellogger_path, tmp_log_path)
2105    for path_key in os.listdir(tmp_log_path):
2106        zip_path = posixpath.join(tmp_log_path, path_key)
2107        if path_key.endswith(".zip"):
2108            ad.log.info("Processing zip file: {}".format(zip_path))
2109            with zipfile.ZipFile(zip_path, "r") as zip_file:
2110                zip_file.extractall(tmp_log_path)
2111                gl_logs = zip_file.namelist()
2112                # b/214145973 check if hidden exists in pixel logger zip file
2113                tmp_file = [name for name in gl_logs if 'tmp' in name]
2114                if tmp_file:
2115                    ad.log.warn(f"Hidden file {tmp_file} exists in pixel logger zip file")
2116            break
2117        elif os.path.isdir(zip_path):
2118            ad.log.info("BRCM logs didn't zip properly. Log path is directory.")
2119            tmp_log_path = zip_path
2120            gl_logs = os.listdir(tmp_log_path)
2121            ad.log.info("Processing BRCM log files: {}".format(gl_logs))
2122            break
2123    else:
2124        raise signals.TestError(
2125            "No BRCM logs found in {}".format(os.listdir(tmp_log_path)))
2126    gl_logs = [log for log in gl_logs
2127               if log.startswith("gl") and log.endswith(".log")]
2128    for file in gl_logs:
2129        nmea_log_path = posixpath.join(tmp_log_path, file)
2130        ad.log.info("Parsing log pattern of \"%s\" in %s" % (nmea_pattern,
2131                                                             nmea_log_path))
2132        brcm_log = open(nmea_log_path, "r", encoding="UTF-8", errors="ignore")
2133        lines = brcm_log.readlines()
2134        for line in lines:
2135            if nmea_pattern in line:
2136                brcm_log_list.append(line)
2137            for attr in brcm_log_error_pattern:
2138                if attr in line:
2139                    benign_log = False
2140                    for allow_log in brcm_error_log_allowlist:
2141                        if allow_log in line:
2142                            benign_log = True
2143                            ad.log.info("\"%s\" is in allow-list and removed "
2144                                        "from error." % allow_log)
2145                    if not benign_log:
2146                        brcm_error_log_list.append(line)
2147    brcm_error_log = "".join(brcm_error_log_list)
2148    shutil.rmtree(tmp_log_path, ignore_errors=True)
2149    return brcm_log_list, brcm_error_log
2150
2151
2152def check_dpo_rate_via_brcm_log(ad, dpo_threshold, brcm_error_log_allowlist):
2153    """Check DPO engage rate through "$PGLOR,11,STA" in BRCM Log.
2154    D - Disabled, Always full power.
2155    F - Enabled, now in full power mode.
2156    S - Enabled, now in power save mode.
2157    H - Host off load mode.
2158
2159    Args:
2160        ad: An AndroidDevice object.
2161        dpo_threshold: The value to set threshold. (Ex: dpo_threshold = 60)
2162        brcm_error_log_allowlist: Benign error logs to exclude.
2163    """
2164    always_full_power_count = 0
2165    full_power_count = 0
2166    power_save_count = 0
2167    pglor_list, brcm_error_log = parse_brcm_nmea_log(
2168        ad, "$PGLOR,11,STA", brcm_error_log_allowlist)
2169    if not pglor_list:
2170        raise signals.TestFailure("Fail to get DPO logs from pixel logger")
2171
2172    for pglor in pglor_list:
2173        power_res = re.compile(r',P,(\w),').search(pglor).group(1)
2174        if power_res == "D":
2175            always_full_power_count += 1
2176        elif power_res == "F":
2177            full_power_count += 1
2178        elif power_res == "S":
2179            power_save_count += 1
2180    ad.log.info(sorted(pglor_list)[0])
2181    ad.log.info(sorted(pglor_list)[-1])
2182    ad.log.info("TestResult Total_Count %d" % len(pglor_list))
2183    ad.log.info("TestResult Always_Full_Power_Count %d" %
2184                always_full_power_count)
2185    ad.log.info("TestResult Full_Power_Mode_Count %d" % full_power_count)
2186    ad.log.info("TestResult Power_Save_Mode_Count %d" % power_save_count)
2187    dpo_rate = (power_save_count / len(pglor_list))
2188    dpo_engage_rate = "{percent:.2%}".format(percent=dpo_rate)
2189    ad.log.info("Power Save Mode is ON for %d seconds during %d seconds test."
2190                % (power_save_count, len(pglor_list)))
2191    ad.log.info("TestResult DPO_Engage_Rate " + dpo_engage_rate)
2192    threshold = "{percent:.0%}".format(percent=dpo_threshold / 100)
2193    asserts.assert_true((dpo_rate * 100 > dpo_threshold) and not brcm_error_log,
2194                        "Power Save Mode only engaged %s in %d seconds test "
2195                        "with threshold %s.\nAbnormal behavior found as below."
2196                        "\n%s" % (dpo_engage_rate,
2197                                  len(pglor_list),
2198                                  threshold,
2199                                  brcm_error_log))
2200
2201
2202def pair_to_wearable(ad, ad1):
2203    """Pair phone to watch via Bluetooth.
2204
2205    Args:
2206        ad: A pixel phone.
2207        ad1: A wearable project.
2208    """
2209    check_location_service(ad1)
2210    utils.sync_device_time(ad1)
2211    bt_model_name = ad.adb.getprop("ro.product.model")
2212    bt_sn_name = ad.adb.getprop("ro.serialno")
2213    bluetooth_name = bt_model_name +" " + bt_sn_name[10:]
2214    fastboot_factory_reset(ad, False)
2215    ad.log.info("Wait 1 min for wearable system busy time.")
2216    time.sleep(60)
2217    ad.adb.shell("input keyevent 4")
2218    # Clear Denali paired data in phone.
2219    ad1.adb.shell("pm clear com.google.android.gms")
2220    ad1.adb.shell("pm clear com.google.android.apps.wear.companion")
2221    ad1.adb.shell("am start -S -n com.google.android.apps.wear.companion/"
2222                        "com.google.android.apps.wear.companion.application.RootActivity")
2223    uia_click(ad1, "Next")
2224    uia_click(ad1, "I agree")
2225    uia_click(ad1, bluetooth_name)
2226    uia_click(ad1, "Pair")
2227    uia_click(ad1, "Skip")
2228    uia_click(ad1, "Skip")
2229    uia_click(ad1, "Finish")
2230    ad.log.info("Wait 3 mins for complete pairing process.")
2231    time.sleep(180)
2232    ad.adb.shell("settings put global stay_on_while_plugged_in 7")
2233    check_location_service(ad)
2234    enable_gnss_verbose_logging(ad)
2235    if is_bluetooth_connected(ad, ad1):
2236        ad.log.info("Pairing successfully.")
2237    else:
2238        raise signals.TestFailure("Fail to pair watch and phone successfully.")
2239
2240
2241def is_bluetooth_connected(ad, ad1):
2242    """Check if device's Bluetooth status is connected or not.
2243
2244    Args:
2245    ad: A wearable project
2246    ad1: A pixel phone.
2247    """
2248    return ad.droid.bluetoothIsDeviceConnected(ad1.droid.bluetoothGetLocalAddress())
2249
2250
2251def detect_crash_during_tracking(ad, begin_time, type):
2252    """Check if GNSS or GPSTool crash happened druing GNSS Tracking.
2253
2254    Args:
2255    ad: An AndroidDevice object.
2256    begin_time: Start Time to check if crash happened in logs.
2257    type: Using GNSS or FLP reading method in GNSS tracking.
2258    """
2259    gnss_crash_list = [".*Fatal signal.*gnss",
2260                       ".*Fatal signal.*xtra",
2261                       ".*F DEBUG.*gnss",
2262                       ".*Fatal signal.*gpsd"]
2263    if not ad.is_adb_logcat_on:
2264        ad.start_adb_logcat()
2265    for attr in gnss_crash_list:
2266        gnss_crash_result = ad.adb.shell(
2267            "logcat -d | grep -E -i '%s'" % attr)
2268        if gnss_crash_result:
2269            start_gnss_by_gtw_gpstool(ad, state=False, type=type)
2270            raise signals.TestFailure(
2271                "Test failed due to GNSS HAL crashed. \n%s" %
2272                gnss_crash_result)
2273    gpstool_crash_result = ad.search_logcat("Force finishing activity "
2274                                            "com.android.gpstool/.GPSTool",
2275                                            begin_time)
2276    if gpstool_crash_result:
2277            raise signals.TestError("GPSTool crashed. Abort test.")
2278
2279
2280def is_wearable_btwifi(ad):
2281    """Check device is wearable btwifi sku or not.
2282
2283    Args:
2284        ad: An AndroidDevice object.
2285    """
2286    package = ad.adb.getprop("ro.product.product.name")
2287    ad.log.debug("[ro.product.product.name]: [%s]" % package)
2288    return "btwifi" in package
2289
2290
2291def compare_watch_phone_location(ad,watch_file, phone_file):
2292    """Compare watch and phone's FLP location to see if the same or not.
2293
2294    Args:
2295        ad: An AndroidDevice object.
2296        watch_file: watch's FLP locations
2297        phone_file: phone's FLP locations
2298    """
2299    not_match_location_counts = 0
2300    not_match_location = []
2301    for watch_key, watch_value in watch_file.items():
2302        if phone_file.get(watch_key):
2303            lat_ads = abs(float(watch_value[0]) - float(phone_file[watch_key][0]))
2304            lon_ads = abs(float(watch_value[1]) - float(phone_file[watch_key][1]))
2305            if lat_ads > 0.000002 or lon_ads > 0.000002:
2306                not_match_location_counts += 1
2307                not_match_location += (watch_key, watch_value, phone_file[watch_key])
2308    if not_match_location_counts > 0:
2309        ad.log.info("There are %s not match locations: %s" %(not_match_location_counts, not_match_location))
2310        ad.log.info("Watch's locations are not using Phone's locations.")
2311        return False
2312    else:
2313        ad.log.info("Watch's locations are using Phone's location.")
2314        return True
2315
2316
2317def check_tracking_file(ad):
2318    """Check tracking file in device and save "Latitude", "Longitude", and "Time" information.
2319
2320    Args:
2321        ad: An AndroidDevice object.
2322
2323    Returns:
2324        location_reports: A dict with [latitude, longitude]
2325    """
2326    location_reports = dict()
2327    test_logfile = {}
2328    file_count = int(ad.adb.shell("find %s -type f -iname *.txt | wc -l"
2329                                  % GNSSSTATUS_LOG_PATH))
2330    if file_count != 1:
2331        ad.log.error("%d API logs exist." % file_count)
2332    dir_file = ad.adb.shell("ls %s" % GNSSSTATUS_LOG_PATH).split()
2333    for path_key in dir_file:
2334        if fnmatch.fnmatch(path_key, "*.txt"):
2335            logpath = posixpath.join(GNSSSTATUS_LOG_PATH, path_key)
2336            out = ad.adb.shell("wc -c %s" % logpath)
2337            file_size = int(out.split(" ")[0])
2338            if file_size < 10:
2339                ad.log.info("Skip log %s due to log size %d bytes" %
2340                            (path_key, file_size))
2341                continue
2342            test_logfile = logpath
2343    if not test_logfile:
2344        raise signals.TestError("Failed to get test log file in device.")
2345    lines = ad.adb.shell("cat %s" % test_logfile).split("\n")
2346    for file_data in lines:
2347        if "Latitude:" in file_data:
2348            file_lat = ("%.6f" %float(file_data[9:]))
2349        elif "Longitude:" in file_data:
2350            file_long = ("%.6f" %float(file_data[11:]))
2351        elif "Time:" in file_data:
2352            file_time = (file_data[17:25])
2353            location_reports[file_time] = [file_lat, file_long]
2354    return location_reports
2355
2356
2357def uia_click(ad, matching_text):
2358    """Use uiautomator to click objects.
2359
2360    Args:
2361        ad: An AndroidDevice object.
2362        matching_text: Text of the target object to click
2363    """
2364    if ad.uia(textMatches=matching_text).wait.exists(timeout=60000):
2365
2366        ad.uia(textMatches=matching_text).click()
2367        ad.log.info("Click button %s" % matching_text)
2368    else:
2369        ad.log.error("No button named %s" % matching_text)
2370
2371
2372def delete_bcm_nvmem_sto_file(ad):
2373    """Delete BCM's NVMEM ephemeris gldata.sto.
2374
2375    Args:
2376        ad: An AndroidDevice object.
2377    """
2378    remount_device(ad)
2379    rm_cmd = "rm -rf {}".format(BCM_NVME_STO_PATH)
2380    status = ad.adb.shell(rm_cmd)
2381    ad.log.info("Delete BCM's NVMEM ephemeris files.\n%s" % status)
2382
2383
2384def bcm_gps_xml_add_option(ad,
2385                           search_line=None,
2386                           append_txt=None,
2387                           gps_xml_path=BCM_GPS_XML_PATH):
2388    """Append parameter setting in gps.xml for BCM solution
2389
2390    Args:
2391        ad: An AndroidDevice object.
2392        search_line: Pattern matching of target
2393        line for appending new line data.
2394        append_txt: New line that will be appended after the search_line.
2395        gps_xml_path: gps.xml file location of DUT
2396    """
2397    remount_device(ad)
2398    #Update gps.xml
2399    if not search_line or not append_txt:
2400        ad.log.info("Nothing for update.")
2401    else:
2402        tmp_log_path = tempfile.mkdtemp()
2403        ad.pull_files(gps_xml_path, tmp_log_path)
2404        gps_xml_tmp_path = os.path.join(tmp_log_path, "gps.xml")
2405        gps_xml_file = open(gps_xml_tmp_path, "r")
2406        lines = gps_xml_file.readlines()
2407        gps_xml_file.close()
2408        fout = open(gps_xml_tmp_path, "w")
2409        append_txt_tag = append_txt.strip()
2410        for line in lines:
2411            if append_txt_tag in line:
2412                ad.log.info('{} is already in the file. Skip'.format(append_txt))
2413                continue
2414            fout.write(line)
2415            if search_line in line:
2416                fout.write(append_txt)
2417                ad.log.info("Update new line: '{}' in gps.xml.".format(append_txt))
2418        fout.close()
2419
2420        # Update gps.xml with gps_new.xml
2421        ad.push_system_file(gps_xml_tmp_path, gps_xml_path)
2422
2423        # remove temp folder
2424        shutil.rmtree(tmp_log_path, ignore_errors=True)
2425
2426
2427def bcm_gps_ignore_rom_alm(ad):
2428    """ Update BCM gps.xml with ignoreRomAlm="True"
2429    Args:
2430        ad: An AndroidDevice object.
2431    """
2432    search_line_tag = '<gll\n'
2433    append_line_str = '       IgnoreRomAlm=\"true\"\n'
2434    bcm_gps_xml_add_option(ad, search_line_tag, append_line_str)
2435
2436
2437def check_inject_time(ad):
2438    """Check if watch could get the UTC time.
2439
2440    Args:
2441        ad: An AndroidDevice object.
2442    """
2443    for i in range(1, 6):
2444        time.sleep(10)
2445        inject_time_results = ad.search_logcat("GPSIC.OUT.gps_inject_time")
2446        ad.log.info("Check time injected - attempt %s" % i)
2447        if inject_time_results:
2448            ad.log.info("Time is injected successfully.")
2449            return True
2450    raise signals.TestFailure("Fail to get time injected within %s attempts." % i)
2451
2452
2453def enable_framework_log(ad):
2454    """Enable framework log for wearable to check UTC time download.
2455
2456    Args:
2457        ad: An AndroidDevice object.
2458    """
2459    remount_device(ad)
2460    time.sleep(3)
2461    ad.log.info("Start to enable framwork log for wearable.")
2462    ad.adb.shell("echo 'log.tag.LocationManagerService=VERBOSE' >> /data/local.prop")
2463    ad.adb.shell("echo 'log.tag.GnssLocationProvider=VERBOSE' >> /data/local.prop")
2464    ad.adb.shell("echo 'log.tag.GpsNetInitiatedHandler=VERBOSE' >> /data/local.prop")
2465    ad.adb.shell("echo 'log.tag.GnssNetInitiatedHandler=VERBOSE' >> /data/local.prop")
2466    ad.adb.shell("echo 'log.tag.GnssNetworkConnectivityHandler=VERBOSE' >> /data/local.prop")
2467    ad.adb.shell("echo 'log.tag.NtpTimeHelper=VERBOSE' >> /data/local.prop")
2468    ad.adb.shell("echo 'log.tag.ConnectivityService=VERBOSE' >> /data/local.prop")
2469    ad.adb.shell("echo 'log.tag.GnssPsdsDownloader=VERBOSE' >> /data/local.prop")
2470    ad.adb.shell("echo 'log.tag.GnssVisibilityControl=VERBOSE'  >> /data/local.prop")
2471    ad.adb.shell("echo 'log.tag.Gnss=VERBOSE' >> /data/local.prop")
2472    ad.adb.shell("echo 'log.tag.GnssConfiguration=VERBOSE' >> /data/local.prop")
2473    ad.adb.shell("echo 'log.tag.ImsPhone=VERBOSE' >> /data/local.prop")
2474    ad.adb.shell("echo 'log.tag.GsmCdmaPhone=VERBOSE' >> /data/local.prop")
2475    ad.adb.shell("echo 'log.tag.Phone=VERBOSE' >> /data/local.prop")
2476    ad.adb.shell("echo 'log.tag.GCoreFlp=VERBOSE' >> /data/local.prop")
2477    ad.adb.shell("chmod 644 /data/local.prop")
2478    ad.adb.shell("echo 'LogEnabled=true' > /data/vendor/gps/libgps.conf")
2479    ad.adb.shell("chown gps.system /data/vendor/gps/libgps.conf")
2480    ad.adb.shell("sync")
2481    reboot(ad)
2482    ad.log.info("Wait 2 mins for Wearable booting system busy")
2483    time.sleep(120)
2484