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

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

Issue 885443002: Roll Chrome into Mojo. (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Rebase to ToT mojo Created 5 years, 10 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
« no previous file with comments | « build/android/ndk.gyp ('k') | build/android/pylib/base/test_instance_factory.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # 2 #
3 # Copyright (c) 2013 The Chromium Authors. All rights reserved. 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 4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file. 5 # found in the LICENSE file.
6 6
7 """Provisions Android devices with settings required for bots. 7 """Provisions Android devices with settings required for bots.
8 8
9 Usage: 9 Usage:
10 ./provision_devices.py [-d <device serial number>] 10 ./provision_devices.py [-d <device serial number>]
11 """ 11 """
12 12
13 import argparse
13 import logging 14 import logging
14 import optparse
15 import os 15 import os
16 import re 16 import re
17 import subprocess 17 import subprocess
18 import sys 18 import sys
19 import time 19 import time
20 20
21 from pylib import android_commands 21 from pylib import android_commands
22 from pylib import constants 22 from pylib import constants
23 from pylib import device_settings 23 from pylib import device_settings
24 from pylib.device import device_blacklist 24 from pylib.device import device_blacklist
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
72 logging.info(' Pushing adb_reboot ...') 72 logging.info(' Pushing adb_reboot ...')
73 adb_reboot = os.path.join(constants.DIR_SOURCE_ROOT, 73 adb_reboot = os.path.join(constants.DIR_SOURCE_ROOT,
74 'out/%s/adb_reboot' % target) 74 'out/%s/adb_reboot' % target)
75 device.PushChangedFiles([(adb_reboot, '/data/local/tmp/')]) 75 device.PushChangedFiles([(adb_reboot, '/data/local/tmp/')])
76 # Launch adb_reboot 76 # Launch adb_reboot
77 logging.info(' Launching adb_reboot ...') 77 logging.info(' Launching adb_reboot ...')
78 device.old_interface.GetAndroidToolStatusAndOutput( 78 device.old_interface.GetAndroidToolStatusAndOutput(
79 '/data/local/tmp/adb_reboot') 79 '/data/local/tmp/adb_reboot')
80 80
81 81
82 def _ConfigureLocalProperties(device, is_perf): 82 def _ConfigureLocalProperties(device, java_debug=True):
83 """Set standard readonly testing device properties prior to reboot.""" 83 """Set standard readonly testing device properties prior to reboot."""
84 local_props = [ 84 local_props = [
85 'persist.sys.usb.config=adb', 85 'persist.sys.usb.config=adb',
86 'ro.monkey=1', 86 'ro.monkey=1',
87 'ro.test_harness=1', 87 'ro.test_harness=1',
88 'ro.audio.silent=1', 88 'ro.audio.silent=1',
89 'ro.setupwizard.mode=DISABLED', 89 'ro.setupwizard.mode=DISABLED',
90 ] 90 ]
91 if not is_perf: 91 if java_debug:
92 local_props.append('%s=all' % android_commands.JAVA_ASSERT_PROPERTY) 92 local_props.append('%s=all' % android_commands.JAVA_ASSERT_PROPERTY)
93 local_props.append('debug.checkjni=1') 93 local_props.append('debug.checkjni=1')
94 try: 94 try:
95 device.WriteFile( 95 device.WriteFile(
96 constants.DEVICE_LOCAL_PROPERTIES_PATH, 96 constants.DEVICE_LOCAL_PROPERTIES_PATH,
97 '\n'.join(local_props), as_root=True) 97 '\n'.join(local_props), as_root=True)
98 # Android will not respect the local props file if it is world writable. 98 # Android will not respect the local props file if it is world writable.
99 device.RunShellCommand( 99 device.RunShellCommand(
100 'chmod 644 %s' % constants.DEVICE_LOCAL_PROPERTIES_PATH, 100 ['chmod', '644', constants.DEVICE_LOCAL_PROPERTIES_PATH],
101 as_root=True) 101 as_root=True)
102 except device_errors.CommandFailedError as e: 102 except device_errors.CommandFailedError as e:
103 logging.warning(str(e)) 103 logging.warning(str(e))
104 104
105 # LOCAL_PROPERTIES_PATH = '/data/local.prop' 105 # LOCAL_PROPERTIES_PATH = '/data/local.prop'
106 106
107 107
108 def WipeDeviceData(device): 108 def WipeDeviceData(device):
109 """Wipes data from device, keeping only the adb_keys for authorization. 109 """Wipes data from device, keeping only the adb_keys for authorization.
110 110
111 After wiping data on a device that has been authorized, adb can still 111 After wiping data on a device that has been authorized, adb can still
112 communicate with the device, but after reboot the device will need to be 112 communicate with the device, but after reboot the device will need to be
113 re-authorized because the adb keys file is stored in /data/misc/adb/. 113 re-authorized because the adb keys file is stored in /data/misc/adb/.
114 Thus, adb_keys file is rewritten so the device does not need to be 114 Thus, adb_keys file is rewritten so the device does not need to be
115 re-authorized. 115 re-authorized.
116 116
117 Arguments: 117 Arguments:
118 device: the device to wipe 118 device: the device to wipe
119 """ 119 """
120 device_authorized = device.FileExists(constants.ADB_KEYS_FILE) 120 device_authorized = device.FileExists(constants.ADB_KEYS_FILE)
121 if device_authorized: 121 if device_authorized:
122 adb_keys = device.RunShellCommand('cat %s' % constants.ADB_KEYS_FILE, 122 adb_keys = device.ReadFile(constants.ADB_KEYS_FILE, as_root=True)
123 as_root=True)
124 device.RunShellCommand('wipe data', as_root=True) 123 device.RunShellCommand('wipe data', as_root=True)
125 if device_authorized: 124 if device_authorized:
126 path_list = constants.ADB_KEYS_FILE.split('/') 125 path_list = constants.ADB_KEYS_FILE.split('/')
127 dir_path = '/'.join(path_list[:len(path_list)-1]) 126 dir_path = '/'.join(path_list[:len(path_list)-1])
128 device.RunShellCommand('mkdir -p %s' % dir_path, as_root=True) 127 device.RunShellCommand('mkdir -p %s' % dir_path, as_root=True)
129 device.RunShellCommand('restorecon %s' % dir_path, as_root=True) 128 device.RunShellCommand('restorecon %s' % dir_path, as_root=True)
130 device.RunShellCommand('echo %s > %s' % 129 device.WriteFile(constants.ADB_KEYS_FILE, adb_keys, as_root=True)
131 (adb_keys[0], constants.ADB_KEYS_FILE), as_root=True)
132 for adb_key in adb_keys[1:]:
133 device.RunShellCommand(
134 'echo %s >> %s' % (adb_key, constants.ADB_KEYS_FILE), as_root=True)
135 device.RunShellCommand('restorecon %s' % constants.ADB_KEYS_FILE, 130 device.RunShellCommand('restorecon %s' % constants.ADB_KEYS_FILE,
136 as_root=True) 131 as_root=True)
137 132
138 133
139 def WipeDeviceIfPossible(device): 134 def WipeDeviceIfPossible(device, timeout):
140 try: 135 try:
141 device.EnableRoot() 136 device.EnableRoot()
142 WipeDeviceData(device) 137 WipeDeviceData(device)
143 # TODO(jbudorick): Tune the timeout per OS version. 138 device.Reboot(True, timeout=timeout, retries=0)
144 device.Reboot(True, timeout=600, retries=0)
145 except (errors.DeviceUnresponsiveError, device_errors.CommandFailedError): 139 except (errors.DeviceUnresponsiveError, device_errors.CommandFailedError):
146 pass 140 pass
147 141
148 142
149 def ProvisionDevice(device, options, is_perf): 143 def ProvisionDevice(device, options):
150 try: 144 try:
151 if not options.skip_wipe: 145 if not options.skip_wipe:
152 WipeDeviceIfPossible(device) 146 WipeDeviceIfPossible(device, options.reboot_timeout)
153 try: 147 try:
154 device.EnableRoot() 148 device.EnableRoot()
155 except device_errors.CommandFailedError as e: 149 except device_errors.CommandFailedError as e:
156 logging.warning(str(e)) 150 logging.warning(str(e))
157 _ConfigureLocalProperties(device, is_perf) 151 _ConfigureLocalProperties(device, options.enable_java_debug)
158 device_settings.ConfigureContentSettings( 152 device_settings.ConfigureContentSettings(
159 device, device_settings.DETERMINISTIC_DEVICE_SETTINGS) 153 device, device_settings.DETERMINISTIC_DEVICE_SETTINGS)
160 if options.disable_location: 154 if options.disable_location:
161 device_settings.ConfigureContentSettings( 155 device_settings.ConfigureContentSettings(
162 device, device_settings.DISABLE_LOCATION_SETTINGS) 156 device, device_settings.DISABLE_LOCATION_SETTINGS)
163 else: 157 else:
164 device_settings.ConfigureContentSettings( 158 device_settings.ConfigureContentSettings(
165 device, device_settings.ENABLE_LOCATION_SETTINGS) 159 device, device_settings.ENABLE_LOCATION_SETTINGS)
166 device_settings.SetLockScreenSettings(device) 160 device_settings.SetLockScreenSettings(device)
167 if is_perf: 161 if options.disable_network:
168 # TODO(tonyg): We eventually want network on. However, currently radios
169 # can cause perfbots to drain faster than they charge.
170 device_settings.ConfigureContentSettings( 162 device_settings.ConfigureContentSettings(
171 device, device_settings.NETWORK_DISABLED_SETTINGS) 163 device, device_settings.NETWORK_DISABLED_SETTINGS)
172 # Some perf bots run benchmarks with USB charging disabled which leads 164 if options.wait_for_battery:
173 # to gradual draining of the battery. We must wait for a full charge
174 # before starting a run in order to keep the devices online.
175 try: 165 try:
176 battery_info = device.old_interface.GetBatteryInfo() 166 battery_info = device.old_interface.GetBatteryInfo()
177 except Exception as e: 167 except Exception as e:
178 battery_info = {} 168 battery_info = {}
179 logging.error('Unable to obtain battery info for %s, %s', 169 logging.error('Unable to obtain battery info for %s, %s',
180 str(device), e) 170 str(device), e)
181 171
182 while int(battery_info.get('level', 100)) < options.min_battery_level: 172 while int(battery_info.get('level', 100)) < options.min_battery_level:
183 if not device.old_interface.IsDeviceCharging(): 173 if not device.old_interface.IsDeviceCharging():
184 if device.old_interface.CanControlUsbCharging(): 174 if device.old_interface.CanControlUsbCharging():
185 device.old_interface.EnableUsbCharging() 175 device.old_interface.EnableUsbCharging()
186 else: 176 else:
187 logging.error('Device is not charging') 177 logging.error('Device is not charging')
188 break 178 break
189 logging.info('Waiting for device to charge. Current level=%s', 179 logging.info('Waiting for device to charge. Current level=%s',
190 battery_info.get('level', 0)) 180 battery_info.get('level', 0))
191 time.sleep(60) 181 time.sleep(60)
192 battery_info = device.old_interface.GetBatteryInfo() 182 battery_info = device.old_interface.GetBatteryInfo()
193 if not options.skip_wipe: 183 if not options.skip_wipe:
194 # TODO(jbudorick): Tune the timeout per OS version. 184 device.Reboot(True, timeout=options.reboot_timeout, retries=0)
195 device.Reboot(True, timeout=600, retries=0)
196 device.RunShellCommand('date -s %s' % time.strftime('%Y%m%d.%H%M%S', 185 device.RunShellCommand('date -s %s' % time.strftime('%Y%m%d.%H%M%S',
197 time.gmtime()), 186 time.gmtime()),
198 as_root=True) 187 as_root=True)
199 props = device.RunShellCommand('getprop') 188 props = device.RunShellCommand('getprop')
200 for prop in props: 189 for prop in props:
201 logging.info(' %s' % prop) 190 logging.info(' %s' % prop)
202 if options.auto_reconnect: 191 if options.auto_reconnect:
203 PushAndLaunchAdbReboot(device, options.target) 192 PushAndLaunchAdbReboot(device, options.target)
204 except (errors.WaitForResponseTimedOutError, 193 except (errors.WaitForResponseTimedOutError,
205 device_errors.CommandTimeoutError): 194 device_errors.CommandTimeoutError):
206 logging.info('Timed out waiting for device %s. Adding to blacklist.', 195 logging.info('Timed out waiting for device %s. Adding to blacklist.',
207 str(device)) 196 str(device))
208 # Device black list is reset by bb_device_status_check.py per build. 197 # Device black list is reset by bb_device_status_check.py per build.
209 device_blacklist.ExtendBlacklist([str(device)]) 198 device_blacklist.ExtendBlacklist([str(device)])
210 except device_errors.CommandFailedError: 199 except device_errors.CommandFailedError:
211 logging.exception('Failed to provision device %s. Adding to blacklist.', 200 logging.exception('Failed to provision device %s. Adding to blacklist.',
212 str(device)) 201 str(device))
213 device_blacklist.ExtendBlacklist([str(device)]) 202 device_blacklist.ExtendBlacklist([str(device)])
214 203
215 204
216 def ProvisionDevices(options): 205 def ProvisionDevices(options):
217 is_perf = 'perf' in os.environ.get('BUILDBOT_BUILDERNAME', '').lower()
218 if options.device is not None: 206 if options.device is not None:
219 devices = [options.device] 207 devices = [options.device]
220 else: 208 else:
221 devices = android_commands.GetAttachedDevices() 209 devices = android_commands.GetAttachedDevices()
222 210
223 parallel_devices = device_utils.DeviceUtils.parallel(devices) 211 parallel_devices = device_utils.DeviceUtils.parallel(devices)
224 parallel_devices.pMap(ProvisionDevice, options, is_perf) 212 parallel_devices.pMap(ProvisionDevice, options)
225 if options.auto_reconnect: 213 if options.auto_reconnect:
226 LaunchHostHeartbeat() 214 LaunchHostHeartbeat()
227 blacklist = device_blacklist.ReadBlacklist() 215 blacklist = device_blacklist.ReadBlacklist()
228 if all(d in blacklist for d in devices): 216 if all(d in blacklist for d in devices):
229 raise device_errors.NoDevicesError 217 raise device_errors.NoDevicesError
230 return 0 218 return 0
231 219
232 220
233 def main(argv): 221 def main():
234 custom_handler = logging.StreamHandler(sys.stdout) 222 custom_handler = logging.StreamHandler(sys.stdout)
235 custom_handler.setFormatter(run_tests_helper.CustomFormatter()) 223 custom_handler.setFormatter(run_tests_helper.CustomFormatter())
236 logging.getLogger().addHandler(custom_handler) 224 logging.getLogger().addHandler(custom_handler)
237 logging.getLogger().setLevel(logging.INFO) 225 logging.getLogger().setLevel(logging.INFO)
238 226
239 parser = optparse.OptionParser() 227 # TODO(perezju): This script used to rely on the builder name to determine
240 parser.add_option('--min-battery-level', default=95, type='int', 228 # the desired device configuration for perf bots. To safely phase this out,
241 help="Minimum battery level for performance testing " 229 # we now:
242 "(default: 95).") 230 # - expose these configuration settings as command line options
243 parser.add_option('--skip-wipe', action='store_true', default=False, 231 # - set default values for these options based on the builder name, thus
244 help="Don't wipe device data during provisioning.") 232 # matching the previous behaviour of the script on all bots.
245 parser.add_option('--disable-location', action='store_true', default=False, 233 # - explicitly adding these options on the perf bots will also maintain the
246 help="Disallow Google location services on devices.") 234 # script behaviour, namely:
247 parser.add_option('-d', '--device', 235 # --wait-for-battery --disable-network --disable-java-debug
248 help='The serial number of the device to be provisioned') 236 # - after all perf-bot recipes are updated, we can remove the following
249 parser.add_option('-t', '--target', default='Debug', help='The build target') 237 # builder-name-sniffing code and replace |is_perf| with |False|.
250 parser.add_option( 238 is_perf = 'perf' in os.environ.get('BUILDBOT_BUILDERNAME', '').lower()
251 '-r', '--auto-reconnect', action='store_true',
252 help='Push binary which will reboot the device on adb disconnections.')
253 options, args = parser.parse_args(argv[1:])
254 constants.SetBuildType(options.target)
255 239
256 if args: 240 # Recommended options on perf bots:
257 print >> sys.stderr, 'Unused args %s' % args 241 # --disable-network
258 return 1 242 # TODO(tonyg): We eventually want network on. However, currently radios
243 # can cause perfbots to drain faster than they charge.
244 # --wait-for-battery
245 # Some perf bots run benchmarks with USB charging disabled which leads
246 # to gradual draining of the battery. We must wait for a full charge
247 # before starting a run in order to keep the devices online.
259 248
260 return ProvisionDevices(options) 249 parser = argparse.ArgumentParser(
250 description='Provision Android devices with settings required for bots.')
251 parser.add_argument('-d', '--device', metavar='SERIAL',
252 help='the serial number of the device to be provisioned'
253 ' (the default is to provision all devices attached)')
254 parser.add_argument('--skip-wipe', action='store_true', default=False,
255 help="don't wipe device data during provisioning")
256 parser.add_argument('--reboot-timeout', default=600, type=int,
257 metavar='SECS',
258 help='when wiping the device, max number of seconds to'
259 ' wait after each reboot (default: %(default)s)')
260 parser.add_argument('--wait-for-battery', action='store_true',
261 default=is_perf,
262 help='wait for the battery on the devices to charge')
263 parser.add_argument('--min-battery-level', default=95, type=int,
264 metavar='NUM',
265 help='when waiting for battery, minimum battery level'
266 ' required to continue (default: %(default)s)')
267 parser.add_argument('--disable-location', action='store_true',
268 help='disable Google location services on devices')
269 parser.add_argument('--disable-network', action='store_true',
270 default=is_perf,
271 help='disable network access on devices')
272 parser.add_argument('--disable-java-debug', action='store_false',
273 dest='enable_java_debug', default=not is_perf,
274 help='disable Java property asserts and JNI checking')
275 parser.add_argument('-t', '--target', default='Debug',
276 help='the build target (default: %(default)s)')
277 parser.add_argument('-r', '--auto-reconnect', action='store_true',
278 help='push binary which will reboot the device on adb'
279 ' disconnections')
280 args = parser.parse_args()
281 constants.SetBuildType(args.target)
282
283 return ProvisionDevices(args)
261 284
262 285
263 if __name__ == '__main__': 286 if __name__ == '__main__':
264 sys.exit(main(sys.argv)) 287 sys.exit(main())
OLDNEW
« no previous file with comments | « build/android/ndk.gyp ('k') | build/android/pylib/base/test_instance_factory.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698