示例#1
0
def main(raw_args):
  parser = argparse.ArgumentParser()
  logging_common.AddLoggingArguments(parser)
  script_common.AddEnvironmentArguments(parser)
  parser.add_argument(
      '--device',
      dest='devices',
      action='append',
      default=[],
      help='Devices for which the governor should be set. Defaults to all.')

  subparsers = parser.add_subparsers()

  set_governor = subparsers.add_parser('set-governor')
  set_governor.add_argument('governor', help='Desired CPU governor.')
  set_governor.set_defaults(func=SetScalingGovernor)

  get_governor = subparsers.add_parser('get-governor')
  get_governor.set_defaults(func=GetScalingGovernor)

  list_governors = subparsers.add_parser('list-governors')
  list_governors.set_defaults(func=ListAvailableGovernors)

  args = parser.parse_args(raw_args)

  logging_common.InitializeLogging(args)
  script_common.InitializeEnvironment(args)

  devices = device_utils.DeviceUtils.HealthyDevices(device_arg=args.devices)

  parallel_devices = device_utils.DeviceUtils.parallel(devices)
  parallel_devices.pMap(args.func, args)

  return 0
示例#2
0
def main():
  parser = argparse.ArgumentParser()
  logging_common.AddLoggingArguments(parser)
  script_common.AddEnvironmentArguments(parser)
  parser.add_argument('--blacklist-file', help='Device blacklist JSON file.')
  parser.add_argument('--known-devices-file', action='append', default=[],
                      dest='known_devices_files',
                      help='Path to known device lists.')
  parser.add_argument('--enable-usb-reset', action='store_true',
                      help='Reset USB if necessary.')

  args = parser.parse_args()
  logging_common.InitializeLogging(args)
  script_common.InitializeEnvironment(args)

  blacklist = (device_blacklist.Blacklist(args.blacklist_file)
               if args.blacklist_file
               else None)

  expected_devices = device_status.GetExpectedDevices(args.known_devices_files)
  usb_devices = set(lsusb.get_android_devices())
  devices = [device_utils.DeviceUtils(s)
             for s in expected_devices.union(usb_devices)]

  RecoverDevices(devices, blacklist, enable_usb_reset=args.enable_usb_reset)
示例#3
0
 def testAdb(self):
   with tempfile.NamedTemporaryFile() as f:
     args = self.parser.parse_args(['--adb-path=%s' % f.name])
     script_common.InitializeEnvironment(args)
     self.assertEquals(
         f.name,
         devil_env.config.LocalPath('adb'))
示例#4
0
def main(argv):
  """Launches the device monitor.

  Polls the devices for their battery and cpu temperatures and scans the
  blacklist file every 60 seconds and dumps the data to DEVICE_FILE.
  """

  parser = argparse.ArgumentParser(
      description='Launches the device monitor.')
  script_common.AddEnvironmentArguments(parser)
  parser.add_argument('--blacklist-file', help='Path to device blacklist file.')
  args = parser.parse_args(argv)

  logger = logging.getLogger()
  logger.setLevel(logging.DEBUG)
  handler = logging.handlers.RotatingFileHandler(
      '/tmp/device_monitor.log', maxBytes=10 * 1024 * 1024, backupCount=5)
  fmt = logging.Formatter('%(asctime)s %(levelname)s %(message)s',
                          datefmt='%y%m%d %H:%M:%S')
  handler.setFormatter(fmt)
  logger.addHandler(handler)
  script_common.InitializeEnvironment(args)

  blacklist = (device_blacklist.Blacklist(args.blacklist_file)
               if args.blacklist_file else None)

  logging.info('Device monitor running with pid %d, adb: %s, blacklist: %s',
               os.getpid(), args.adb_path, args.blacklist_file)
  while True:
    start = time.time()
    status_dict = get_all_status(blacklist)
    with open(DEVICE_FILE, 'wb') as f:
      json.dump(status_dict, f, indent=2, sort_keys=True)
    logging.info('Got status of all devices in %.2fs.', time.time() - start)
    time.sleep(60)
