1# Copyright 2013 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import logging 6import pipes 7import sys 8 9from devil.android import device_errors # pylint: disable=import-error 10 11 12def _QuoteIfNeeded(arg): 13 # Properly escape "key=valueA valueB" to "key='valueA valueB'" 14 # Values without spaces, or that seem to be quoted are left untouched. 15 # This is required so CommandLine.java can parse valueB correctly rather 16 # than as a separate switch. 17 params = arg.split('=', 1) 18 if len(params) != 2: 19 return arg 20 key, values = params 21 if ' ' not in values: 22 return arg 23 if values[0] in '"\'' and values[-1] == values[0]: 24 return arg 25 return '%s=%s' % (key, pipes.quote(values)) 26 27 28class SetUpCommandLineFlags(object): 29 """A context manager for setting up the android command line flags. 30 31 This provides a readable way of using the android command line backend class. 32 Example usage: 33 34 with android_command_line_backend.SetUpCommandLineFlags( 35 device, backend_settings, startup_args): 36 # Something to run while the command line flags are set appropriately. 37 """ 38 def __init__(self, device, backend_settings, startup_args): 39 self._android_command_line_backend = _AndroidCommandLineBackend( 40 device, backend_settings, startup_args) 41 42 def __enter__(self): 43 self._android_command_line_backend.SetUpCommandLineFlags() 44 45 def __exit__(self, *args): 46 self._android_command_line_backend.RestoreCommandLineFlags() 47 48 49class _AndroidCommandLineBackend(object): 50 """The backend for providing command line flags on android. 51 52 There are command line flags that Chromium accept in order to enable 53 particular features or modify otherwise default functionality. To set the 54 flags for Chrome on Android, specific files on the device must be updated 55 with the flags to enable. This class provides a wrapper around this 56 functionality. 57 """ 58 59 def __init__(self, device, backend_settings, startup_args): 60 self._device = device 61 self._backend_settings = backend_settings 62 self._startup_args = startup_args 63 self._saved_command_line_file_contents = None 64 65 @property 66 def command_line_file(self): 67 return self._backend_settings.GetCommandLineFile(self._device.IsUserBuild()) 68 69 def SetUpCommandLineFlags(self): 70 args = [self._backend_settings.pseudo_exec_name] 71 args.extend(self._startup_args) 72 content = ' '.join(_QuoteIfNeeded(arg) for arg in args) 73 74 try: 75 # Save the current command line to restore later, except if it appears to 76 # be a Telemetry created one. This is to prevent a common bug where 77 # --host-resolver-rules borks people's browsers if something goes wrong 78 # with Telemetry. 79 self._saved_command_line_file_contents = self._ReadFile() 80 if (self._saved_command_line_file_contents and 81 '--host-resolver-rules' in self._saved_command_line_file_contents): 82 self._saved_command_line_file_contents = None 83 except device_errors.CommandFailedError: 84 self._saved_command_line_file_contents = None 85 86 try: 87 self._WriteFile(content) 88 except device_errors.CommandFailedError as exc: 89 logging.critical(exc) 90 logging.critical('Cannot set Chrome command line. ' 91 'Fix this by flashing to a userdebug build.') 92 sys.exit(1) 93 94 def RestoreCommandLineFlags(self): 95 if self._saved_command_line_file_contents is None: 96 self._RemoveFile() 97 else: 98 self._WriteFile(self._saved_command_line_file_contents) 99 100 def _ReadFile(self): 101 if self._device.PathExists(self.command_line_file): 102 return self._device.ReadFile(self.command_line_file, as_root=True) 103 else: 104 return None 105 106 def _WriteFile(self, contents): 107 self._device.WriteFile(self.command_line_file, contents, as_root=True) 108 109 def _RemoveFile(self): 110 self._device.RunShellCommand(['rm', '-f', self.command_line_file], 111 as_root=True, check_return=True) 112