Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(45)

Side by Side Diff: build/android/provision_devices.py

Issue 2101243005: Add a snapshot of flutter/engine/src/build to our sdk (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: add README.dart Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 #
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.
6
7 """Provisions Android devices with settings required for bots.
8
9 Usage:
10 ./provision_devices.py [-d <device serial number>]
11 """
12
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
22
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
31
32 sys.path.append(os.path.join(constants.DIR_SOURCE_ROOT,
33 'third_party', 'android_testrunner'))
34 import errors
35
36
37 class _DEFAULT_TIMEOUTS(object):
38 # L can take a while to reboot after a wipe.
39 LOLLIPOP = 600
40 PRE_LOLLIPOP = 180
41
42 HELP_TEXT = '{}s on L, {}s on pre-L'.format(LOLLIPOP, PRE_LOLLIPOP)
43
44
45 class _PHASES(object):
46 WIPE = 'wipe'
47 PROPERTIES = 'properties'
48 FINISH = 'finish'
49
50 ALL = [WIPE, PROPERTIES, FINISH]
51
52
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)
59
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
71
72
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
81
82 def should_run_phase(phase_name):
83 return not options.phases or phase_name in options.phases
84
85 def run_phase(phase_func, reboot=True):
86 try:
87 device.WaitUntilFullyBooted(timeout=reboot_timeout, retries=0)
88 except device_errors.CommandTimeoutError:
89 logging.error('Device did not finish booting. Will try to reboot.')
90 device.Reboot(timeout=reboot_timeout)
91 phase_func(device, options)
92 if reboot:
93 device.Reboot(False, retries=0)
94 device.adb.WaitForDevice()
95
96 try:
97 if should_run_phase(_PHASES.WIPE):
98 run_phase(WipeDevice)
99
100 if should_run_phase(_PHASES.PROPERTIES):
101 run_phase(SetProperties)
102
103 if should_run_phase(_PHASES.FINISH):
104 run_phase(FinishProvisioning, reboot=False)
105
106 except (errors.WaitForResponseTimedOutError,
107 device_errors.CommandTimeoutError):
108 logging.exception('Timed out waiting for device %s. Adding to blacklist.',
109 str(device))
110 device_blacklist.ExtendBlacklist([str(device)])
111
112 except device_errors.CommandFailedError:
113 logging.exception('Failed to provision device %s. Adding to blacklist.',
114 str(device))
115 device_blacklist.ExtendBlacklist([str(device)])
116
117
118 def WipeDevice(device, options):
119 """Wipes data from device, keeping only the adb_keys for authorization.
120
121 After wiping data on a device that has been authorized, adb can still
122 communicate with the device, but after reboot the device will need to be
123 re-authorized because the adb keys file is stored in /data/misc/adb/.
124 Thus, adb_keys file is rewritten so the device does not need to be
125 re-authorized.
126
127 Arguments:
128 device: the device to wipe
129 """
130 if options.skip_wipe:
131 return
132
133 try:
134 device.EnableRoot()
135 device_authorized = device.FileExists(constants.ADB_KEYS_FILE)
136 if device_authorized:
137 adb_keys = device.ReadFile(constants.ADB_KEYS_FILE,
138 as_root=True).splitlines()
139 device.RunShellCommand(['wipe', 'data'],
140 as_root=True, check_return=True)
141 device.adb.WaitForDevice()
142
143 if device_authorized:
144 adb_keys_set = set(adb_keys)
145 for adb_key_file in options.adb_key_files or []:
146 try:
147 with open(adb_key_file, 'r') as f:
148 adb_public_keys = f.readlines()
149 adb_keys_set.update(adb_public_keys)
150 except IOError:
151 logging.warning('Unable to find adb keys file %s.' % adb_key_file)
152 _WriteAdbKeysFile(device, '\n'.join(adb_keys_set))
153 except device_errors.CommandFailedError:
154 logging.exception('Possible failure while wiping the device. '
155 'Attempting to continue.')
156
157
158 def _WriteAdbKeysFile(device, adb_keys_string):
159 dir_path = posixpath.dirname(constants.ADB_KEYS_FILE)
160 device.RunShellCommand(['mkdir', '-p', dir_path],
161 as_root=True, check_return=True)
162 device.RunShellCommand(['restorecon', dir_path],
163 as_root=True, check_return=True)
164 device.WriteFile(constants.ADB_KEYS_FILE, adb_keys_string, as_root=True)
165 device.RunShellCommand(['restorecon', constants.ADB_KEYS_FILE],
166 as_root=True, check_return=True)
167
168
169 def SetProperties(device, options):
170 try:
171 device.EnableRoot()
172 except device_errors.CommandFailedError as e:
173 logging.warning(str(e))
174
175 _ConfigureLocalProperties(device, options.enable_java_debug)
176 device_settings.ConfigureContentSettings(
177 device, device_settings.DETERMINISTIC_DEVICE_SETTINGS)
178 if options.disable_location:
179 device_settings.ConfigureContentSettings(
180 device, device_settings.DISABLE_LOCATION_SETTINGS)
181 else:
182 device_settings.ConfigureContentSettings(
183 device, device_settings.ENABLE_LOCATION_SETTINGS)
184
185 if options.disable_mock_location:
186 device_settings.ConfigureContentSettings(
187 device, device_settings.DISABLE_MOCK_LOCATION_SETTINGS)
188 else:
189 device_settings.ConfigureContentSettings(
190 device, device_settings.ENABLE_MOCK_LOCATION_SETTINGS)
191
192 device_settings.SetLockScreenSettings(device)
193 if options.disable_network:
194 device_settings.ConfigureContentSettings(
195 device, device_settings.NETWORK_DISABLED_SETTINGS)
196
197 def _ConfigureLocalProperties(device, java_debug=True):
198 """Set standard readonly testing device properties prior to reboot."""
199 local_props = [
200 'persist.sys.usb.config=adb',
201 'ro.monkey=1',
202 'ro.test_harness=1',
203 'ro.audio.silent=1',
204 'ro.setupwizard.mode=DISABLED',
205 ]
206 if java_debug:
207 local_props.append(
208 '%s=all' % device_utils.DeviceUtils.JAVA_ASSERT_PROPERTY)
209 local_props.append('debug.checkjni=1')
210 try:
211 device.WriteFile(
212 constants.DEVICE_LOCAL_PROPERTIES_PATH,
213 '\n'.join(local_props), as_root=True)
214 # Android will not respect the local props file if it is world writable.
215 device.RunShellCommand(
216 ['chmod', '644', constants.DEVICE_LOCAL_PROPERTIES_PATH],
217 as_root=True, check_return=True)
218 except device_errors.CommandFailedError:
219 logging.exception('Failed to configure local properties.')
220
221
222 def FinishProvisioning(device, options):
223 if options.min_battery_level is not None:
224 try:
225 battery = battery_utils.BatteryUtils(device)
226 battery.ChargeDeviceToLevel(options.min_battery_level)
227 except device_errors.CommandFailedError:
228 logging.exception('Unable to charge device to specified level.')
229
230 if options.max_battery_temp is not None:
231 try:
232 battery = battery_utils.BatteryUtils(device)
233 battery.LetBatteryCoolToTemperature(options.max_battery_temp)
234 except device_errors.CommandFailedError:
235 logging.exception('Unable to let battery cool to specified temperature.')
236
237 device.RunShellCommand(
238 ['date', '-s', time.strftime('%Y%m%d.%H%M%S', time.gmtime())],
239 as_root=True, check_return=True)
240 props = device.RunShellCommand('getprop', check_return=True)
241 for prop in props:
242 logging.info(' %s' % prop)
243 if options.auto_reconnect:
244 _PushAndLaunchAdbReboot(device, options.target)
245
246
247 def _PushAndLaunchAdbReboot(device, target):
248 """Pushes and launches the adb_reboot binary on the device.
249
250 Arguments:
251 device: The DeviceUtils instance for the device to which the adb_reboot
252 binary should be pushed.
253 target: The build target (example, Debug or Release) which helps in
254 locating the adb_reboot binary.
255 """
256 logging.info('Will push and launch adb_reboot on %s' % str(device))
257 # Kill if adb_reboot is already running.
258 device.KillAll('adb_reboot', blocking=True, timeout=2, quiet=True)
259 # Push adb_reboot
260 logging.info(' Pushing adb_reboot ...')
261 adb_reboot = os.path.join(constants.DIR_SOURCE_ROOT,
262 'out/%s/adb_reboot' % target)
263 device.PushChangedFiles([(adb_reboot, '/data/local/tmp/')])
264 # Launch adb_reboot
265 logging.info(' Launching adb_reboot ...')
266 device.RunShellCommand(
267 ['/data/local/tmp/adb_reboot'],
268 check_return=True)
269
270
271 def _LaunchHostHeartbeat():
272 # Kill if existing host_heartbeat
273 KillHostHeartbeat()
274 # Launch a new host_heartbeat
275 logging.info('Spawning host heartbeat...')
276 subprocess.Popen([os.path.join(constants.DIR_SOURCE_ROOT,
277 'build/android/host_heartbeat.py')])
278
279
280 def KillHostHeartbeat():
281 ps = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
282 stdout, _ = ps.communicate()
283 matches = re.findall('\\n.*host_heartbeat.*', stdout)
284 for match in matches:
285 logging.info('An instance of host heart beart running... will kill')
286 pid = re.findall(r'(\S+)', match)[1]
287 subprocess.call(['kill', str(pid)])
288
289
290 def main():
291 # Recommended options on perf bots:
292 # --disable-network
293 # TODO(tonyg): We eventually want network on. However, currently radios
294 # can cause perfbots to drain faster than they charge.
295 # --min-battery-level 95
296 # Some perf bots run benchmarks with USB charging disabled which leads
297 # to gradual draining of the battery. We must wait for a full charge
298 # before starting a run in order to keep the devices online.
299
300 parser = argparse.ArgumentParser(
301 description='Provision Android devices with settings required for bots.')
302 parser.add_argument('-d', '--device', metavar='SERIAL',
303 help='the serial number of the device to be provisioned'
304 ' (the default is to provision all devices attached)')
305 parser.add_argument('--phase', action='append', choices=_PHASES.ALL,
306 dest='phases',
307 help='Phases of provisioning to run. '
308 '(If omitted, all phases will be run.)')
309 parser.add_argument('--skip-wipe', action='store_true', default=False,
310 help="don't wipe device data during provisioning")
311 parser.add_argument('--reboot-timeout', metavar='SECS', type=int,
312 help='when wiping the device, max number of seconds to'
313 ' wait after each reboot '
314 '(default: %s)' % _DEFAULT_TIMEOUTS.HELP_TEXT)
315 parser.add_argument('--min-battery-level', type=int, metavar='NUM',
316 help='wait for the device to reach this minimum battery'
317 ' level before trying to continue')
318 parser.add_argument('--disable-location', action='store_true',
319 help='disable Google location services on devices')
320 parser.add_argument('--disable-mock-location', action='store_true',
321 default=False, help='Set ALLOW_MOCK_LOCATION to false')
322 parser.add_argument('--disable-network', action='store_true',
323 help='disable network access on devices')
324 parser.add_argument('--disable-java-debug', action='store_false',
325 dest='enable_java_debug', default=True,
326 help='disable Java property asserts and JNI checking')
327 parser.add_argument('-t', '--target', default='Debug',
328 help='the build target (default: %(default)s)')
329 parser.add_argument('-r', '--auto-reconnect', action='store_true',
330 help='push binary which will reboot the device on adb'
331 ' disconnections')
332 parser.add_argument('--adb-key-files', type=str, nargs='+',
333 help='list of adb keys to push to device')
334 parser.add_argument('-v', '--verbose', action='count', default=1,
335 help='Log more information.')
336 parser.add_argument('--max-battery-temp', type=int, metavar='NUM',
337 help='Wait for the battery to have this temp or lower.')
338 parser.add_argument('--output-device-blacklist',
339 help='Json file to output the device blacklist.')
340 args = parser.parse_args()
341 constants.SetBuildType(args.target)
342
343 run_tests_helper.SetLogLevel(args.verbose)
344
345 return ProvisionDevices(args)
346
347
348 if __name__ == '__main__':
349 sys.exit(main())
OLDNEW
« no previous file with comments | « build/android/preprocess_google_play_services.config.json ('k') | build/android/push_libraries.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698