Remove unused parameter.
[chromium-blink-merge.git] / build / android / provision_devices.py
blobad51f039e068af625cd8035e7369b56ed4ada743
1 #!/usr/bin/env python
3 # Copyright (c) 2013 The Chromium Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
7 """Provisions Android devices with settings required for bots.
9 Usage:
10 ./provision_devices.py [-d <device serial number>]
11 """
13 import argparse
14 import logging
15 import os
16 import posixpath
17 import re
18 import subprocess
19 import sys
20 import time
22 from pylib import android_commands
23 from pylib import constants
24 from pylib import device_settings
25 from pylib.device import battery_utils
26 from pylib.device import device_blacklist
27 from pylib.device import device_errors
28 from pylib.device import device_utils
29 from pylib.utils import run_tests_helper
30 from pylib.utils import timeout_retry
32 sys.path.append(os.path.join(constants.DIR_SOURCE_ROOT,
33 'third_party', 'android_testrunner'))
34 import errors
37 class _DEFAULT_TIMEOUTS(object):
38 # L can take a while to reboot after a wipe.
39 LOLLIPOP = 600
40 PRE_LOLLIPOP = 180
42 HELP_TEXT = '{}s on L, {}s on pre-L'.format(LOLLIPOP, PRE_LOLLIPOP)
45 def KillHostHeartbeat():
46 ps = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
47 stdout, _ = ps.communicate()
48 matches = re.findall('\\n.*host_heartbeat.*', stdout)
49 for match in matches:
50 logging.info('An instance of host heart beart running... will kill')
51 pid = re.findall(r'(\S+)', match)[1]
52 subprocess.call(['kill', str(pid)])
55 def LaunchHostHeartbeat():
56 # Kill if existing host_heartbeat
57 KillHostHeartbeat()
58 # Launch a new host_heartbeat
59 logging.info('Spawning host heartbeat...')
60 subprocess.Popen([os.path.join(constants.DIR_SOURCE_ROOT,
61 'build/android/host_heartbeat.py')])
64 def PushAndLaunchAdbReboot(device, target):
65 """Pushes and launches the adb_reboot binary on the device.
67 Arguments:
68 device: The DeviceUtils instance for the device to which the adb_reboot
69 binary should be pushed.
70 target: The build target (example, Debug or Release) which helps in
71 locating the adb_reboot binary.
72 """
73 logging.info('Will push and launch adb_reboot on %s' % str(device))
74 # Kill if adb_reboot is already running.
75 try:
76 # Don't try to kill adb_reboot more than once. We don't expect it to be
77 # running at all.
78 device.KillAll('adb_reboot', blocking=True, timeout=2, retries=0)
79 except device_errors.CommandFailedError:
80 # We can safely ignore the exception because we don't expect adb_reboot
81 # to be running.
82 pass
83 # Push adb_reboot
84 logging.info(' Pushing adb_reboot ...')
85 adb_reboot = os.path.join(constants.DIR_SOURCE_ROOT,
86 'out/%s/adb_reboot' % target)
87 device.PushChangedFiles([(adb_reboot, '/data/local/tmp/')])
88 # Launch adb_reboot
89 logging.info(' Launching adb_reboot ...')
90 device.RunShellCommand([
91 device.GetDevicePieWrapper(),
92 '/data/local/tmp/adb_reboot'])
95 def _ConfigureLocalProperties(device, java_debug=True):
96 """Set standard readonly testing device properties prior to reboot."""
97 local_props = [
98 'persist.sys.usb.config=adb',
99 'ro.monkey=1',
100 'ro.test_harness=1',
101 'ro.audio.silent=1',
102 'ro.setupwizard.mode=DISABLED',
104 if java_debug:
105 local_props.append('%s=all' % android_commands.JAVA_ASSERT_PROPERTY)
106 local_props.append('debug.checkjni=1')
107 try:
108 device.WriteFile(
109 constants.DEVICE_LOCAL_PROPERTIES_PATH,
110 '\n'.join(local_props), as_root=True)
111 # Android will not respect the local props file if it is world writable.
112 device.RunShellCommand(
113 ['chmod', '644', constants.DEVICE_LOCAL_PROPERTIES_PATH],
114 as_root=True)
115 except device_errors.CommandFailedError as e:
116 logging.warning(str(e))
118 # LOCAL_PROPERTIES_PATH = '/data/local.prop'
120 def WriteAdbKeysFile(device, adb_keys_string):
121 dir_path = posixpath.dirname(constants.ADB_KEYS_FILE)
122 device.RunShellCommand('mkdir -p %s' % dir_path, as_root=True)
123 device.RunShellCommand('restorecon %s' % dir_path, as_root=True)
124 device.WriteFile(constants.ADB_KEYS_FILE, adb_keys_string, as_root=True)
125 device.RunShellCommand('restorecon %s' % constants.ADB_KEYS_FILE,
126 as_root=True)
129 def WipeDeviceData(device, options):
130 """Wipes data from device, keeping only the adb_keys for authorization.
132 After wiping data on a device that has been authorized, adb can still
133 communicate with the device, but after reboot the device will need to be
134 re-authorized because the adb keys file is stored in /data/misc/adb/.
135 Thus, adb_keys file is rewritten so the device does not need to be
136 re-authorized.
138 Arguments:
139 device: the device to wipe
141 device_authorized = device.FileExists(constants.ADB_KEYS_FILE)
142 if device_authorized:
143 adb_keys = device.ReadFile(constants.ADB_KEYS_FILE,
144 as_root=True).splitlines()
145 device.RunShellCommand('wipe data', as_root=True)
146 if device_authorized:
147 adb_keys_set = set(adb_keys)
148 for adb_key_file in options.adb_key_files or []:
149 try:
150 with open(adb_key_file, 'r') as f:
151 adb_public_keys = f.readlines()
152 adb_keys_set.update(adb_public_keys)
153 except IOError:
154 logging.warning('Unable to find adb keys file %s.' % adb_key_file)
155 WriteAdbKeysFile(device, '\n'.join(adb_keys_set))
158 def WipeDeviceIfPossible(device, timeout, options):
159 try:
160 device.EnableRoot()
161 WipeDeviceData(device, options)
162 device.Reboot(True, timeout=timeout, retries=0)
163 except (errors.DeviceUnresponsiveError, device_errors.CommandFailedError):
164 pass
167 def ProvisionDevice(device, options):
168 if options.reboot_timeout:
169 reboot_timeout = options.reboot_timeout
170 elif (device.build_version_sdk >=
171 constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP):
172 reboot_timeout = _DEFAULT_TIMEOUTS.LOLLIPOP
173 else:
174 reboot_timeout = _DEFAULT_TIMEOUTS.PRE_LOLLIPOP
176 try:
177 if not options.skip_wipe:
178 WipeDeviceIfPossible(device, reboot_timeout, options)
179 try:
180 device.EnableRoot()
181 except device_errors.CommandFailedError as e:
182 logging.warning(str(e))
183 _ConfigureLocalProperties(device, options.enable_java_debug)
184 device_settings.ConfigureContentSettings(
185 device, device_settings.DETERMINISTIC_DEVICE_SETTINGS)
186 if options.disable_location:
187 device_settings.ConfigureContentSettings(
188 device, device_settings.DISABLE_LOCATION_SETTINGS)
189 else:
190 device_settings.ConfigureContentSettings(
191 device, device_settings.ENABLE_LOCATION_SETTINGS)
192 device_settings.SetLockScreenSettings(device)
193 if options.disable_network:
194 device_settings.ConfigureContentSettings(
195 device, device_settings.NETWORK_DISABLED_SETTINGS)
196 if options.min_battery_level is not None:
197 try:
198 battery = battery_utils.BatteryUtils(device)
199 battery.ChargeDeviceToLevel(options.min_battery_level)
200 except device_errors.CommandFailedError as e:
201 logging.exception('Unable to charge device to specified level.')
203 if not options.skip_wipe:
204 device.Reboot(True, timeout=reboot_timeout, retries=0)
205 device.RunShellCommand('date -s %s' % time.strftime('%Y%m%d.%H%M%S',
206 time.gmtime()),
207 as_root=True)
208 props = device.RunShellCommand('getprop')
209 for prop in props:
210 logging.info(' %s' % prop)
211 if options.auto_reconnect:
212 PushAndLaunchAdbReboot(device, options.target)
213 except (errors.WaitForResponseTimedOutError,
214 device_errors.CommandTimeoutError):
215 logging.info('Timed out waiting for device %s. Adding to blacklist.',
216 str(device))
217 # Device black list is reset by bb_device_status_check.py per build.
218 device_blacklist.ExtendBlacklist([str(device)])
219 except device_errors.CommandFailedError:
220 logging.exception('Failed to provision device %s. Adding to blacklist.',
221 str(device))
222 device_blacklist.ExtendBlacklist([str(device)])
225 def ProvisionDevices(options):
226 if options.device is not None:
227 devices = [options.device]
228 else:
229 devices = android_commands.GetAttachedDevices()
231 parallel_devices = device_utils.DeviceUtils.parallel(devices)
232 parallel_devices.pMap(ProvisionDevice, options)
233 if options.auto_reconnect:
234 LaunchHostHeartbeat()
235 blacklist = device_blacklist.ReadBlacklist()
236 if all(d in blacklist for d in devices):
237 raise device_errors.NoDevicesError
238 return 0
241 def main():
242 custom_handler = logging.StreamHandler(sys.stdout)
243 custom_handler.setFormatter(run_tests_helper.CustomFormatter())
244 logging.getLogger().addHandler(custom_handler)
245 logging.getLogger().setLevel(logging.INFO)
247 # Recommended options on perf bots:
248 # --disable-network
249 # TODO(tonyg): We eventually want network on. However, currently radios
250 # can cause perfbots to drain faster than they charge.
251 # --min-battery-level 95
252 # Some perf bots run benchmarks with USB charging disabled which leads
253 # to gradual draining of the battery. We must wait for a full charge
254 # before starting a run in order to keep the devices online.
256 parser = argparse.ArgumentParser(
257 description='Provision Android devices with settings required for bots.')
258 parser.add_argument('-d', '--device', metavar='SERIAL',
259 help='the serial number of the device to be provisioned'
260 ' (the default is to provision all devices attached)')
261 parser.add_argument('--skip-wipe', action='store_true', default=False,
262 help="don't wipe device data during provisioning")
263 parser.add_argument('--reboot-timeout', metavar='SECS', type=int,
264 help='when wiping the device, max number of seconds to'
265 ' wait after each reboot '
266 '(default: %s)' % _DEFAULT_TIMEOUTS.HELP_TEXT)
267 parser.add_argument('--min-battery-level', type=int, metavar='NUM',
268 help='wait for the device to reach this minimum battery'
269 ' level before trying to continue')
270 parser.add_argument('--disable-location', action='store_true',
271 help='disable Google location services on devices')
272 parser.add_argument('--disable-network', action='store_true',
273 help='disable network access on devices')
274 parser.add_argument('--disable-java-debug', action='store_false',
275 dest='enable_java_debug', default=True,
276 help='disable Java property asserts and JNI checking')
277 parser.add_argument('-t', '--target', default='Debug',
278 help='the build target (default: %(default)s)')
279 parser.add_argument('-r', '--auto-reconnect', action='store_true',
280 help='push binary which will reboot the device on adb'
281 ' disconnections')
282 parser.add_argument('--adb-key-files', type=str, nargs='+',
283 help='list of adb keys to push to device')
284 args = parser.parse_args()
285 constants.SetBuildType(args.target)
287 return ProvisionDevices(args)
290 if __name__ == '__main__':
291 sys.exit(main())