chromium/android_webview/tools/record_netlog.py

#!/usr/bin/env vpython3
#
# Copyright 2019 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Takes a netlog for the WebViews in a given application.

Developer guide:
https://chromium.googlesource.com/chromium/src/+/HEAD/android_webview/docs/net-debugging.md
"""

from __future__ import print_function

import argparse
import logging
import os
import posixpath
import re
import sys
import time

sys.path.append(
    os.path.join(
        os.path.dirname(__file__), os.pardir, os.pardir, 'build', 'android'))
# pylint: disable=wrong-import-position,import-error
import devil_chromium
from devil.android import device_errors
from devil.android import flag_changer
from devil.android import device_utils
from devil.android.tools import script_common
from devil.utils import logging_common

WEBVIEW_COMMAND_LINE = 'webview-command-line'


def _WaitUntilCtrlC():
  try:
    while True:
      time.sleep(1)
  except KeyboardInterrupt:
    print()  # print a new line after the "^C" the user typed to the console


def CheckAppNotRunning(device, package_name, force):
  is_running = bool(device.GetApplicationPids(package_name))
  if is_running:
    msg = ('Netlog requires setting commandline flags, which only works if the '
           'application ({}) is not already running. Please kill the app and '
           'restart the script.'.format(
               package_name))
    if force:
      logging.warning(msg)
    else:
      # Extend the sentence to mention the user can skip the check.
      msg = re.sub(r'\.$', ', or pass --force to ignore this check.', msg)
      raise RuntimeError(msg)


def main():
  parser = argparse.ArgumentParser(description="""
Configures WebView to start recording a netlog. This script chooses a suitable
netlog filename for the application, and will pull the netlog off the device
when the user terminates the script (with ctrl-C). For a more complete usage
guide, open your web browser to:
https://chromium.googlesource.com/chromium/src/+/HEAD/android_webview/docs/net-debugging.md
""")
  parser.add_argument(
      '--package',
      required=True,
      type=str,
      help='Package name of the application you intend to use.')
  parser.add_argument(
      '--force',
      default=False,
      action='store_true',
      help='Suppress user checks.')

  script_common.AddEnvironmentArguments(parser)
  script_common.AddDeviceArguments(parser)
  logging_common.AddLoggingArguments(parser)

  args = parser.parse_args()
  logging_common.InitializeLogging(args)
  devil_chromium.Initialize(adb_path=args.adb_path)

  # Only use a single device, for the sake of simplicity (of implementation and
  # user experience).
  devices = device_utils.DeviceUtils.HealthyDevices(device_arg=args.devices)
  device = devices[0]
  if len(devices) > 1:
    raise device_errors.MultipleDevicesError(devices)

  if device.build_type == 'user':
    device_setup_url = ('https://chromium.googlesource.com/chromium/src/+/HEAD/'
                        'android_webview/docs/device-setup.md')
    raise RuntimeError('It appears your device is a "user" build. We only '
                       'support capturing netlog on userdebug/eng builds. See '
                       '{} to configure a development device or set up an '
                       'emulator.'.format(device_setup_url))

  package_name = args.package
  device_netlog_file_name = 'netlog.json'
  device_netlog_path = posixpath.join(
      device.GetApplicationDataDirectory(package_name), 'app_webview',
      device_netlog_file_name)

  CheckAppNotRunning(device, package_name, args.force)

  # Append to the existing flags, to allow users to experiment with other
  # features/flags enabled. The CustomCommandLineFlags will restore the original
  # flag state after the user presses 'ctrl-C'.
  changer = flag_changer.FlagChanger(device, WEBVIEW_COMMAND_LINE)
  new_flags = changer.GetCurrentFlags()
  new_flags.append('--log-net-log={}'.format(device_netlog_path))

  logging.info('Running with flags %r', new_flags)
  with flag_changer.CustomCommandLineFlags(device, WEBVIEW_COMMAND_LINE,
                                           new_flags):
    print('Netlog will start recording as soon as app starts up. Press ctrl-C '
          'to stop recording.')
    _WaitUntilCtrlC()

  host_netlog_path = 'netlog.json'
  print('Pulling netlog to "%s"' % host_netlog_path)
  # The netlog file will be under the app's uid, which the default shell doesn't
  # have permission to read (but root does). Prefer this to EnableRoot(), which
  # restarts the adb daemon.
  if device.PathExists(device_netlog_path, as_root=True):
    device.PullFile(device_netlog_path, host_netlog_path, as_root=True)
    device.RemovePath(device_netlog_path, as_root=True)
  else:
    raise RuntimeError(
        'Unable to find a netlog file in the "{}" app data directory. '
        'Did you restart and run the app?'.format(package_name))


if __name__ == '__main__':
  main()