def main():
  parser = argparse.ArgumentParser(
      'Run an adb shell command on selected devices')
  parser.add_argument('cmd', help='Adb shell command to run.', nargs="+")
  logging_common.AddLoggingArguments(parser)
  script_common.AddDeviceArguments(parser)
  script_common.AddEnvironmentArguments(parser)
  parser.add_argument('--as-root', action='store_true', help='Run as root.')
  parser.add_argument('--json-output', help='File to dump json output to.')
  args = parser.parse_args()

  logging_common.InitializeLogging(args)
  script_common.InitializeEnvironment(args)

  devices = script_common.GetDevices(args.devices, args.denylist_file)
  p_out = (device_utils.DeviceUtils.parallel(devices).RunShellCommand(
      args.cmd, large_output=True, as_root=args.as_root,
      check_return=True).pGet(None))

  data = {}
  for device, output in zip(devices, p_out):
    for line in output:
      print '%s: %s' % (device, line)
    data[str(device)] = output

  if args.json_output:
    with open(args.json_output, 'w') as f:
      json.dump(data, f)

  return 0
示例#6
0
def main():
    devil_chromium.Initialize()
    parser = argparse.ArgumentParser(description="""
List Java classes in an APK which fail ART class verification.
""")
    parser.add_argument('--package',
                        '-P',
                        type=str,
                        default=None,
                        required=True,
                        help='Specify the full application package name')
    parser.add_argument(
        '--mapping',
        '-m',
        type=os.path.realpath,
        default=None,
        help='Mapping file for the desired APK to deobfuscate class names')
    parser.add_argument(
        '--hide-summary',
        default=False,
        action='store_true',
        help='Do not output the total number of classes in each Status.')
    parser.add_argument(
        '--status',
        type=str,
        default='RetryVerificationAtRuntime',
        choices=STATUSES,
        help='Which category of classes to list at the end of the script')
    parser.add_argument(
        '--workdir',
        '-w',
        type=os.path.realpath,
        default=None,
        help=(
            'Work directory for oatdump output (default = temporary '
            'directory). If specified, this will not be cleaned up at the end '
            'of the script (useful if you want to inspect oatdump output '
            'manually)'))

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

    args = parser.parse_args()
    script_common.InitializeEnvironment(args)
    logging_common.InitializeLogging(args)

    if args.workdir:
        if not os.path.isdir(args.workdir):
            raise RuntimeError('Specified working directory does not exist')
        RealMain(args.mapping, args.devices, args.package, args.status,
                 args.hide_summary, args.workdir)
        # Assume the user wants the workdir to persist (useful for debugging).
        logging.warn('Not cleaning up explicitly-specified workdir: %s',
                     args.workdir)
    else:
        with tempfile_ext.NamedTemporaryDirectory() as workdir:
            RealMain(args.mapping, args.devices, args.package, args.status,
                     args.hide_summary, workdir)
示例#7
0
def main(raw_args):
    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers()

    def add_common_arguments(p):
        script_common.AddDeviceArguments(p)
        script_common.AddEnvironmentArguments(p)
        p.add_argument('-v',
                       '--verbose',
                       action='count',
                       default=0,
                       help='Print more information.')
        p.add_argument('command', nargs='*')

    @contextlib.contextmanager
    def remove_system_app(device, args):
        RemoveSystemApps(device, args.packages)
        yield

    remove_parser = subparsers.add_parser('remove')
    remove_parser.add_argument('--package',
                               dest='packages',
                               nargs='*',
                               required=True,
                               help='The system package(s) to remove.')
    add_common_arguments(remove_parser)
    remove_parser.set_defaults(func=remove_system_app)

    @contextlib.contextmanager
    def replace_system_app(device, args):
        with ReplaceSystemApp(device, args.package, args.replace_with):
            yield

    replace_parser = subparsers.add_parser('replace')
    replace_parser.add_argument('--package',
                                required=True,
                                help='The system package to replace.')
    replace_parser.add_argument(
        '--replace-with',
        metavar='APK',
        required=True,
        help='The APK with which the existing system app should be replaced.')
    add_common_arguments(replace_parser)
    replace_parser.set_defaults(func=replace_system_app)

    args = parser.parse_args(raw_args)

    run_tests_helper.SetLogLevel(args.verbose)
    script_common.InitializeEnvironment(args)

    devices = script_common.GetDevices(args.devices, args.denylist_file)
    parallel_devices = parallelizer.SyncParallelizer(
        [args.func(d, args) for d in devices])
    with parallel_devices:
        if args.command:
            return cmd_helper.Call(args.command)
        return 0
