1"""Functions to interact with modem log.
2
3Different modem logging profile can be found here:
4cs/vendor/google/apps/PixelLogger/log_profile/GFT_Call_Performance.xml
5"""
6import enum
7import logging
8import time
9
10from mobly.controllers import android_device  # type: ignore
11
12_LOG = logging.getLogger(__name__)
13
14
15class ModemLogAction(enum.Enum):
16  """All possible valid PILOT logging actions."""
17
18  START = 'ACTION_START_LOGGING'
19  STOP = 'ACTION_STOP_LOGGING'
20  CLEAR = 'ACTION_CLEAR_LOG'
21
22
23class ModemLogProfile(enum.Enum):
24  """All possible modem logging profiles."""
25
26  LASSEN_AUDIO_TCP_DSP = 'Call_Performance.xml'
27  LASSEN_TCP_DSP = 'Data_Performance.xml'
28
29
30_MODEM_PILOT_ENABLE_PROP_NAME = 'vendor.pixellogger.pilot.logging_enable'
31
32_MODEM_LOG_PATH = '/sdcard/Android/data/com.android.pixellogger/files/logs'
33
34_ADB_SET_LOG_PROFILE_TEMPLATE = (
35    'am broadcast '
36    '-a com.android.pixellogger.experiment.ACTION_LOAD_PROFILE '
37    '-n com.android.pixellogger/.receiver.ExperimentLoggingReceiver '
38    '--es name "{log_profile_name}"'
39)
40
41_ADB_LOG_ACTION = (
42    'am broadcast '
43    '-a com.android.pixellogger.experiment.{log_action} '
44    '-n com.android.pixellogger/.receiver.ExperimentLoggingReceiver'
45)
46
47_MODEM_LOGGING_PROFILE_PROP_NAME = (
48    'persist.vendor.pixellogger.pilot.profile_name'
49)
50
51
52def start_modem_logging(
53    dut: android_device.AndroidDevice,
54    timeout: int = 20,
55    polling_interval: int = 1,
56) -> bool:
57  """Starts modem PILOT logging.
58
59  Args:
60    dut: A mobly AndroidDevice controller object.
61    timeout: Seconds to try to confirm logging before giving up.
62    polling_interval: Seconds to wait between confirmation attempts.
63
64  Raises:
65    RuntimeError: If unable to enable PILOT modem logging within timeout.
66  """
67  dut.adb.root()
68  cmd = _ADB_LOG_ACTION.format(log_action=ModemLogAction.START.value)
69  dut.adb.shell(cmd)
70  end_time = time.time() + timeout
71  while time.time() < end_time:
72    time.sleep(polling_interval)
73    res = dut.adb.getprop(_MODEM_PILOT_ENABLE_PROP_NAME).strip()
74    _LOG.debug('PILOT modem logging enable: %s', res)
75    if res == 'true':
76      return
77  raise RuntimeError('Fail to start modem logging in PILOT mode.')
78
79
80def stop_modem_logging(
81    dut: android_device.AndroidDevice,
82    timeout: int = 20,
83    polling_interval: int = 1,
84) -> bool:
85  """Stops modem PILOT logging.
86
87  Args:
88    dut: A mobly AndroidDevice controller object.
89    timeout: An integer of time in second to wait for modem log to stop.
90    polling_interval: Interval in second to check if modem logging stopped.
91
92  Raises:
93    RuntimeError: If unable to disable PILOT modem logging within timeout.
94  """
95  dut.adb.root()
96  cmd = _ADB_LOG_ACTION.format(log_action=ModemLogAction.STOP.value)
97  dut.adb.shell(cmd)
98  end_time = time.time() + timeout
99  while time.time() < end_time:
100    time.sleep(polling_interval)
101    res = dut.adb.getprop(_MODEM_PILOT_ENABLE_PROP_NAME).strip()
102    _LOG.debug('PILOT modem logging enable: %s', res)
103    if res == 'false' or not res:
104      return
105  raise RuntimeError('Fail to stop modem logging in PILOT mode.')
106
107
108def clear_modem_logging(dut: android_device.AndroidDevice) -> None:
109  """Stops modem PILOT logging.
110
111  Args:
112    dut: A mobly AndroidDevice controller object.
113  """
114  dut.adb.root()
115  cmd = _ADB_LOG_ACTION.format(log_action=ModemLogAction.CLEAR.value)
116  dut.adb.shell(cmd)
117  _LOG.debug('Cleared modem logs.')
118
119
120def set_modem_log_profle(
121    dut: android_device.AndroidDevice,
122    profile: ModemLogProfile,
123    timeout: int = 10,
124    polling_interval: int = 1,
125) -> bool:
126  """Set modem log profile.
127
128  Args:
129    dut: An mobly AndroidDevice controller object.
130    profile: An ModemLogProfile enum represent modem logging profile.
131    timeout: Time waiting for modem log profile to be set.
132    polling_interval: Interval in second to check if log profile change.
133
134  Returns:
135    True if successfully set modem log profile within timeout. Fail otherwise.
136  """
137  dut.adb.root()
138  cmd = _ADB_SET_LOG_PROFILE_TEMPLATE.format(log_profile_name=profile.value)
139  dut.adb.shell(cmd)
140  end_time = time.time() + timeout
141  while time.time() < end_time:
142    time.sleep(polling_interval)
143    if profile.value in get_modem_log_profile(dut):
144      return True
145  return False
146
147
148def get_modem_log_profile(dut: android_device.AndroidDevice) -> str:
149  """Get modem log profile.
150
151  Args:
152    dut: An mobly AndroidDevice controller object.
153
154  Returns:
155    String value of modem logging profile name.
156
157  Raises:
158    RuntimeError: If get empty response from adb shell.
159  """
160  dut.adb.root()
161  res = dut.adb.getprop(_MODEM_LOGGING_PROFILE_PROP_NAME)
162  if not res:
163    raise RuntimeError('Fail to get modem logging profile from device.')
164  return res
165
166
167def pull_logs(dut: android_device.AndroidDevice, out_path: str, pull_timeout = 300) -> None:
168  """Pulls logs on device.
169
170  Args:
171    dut: An mobly AndroidDevice controller object.
172    out_path: A path to extract logs to.
173    pull_timeout: Seconds to wait for pulling complete.
174  """
175  dut.adb.root()
176  dut.adb.pull(
177    "%s %s" % (_MODEM_LOG_PATH, out_path), timeout=pull_timeout)
178  _LOG.debug('Modem logs exported to %s', out_path)