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