示例#8
0
def main():
    parser = argparse.ArgumentParser()
    logging_common.AddLoggingArguments(parser)
    script_common.AddEnvironmentArguments(parser)
    AddArguments(parser)
    args = parser.parse_args()

    logging_common.InitializeLogging(args)
    script_common.InitializeEnvironment(args)

    denylist = (device_denylist.Denylist(args.denylist_file)
                if args.denylist_file else None)

    expected_devices = GetExpectedDevices(args.known_devices_files)
    usb_devices = set(lsusb.get_android_devices())
    devices = [
        device_utils.DeviceUtils(s)
        for s in expected_devices.union(usb_devices)
    ]

    statuses = DeviceStatus(devices, denylist)

    # Log the state of all devices.
    _LogStatuses(statuses)

    # Update the last devices file(s).
    if args.overwrite_known_devices_files:
        for path in args.known_devices_files:
            device_list.WritePersistentDeviceList(
                path, [status['serial'] for status in statuses])

    # Write device info to file for buildbot info display.
    _WriteBuildbotFile(args.buildbot_path, statuses)

    # Dump the device statuses to JSON.
    if args.json_output:
        with open(args.json_output, 'wb') as f:
            f.write(
                json.dumps(statuses,
                           indent=4,
                           sort_keys=True,
                           separators=(',', ': ')))

    live_devices = [
        status['serial'] for status in statuses
        if (status['adb_status'] == 'device'
            and not IsDenylisted(status['serial'], denylist))
    ]

    # If all devices failed, or if there are no devices, it's an infra error.
    if not live_devices:
        logger.error('No available devices.')
    return 0 if live_devices else exit_codes.INFRA
def main():
    parser = argparse.ArgumentParser(description="""
Removes the preinstalled WebView APKs to avoid signature mismatches during
development.
""")

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

    args = parser.parse_args()
    logging_common.InitializeLogging(args)
    devil_chromium.Initialize()
    script_common.InitializeEnvironment(args)

    devices = device_utils.DeviceUtils.HealthyDevices(device_arg=args.devices)
    device_utils.DeviceUtils.parallel(devices).pMap(RemovePreinstalledWebViews)
示例#10
0
def main(raw_args):
    parser = argparse.ArgumentParser()

    def add_common_arguments(p):
        script_common.AddDeviceArguments(p)
        script_common.AddEnvironmentArguments(p)
        p.add_argument('-v',
                       '--verbose',
                       action='count',
                       default=0,
                       help='Print more information.')
        p.add_argument('command', nargs='*')

    @contextlib.contextmanager
    def use_webview_provider(device, args):
        with UseWebViewProvider(device, args.apk, args.expected_package):
            yield

    parser.add_argument('--apk',
                        required=True,
                        help='The apk to use as the provider.')
    parser.add_argument(
        '--expected-package',
        default='',
        help="Verify apk's package name matches value, disabled by default.")
    add_common_arguments(parser)
    parser.set_defaults(func=use_webview_provider)

    args = parser.parse_args(raw_args)

    run_tests_helper.SetLogLevel(args.verbose)
    script_common.InitializeEnvironment(args)

    devices = script_common.GetDevices(args.devices, args.denylist_file)
    parallel_devices = parallelizer.SyncParallelizer(
        [args.func(d, args) for d in devices])
    with parallel_devices:
        if args.command:
            return cmd_helper.Call(args.command)
        return 0
