Mandoline: Make TransferBufferManager RefCounted
[chromium-blink-merge.git] / build / android / provision_devices.py
blob191f1adca58b04f6907344d8058f4a08299a260b
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 json
15 import logging
16 import os
17 import posixpath
18 import re
19 import subprocess
20 import sys
21 import time
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 class _PHASES(object):
46 WIPE = 'wipe'
47 PROPERTIES = 'properties'
48 FINISH = 'finish'
50 ALL = [WIPE, PROPERTIES, FINISH]
53 def ProvisionDevices(options):
54 devices = device_utils.DeviceUtils.HealthyDevices()
55 if options.device:
56 devices = [d for d in devices if d == options.device]
57 if not devices:
58 raise device_errors.DeviceUnreachableError(options.device)
60 parallel_devices = device_utils.DeviceUtils.parallel(devices)
61 parallel_devices.pMap(ProvisionDevice, options)
62 if options.auto_reconnect:
63 _LaunchHostHeartbeat()
64 blacklist = device_blacklist.ReadBlacklist()
65 if options.output_device_blacklist:
66 with open(options.output_device_blacklist, 'w') as f:
67 json.dump(blacklist, f)
68 if all(d in blacklist for d in devices):
69 raise device_errors.NoDevicesError
70 return 0
73 def ProvisionDevice(device, options):
74 if options.reboot_timeout:
75 reboot_timeout = options.reboot_timeout
76 elif (device.build_version_sdk >=
77 constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP):
78 reboot_timeout = _DEFAULT_TIMEOUTS.LOLLIPOP
79 else:
80 reboot_timeout = _DEFAULT_TIMEOUTS.PRE_LOLLIPOP
82 def should_run_phase(phase_name):
83 return not options.phases or phase_name in options.phases
85 def run_phase(phase_func, reboot=True):
86 device.WaitUntilFullyBooted(timeout=reboot_timeout)
87 phase_func(device, options)
88 if reboot:
89 device.Reboot(False, retries=0)
90 device.adb.WaitForDevice()
92 try:
93 if should_run_phase(_PHASES.WIPE):
94 run_phase(WipeDevice)
96 if should_run_phase(_PHASES.PROPERTIES):
97 run_phase(SetProperties)
99 if should_run_phase(_PHASES.FINISH):
100 run_phase(FinishProvisioning, reboot=False)
102 except (errors.WaitForResponseTimedOutError,
103 device_errors.CommandTimeoutError):
104 logging.exception('Timed out waiting for device %s. Adding to blacklist.',
105 str(device))
106 device_blacklist.ExtendBlacklist([str(device)])
108 except device_errors.CommandFailedError:
109 logging.exception('Failed to provision device %s. Adding to blacklist.',
110 str(device))
111 device_blacklist.ExtendBlacklist([str(device)])
114 def WipeDevice(device, options):
115 """Wipes data from device, keeping only the adb_keys for authorization.
117 After wiping data on a device that has been authorized, adb can still
118 communicate with the device, but after reboot the device will need to be
119 re-authorized because the adb keys file is stored in /data/misc/adb/.
120 Thus, adb_keys file is rewritten so the device does not need to be
121 re-authorized.
123 Arguments:
124 device: the device to wipe
126 if options.skip_wipe:
127 return
129 try:
130 device.EnableRoot()
131 device_authorized = device.FileExists(constants.ADB_KEYS_FILE)
132 if device_authorized:
133 adb_keys = device.ReadFile(constants.ADB_KEYS_FILE,
134 as_root=True).splitlines()
135 device.RunShellCommand(['wipe', 'data'],
136 as_root=True, check_return=True)
137 device.adb.WaitForDevice()
139 if device_authorized:
140 adb_keys_set = set(adb_keys)
141 for adb_key_file in options.adb_key_files or []:
142 try:
143 with open(adb_key_file, 'r') as f:
144 adb_public_keys = f.readlines()
145 adb_keys_set.update(adb_public_keys)
146 except IOError:
147 logging.warning('Unable to find adb keys file %s.' % adb_key_file)
148 _WriteAdbKeysFile(device, '\n'.join(adb_keys_set))
149 except device_errors.CommandFailedError:
150 logging.exception('Possible failure while wiping the device. '
151 'Attempting to continue.')
154 def _WriteAdbKeysFile(device, adb_keys_string):
155 dir_path = posixpath.dirname(constants.ADB_KEYS_FILE)
156 device.RunShellCommand(['mkdir', '-p', dir_path],
157 as_root=True, check_return=True)
158 device.RunShellCommand(['restorecon', dir_path],
159 as_root=True, check_return=True)
160 device.WriteFile(constants.ADB_KEYS_FILE, adb_keys_string, as_root=True)
161 device.RunShellCommand(['restorecon', constants.ADB_KEYS_FILE],
162 as_root=True, check_return=True)
165 def SetProperties(device, options):
166 try:
167 device.EnableRoot()
168 except device_errors.CommandFailedError as e:
169 logging.warning(str(e))
171 _ConfigureLocalProperties(device, options.enable_java_debug)
172 device_settings.ConfigureContentSettings(
173 device, device_settings.DETERMINISTIC_DEVICE_SETTINGS)
174 if options.disable_location:
175 device_settings.ConfigureContentSettings(
176 device, device_settings.DISABLE_LOCATION_SETTINGS)
177 else:
178 device_settings.ConfigureContentSettings(
179 device, device_settings.ENABLE_LOCATION_SETTINGS)
180 device_settings.SetLockScreenSettings(device)
181 if options.disable_network:
182 device_settings.ConfigureContentSettings(
183 device, device_settings.NETWORK_DISABLED_SETTINGS)
185 def _ConfigureLocalProperties(device, java_debug=True):
186 """Set standard readonly testing device properties prior to reboot."""
187 local_props = [
188 'persist.sys.usb.config=adb',
189 'ro.monkey=1',
190 'ro.test_harness=1',
191 'ro.audio.silent=1',
192 'ro.setupwizard.mode=DISABLED',
194 if java_debug:
195 local_props.append(
196 '%s=all' % device_utils.DeviceUtils.JAVA_ASSERT_PROPERTY)
197 local_props.append('debug.checkjni=1')
198 try:
199 device.WriteFile(
200 constants.DEVICE_LOCAL_PROPERTIES_PATH,
201 '\n'.join(local_props), as_root=True)
202 # Android will not respect the local props file if it is world writable.
203 device.RunShellCommand(
204 ['chmod', '644', constants.DEVICE_LOCAL_PROPERTIES_PATH],
205 as_root=True, check_return=True)
206 except device_errors.CommandFailedError:
207 logging.exception('Failed to configure local properties.')
210 def FinishProvisioning(device, options):
211 if options.min_battery_level is not None:
212 try:
213 battery = battery_utils.BatteryUtils(device)
214 battery.ChargeDeviceToLevel(options.min_battery_level)
215 except device_errors.CommandFailedError:
216 logging.exception('Unable to charge device to specified level.')
218 if options.max_battery_temp is not None:
219 try:
220 battery = battery_utils.BatteryUtils(device)
221 battery.LetBatteryCoolToTemperature(options.max_battery_temp)
222 except device_errors.CommandFailedError:
223 logging.exception('Unable to let battery cool to specified temperature.')
225 device.RunShellCommand(
226 ['date', '-s', time.strftime('%Y%m%d.%H%M%S', time.gmtime())],
227 as_root=True, check_return=True)
228 props = device.RunShellCommand('getprop', check_return=True)
229 for prop in props:
230 logging.info(' %s' % prop)
231 if options.auto_reconnect:
232 _PushAndLaunchAdbReboot(device, options.target)
235 def _PushAndLaunchAdbReboot(device, target):
236 """Pushes and launches the adb_reboot binary on the device.
238 Arguments:
239 device: The DeviceUtils instance for the device to which the adb_reboot
240 binary should be pushed.
241 target: The build target (example, Debug or Release) which helps in
242 locating the adb_reboot binary.
244 logging.info('Will push and launch adb_reboot on %s' % str(device))
245 # Kill if adb_reboot is already running.
246 device.KillAll('adb_reboot', blocking=True, timeout=2, quiet=True)
247 # Push adb_reboot
248 logging.info(' Pushing adb_reboot ...')
249 adb_reboot = os.path.join(constants.DIR_SOURCE_ROOT,
250 'out/%s/adb_reboot' % target)
251 device.PushChangedFiles([(adb_reboot, '/data/local/tmp/')])
252 # Launch adb_reboot
253 logging.info(' Launching adb_reboot ...')
254 device.RunShellCommand(
255 ['/data/local/tmp/adb_reboot'],
256 check_return=True)
259 def _LaunchHostHeartbeat():
260 # Kill if existing host_heartbeat
261 KillHostHeartbeat()
262 # Launch a new host_heartbeat
263 logging.info('Spawning host heartbeat...')
264 subprocess.Popen([os.path.join(constants.DIR_SOURCE_ROOT,
265 'build/android/host_heartbeat.py')])
268 def KillHostHeartbeat():
269 ps = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
270 stdout, _ = ps.communicate()
271 matches = re.findall('\\n.*host_heartbeat.*', stdout)
272 for match in matches:
273 logging.info('An instance of host heart beart running... will kill')
274 pid = re.findall(r'(\S+)', match)[1]
275 subprocess.call(['kill', str(pid)])
278 def main():
279 # Recommended options on perf bots:
280 # --disable-network
281 # TODO(tonyg): We eventually want network on. However, currently radios
282 # can cause perfbots to drain faster than they charge.
283 # --min-battery-level 95
284 # Some perf bots run benchmarks with USB charging disabled which leads
285 # to gradual draining of the battery. We must wait for a full charge
286 # before starting a run in order to keep the devices online.
288 parser = argparse.ArgumentParser(
289 description='Provision Android devices with settings required for bots.')
290 parser.add_argument('-d', '--device', metavar='SERIAL',
291 help='the serial number of the device to be provisioned'
292 ' (the default is to provision all devices attached)')
293 parser.add_argument('--phase', action='append', choices=_PHASES.ALL,
294 dest='phases',
295 help='Phases of provisioning to run. '
296 '(If omitted, all phases will be run.)')
297 parser.add_argument('--skip-wipe', action='store_true', default=False,
298 help="don't wipe device data during provisioning")
299 parser.add_argument('--reboot-timeout', metavar='SECS', type=int,
300 help='when wiping the device, max number of seconds to'
301 ' wait after each reboot '
302 '(default: %s)' % _DEFAULT_TIMEOUTS.HELP_TEXT)
303 parser.add_argument('--min-battery-level', type=int, metavar='NUM',
304 help='wait for the device to reach this minimum battery'
305 ' level before trying to continue')
306 parser.add_argument('--disable-location', action='store_true',
307 help='disable Google location services on devices')
308 parser.add_argument('--disable-network', action='store_true',
309 help='disable network access on devices')
310 parser.add_argument('--disable-java-debug', action='store_false',
311 dest='enable_java_debug', default=True,
312 help='disable Java property asserts and JNI checking')
313 parser.add_argument('-t', '--target', default='Debug',
314 help='the build target (default: %(default)s)')
315 parser.add_argument('-r', '--auto-reconnect', action='store_true',
316 help='push binary which will reboot the device on adb'
317 ' disconnections')
318 parser.add_argument('--adb-key-files', type=str, nargs='+',
319 help='list of adb keys to push to device')
320 parser.add_argument('-v', '--verbose', action='count', default=1,
321 help='Log more information.')
322 parser.add_argument('--max-battery-temp', type=int, metavar='NUM',
323 help='Wait for the battery to have this temp or lower.')
324 parser.add_argument('--output-device-blacklist',
325 help='Json file to output the device blacklist.')
326 args = parser.parse_args()
327 constants.SetBuildType(args.target)
329 run_tests_helper.SetLogLevel(args.verbose)
331 return ProvisionDevices(args)
334 if __name__ == '__main__':
335 sys.exit(main())