def main():
    parser = argparse.ArgumentParser(description="""
Removes the preinstalled WebView APKs to avoid signature mismatches during
development.
""")

    parser.add_argument('--verbose', '-v', default=False, action='store_true')
    parser.add_argument('--quiet', '-q', default=False, action='store_true')
    script_common.AddEnvironmentArguments(parser)
    script_common.AddDeviceArguments(parser)

    args = parser.parse_args()
    if args.verbose:
        logging.basicConfig(stream=sys.stderr, level=logging.INFO)
    elif args.quiet:
        logging.basicConfig(stream=sys.stderr, level=logging.ERROR)
    else:
        logging.basicConfig(stream=sys.stderr, level=logging.WARN)

    devil_chromium.Initialize()
    script_common.InitializeEnvironment(args)

    devices = device_utils.DeviceUtils.HealthyDevices(device_arg=args.devices)
    device_utils.DeviceUtils.parallel(devices).pMap(RemovePreinstalledWebViews)
示例#12
0
def main(raw_args):
    parser = argparse.ArgumentParser()
    parser.add_argument('-v', '--verbose', action='count', help='Log more.')
    parser.add_argument('-t',
                        '--timeout',
                        default=30,
                        type=int,
                        help='Seconds to wait for the devices.')
    parser.add_argument('--adb-path', help='ADB binary to use.')
    parser.add_argument('device_serials',
                        nargs='*',
                        metavar='SERIAL',
                        help='Serials of the devices to wait for.')

    args = parser.parse_args(raw_args)

    run_tests_helper.SetLogLevel(args.verbose)
    script_common.InitializeEnvironment(args)

    devices = device_utils.DeviceUtils.HealthyDevices(
        device_arg=args.device_serials)
    parallel_devices = device_utils.DeviceUtils.parallel(devices)
    parallel_devices.WaitUntilFullyBooted(timeout=args.timeout)
    return 0
示例#13
0
 def testNonExistentAdb(self):
     with tempfile.NamedTemporaryFile() as f:
         args = self.parser.parse_args(['--adb-path=%s' % f.name])
     script_common.InitializeEnvironment(args)
     with self.assertRaises(exceptions.NoPathFoundError):
         devil_env.config.LocalPath('adb')
示例#14
0
 def testNoAdb(self):
     args = self.parser.parse_args([])
     script_common.InitializeEnvironment(args)
     with self.assertRaises(exceptions.NoPathFoundError):
         devil_env.config.LocalPath('adb')
def main(raw_args):
    # Recommended options on perf bots:
    # --disable-network
    #     TODO(tonyg): We eventually want network on. However, currently radios
    #     can cause perfbots to drain faster than they charge.
    # --min-battery-level 95
    #     Some perf bots run benchmarks with USB charging disabled which leads
    #     to gradual draining of the battery. We must wait for a full charge
    #     before starting a run in order to keep the devices online.

    parser = argparse.ArgumentParser(
        description='Provision Android devices with settings required for bots.'
    )
    logging_common.AddLoggingArguments(parser)
    script_common.AddDeviceArguments(parser)
    script_common.AddEnvironmentArguments(parser)
    parser.add_argument('--adb-key-files',
                        type=str,
                        nargs='+',
                        help='list of adb keys to push to device')
    parser.add_argument('--disable-location',
                        action='store_true',
                        help='disable Google location services on devices')
    parser.add_argument('--disable-mock-location',
                        action='store_true',
                        default=False,
                        help='Set ALLOW_MOCK_LOCATION to false')
    parser.add_argument('--disable-network',
                        action='store_true',
                        help='disable network access on devices')
    parser.add_argument('--disable-java-debug',
                        action='store_false',
                        dest='enable_java_debug',
                        default=True,
                        help='disable Java property asserts and JNI checking')
    parser.add_argument(
        '--disable-system-chrome',
        action='store_true',
        help='DEPRECATED: use --remove-system-packages com.android.google '
        'Disable the system chrome from devices.')
    parser.add_argument('--emulators',
                        action='store_true',
                        help='provision only emulators and ignore usb devices '
                        '(this will not wipe emulators)')
    parser.add_argument(
        '--max-battery-temp',
        type=int,
        metavar='NUM',
        help='Wait for the battery to have this temp or lower.')
    parser.add_argument(
        '--min-battery-level',
        type=int,
        metavar='NUM',
        help='wait for the device to reach this minimum battery'
        ' level before trying to continue')
    parser.add_argument('--output-device-denylist',
                        help='Json file to output the device denylist.')
    parser.add_argument('--reboot-timeout',
                        metavar='SECS',
                        type=int,
                        help='when wiping the device, max number of seconds to'
                        ' wait after each reboot '
                        '(default: %s)' % _DEFAULT_TIMEOUTS.HELP_TEXT)
    parser.add_argument(
        '--remove-system-apps',
        nargs='*',
        dest='system_app_remove_list',
        help='DEPRECATED: use --remove-system-packages instead. '
        'The names of system apps to remove. ')
    parser.add_argument('--remove-system-packages',
                        nargs='*',
                        dest='system_package_remove_list',
                        help='The names of system packages to remove.')
    parser.add_argument('--remove-system-webview',
                        action='store_true',
                        help='DEPRECATED: use --remove-system-packages '
                        'com.google.android.webview com.android.webview '
                        'Remove the system webview from devices.')
    parser.add_argument('--skip-wipe',
                        action='store_true',
                        default=False,
                        help='do not wipe device data during provisioning')

    # No-op arguments for compatibility with build/android/provision_devices.py.
    # TODO(jbudorick): Remove these once all callers have stopped using them.
    parser.add_argument('--chrome-specific-wipe',
                        action='store_true',
                        help=argparse.SUPPRESS)
    parser.add_argument('--phase', action='append', help=argparse.SUPPRESS)
    parser.add_argument('-r',
                        '--auto-reconnect',
                        action='store_true',
                        help=argparse.SUPPRESS)
    parser.add_argument('-t', '--target', help=argparse.SUPPRESS)

    args = parser.parse_args(raw_args)

    logging_common.InitializeLogging(args)
    script_common.InitializeEnvironment(args)

    try:
        return ProvisionDevices(
            args.devices,
            args.denylist_file,
            adb_key_files=args.adb_key_files,
            disable_location=args.disable_location,
            disable_mock_location=args.disable_mock_location,
            disable_network=args.disable_network,
            disable_system_chrome=args.disable_system_chrome,
            emulators=args.emulators,
            enable_java_debug=args.enable_java_debug,
            max_battery_temp=args.max_battery_temp,
            min_battery_level=args.min_battery_level,
            output_device_denylist=args.output_device_denylist,
            reboot_timeout=args.reboot_timeout,
            remove_system_webview=args.remove_system_webview,
            system_app_remove_list=args.system_app_remove_list,
            system_package_remove_list=args.system_package_remove_list,
            wipe=not args.skip_wipe and not args.emulators)
    except (device_errors.DeviceUnreachableError,
            device_errors.NoDevicesError):
        logging.exception('Unable to provision local devices.')
        return exit_codes.INFRA
示例#16
0
def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.usage = '''%(prog)s --name FILENAME [--device SERIAL] [flags...]

No flags: Prints existing command-line file.
Empty string: Deletes command-line file.
Otherwise: Writes command-line file.

'''
    parser.add_argument(
        '--name',
        required=True,
        help='Name of file where to store flags on the device.')
    parser.add_argument('-e',
                        '--executable',
                        dest='executable',
                        default='chrome',
                        help='(deprecated) No longer used.')
    script_common.AddEnvironmentArguments(parser)
    script_common.AddDeviceArguments(parser)
    logging_common.AddLoggingArguments(parser)

    args, remote_args = parser.parse_known_args()
    script_common.InitializeEnvironment(args)
    logging_common.InitializeLogging(args)

    devices = device_utils.DeviceUtils.HealthyDevices(device_arg=args.devices,
                                                      default_retries=0)
    all_devices = device_utils.DeviceUtils.parallel(devices)

    if not remote_args:
        # No args == do not update, just print flags.
        remote_args = None
        action = ''
    elif len(remote_args) == 1 and not remote_args[0]:
        # Single empty string arg == delete flags
        remote_args = []
        action = 'Deleted command line file. '
    else:
        action = 'Wrote command line file. '

    is_webview = args.name == 'webview-command-line'

    def update_flags(device):
        if device.IsUserBuild() and is_webview:
            raise device_errors.CommandFailedError(
                'WebView only respects flags on a userdebug or eng device, yours '
                'is a user build.', device)
        elif device.IsUserBuild():
            logging.warning(
                'Your device (%s) is a user build; Chrome may or may not pick up '
                'your commandline flags. Check your '
                '"command_line_on_non_rooted_enabled" preference, or switch '
                'devices.', device)
        changer = flag_changer.FlagChanger(device, args.name)
        if remote_args is not None:
            flags = changer.ReplaceFlags(remote_args)
        else:
            flags = changer.GetCurrentFlags()
        return (device, device.build_description, flags)

    updated_values = all_devices.pMap(update_flags).pGet(None)

    print '%sCurrent flags (in %s):' % (action, args.name)
    for d, desc, flags in updated_values:
        if flags:
            # Shell-quote flags for easy copy/paste as new args on the terminal.
            quoted_flags = ' '.join(
                cmd_helper.SingleQuote(f) for f in sorted(flags))
        else:
            quoted_flags = '( empty )'
        print '  %s (%s): %s' % (d, desc, quoted_flags)

    return 0
示例#17
0
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()
    script_common.InitializeEnvironment(args)

    # 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)

    package_name = args.package
    device_netlog_file_name = 'netlog.json'
    device_netlog_path = os.path.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.
    device.PullFile(device_netlog_path, host_netlog_path, as_root=True)
    device.RemovePath(device_netlog_path, as_root=True)
def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.usage = '''%(prog)s --name FILENAME [--device SERIAL] [flags...]

No flags: Prints existing command-line file.
Empty string: Deletes command-line file.
Otherwise: Writes command-line file.

'''
    parser.add_argument(
        '--name',
        required=True,
        help='Name of file where to store flags on the device.')
    parser.add_argument('-e',
                        '--executable',
                        dest='executable',
                        default='chrome',
                        help='(deprecated) No longer used.')
    script_common.AddEnvironmentArguments(parser)
    script_common.AddDeviceArguments(parser)
    logging_common.AddLoggingArguments(parser)

    args, remote_args = parser.parse_known_args()
    script_common.InitializeEnvironment(args)
    logging_common.InitializeLogging(args)

    devices = device_utils.DeviceUtils.HealthyDevices(device_arg=args.devices,
                                                      default_retries=0)
    all_devices = device_utils.DeviceUtils.parallel(devices)

    if not remote_args:
        # No args == do not update, just print flags.
        remote_args = None
        action = ''
    elif len(remote_args) == 1 and not remote_args[0]:
        # Single empty string arg == delete flags
        remote_args = []
        action = 'Deleted command line file. '
    else:
        action = 'Wrote command line file. '

    def update_flags(device):
        CheckBuildTypeSupportsFlags(device, args.name)
        changer = flag_changer.FlagChanger(device, args.name)
        if remote_args is not None:
            flags = changer.ReplaceFlags(remote_args)
        else:
            flags = changer.GetCurrentFlags()
        return (device, device.build_description, flags)

    updated_values = all_devices.pMap(update_flags).pGet(None)

    print '%sCurrent flags (in %s):' % (action, args.name)
    for d, desc, flags in updated_values:
        if flags:
            # Shell-quote flags for easy copy/paste as new args on the terminal.
            quoted_flags = ' '.join(
                cmd_helper.SingleQuote(f) for f in sorted(flags))
        else:
            quoted_flags = '( empty )'
        print '  %s (%s): %s' % (d, desc, quoted_flags)

    return 0