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.
10 ./provision_devices.py [-d <device serial number>]
21 from pylib
import android_commands
22 from pylib
import constants
23 from pylib
import device_settings
24 from pylib
.device
import device_errors
25 from pylib
.device
import device_utils
27 sys
.path
.append(os
.path
.join(constants
.DIR_SOURCE_ROOT
,
28 'third_party', 'android_testrunner'))
31 def KillHostHeartbeat():
32 ps
= subprocess
.Popen(['ps', 'aux'], stdout
= subprocess
.PIPE
)
33 stdout
, _
= ps
.communicate()
34 matches
= re
.findall('\\n.*host_heartbeat.*', stdout
)
36 print 'An instance of host heart beart running... will kill'
37 pid
= re
.findall('(\d+)', match
)[1]
38 subprocess
.call(['kill', str(pid
)])
41 def LaunchHostHeartbeat():
42 # Kill if existing host_heartbeat
44 # Launch a new host_heartbeat
45 print 'Spawning host heartbeat...'
46 subprocess
.Popen([os
.path
.join(constants
.DIR_SOURCE_ROOT
,
47 'build/android/host_heartbeat.py')])
50 def PushAndLaunchAdbReboot(devices
, target
):
51 """Pushes and launches the adb_reboot binary on the device.
54 devices: The list of serial numbers of the device to which the
55 adb_reboot binary should be pushed.
56 target : The build target (example, Debug or Release) which helps in
57 locating the adb_reboot binary.
59 for device_serial
in devices
:
60 print 'Will push and launch adb_reboot on %s' % device_serial
61 device
= device_utils
.DeviceUtils(device_serial
)
62 # Kill if adb_reboot is already running.
64 # Don't try to kill adb_reboot more than once. We don't expect it to be
66 device
.KillAll('adb_reboot', blocking
=True, timeout
=2, retries
=0)
67 except device_errors
.CommandFailedError
:
68 # We can safely ignore the exception because we don't expect adb_reboot
72 print ' Pushing adb_reboot ...'
73 adb_reboot
= os
.path
.join(constants
.DIR_SOURCE_ROOT
,
74 'out/%s/adb_reboot' % target
)
75 device
.PushChangedFiles(adb_reboot
, '/data/local/tmp/')
77 print ' Launching adb_reboot ...'
78 device
.old_interface
.GetAndroidToolStatusAndOutput(
79 '/data/local/tmp/adb_reboot')
83 def _ConfigureLocalProperties(device
, is_perf
):
84 """Set standard readonly testing device properties prior to reboot."""
89 'ro.setupwizard.mode=DISABLED',
92 local_props
.append('%s=all' % android_commands
.JAVA_ASSERT_PROPERTY
)
93 local_props
.append('debug.checkjni=1')
95 constants
.DEVICE_LOCAL_PROPERTIES_PATH
,
96 '\n'.join(local_props
), as_root
=True)
97 # Android will not respect the local props file if it is world writable.
98 device
.RunShellCommand(
99 'chmod 644 %s' % constants
.DEVICE_LOCAL_PROPERTIES_PATH
,
102 # LOCAL_PROPERTIES_PATH = '/data/local.prop'
105 def WipeDeviceData(device
):
106 """Wipes data from device, keeping only the adb_keys for authorization.
108 After wiping data on a device that has been authorized, adb can still
109 communicate with the device, but after reboot the device will need to be
110 re-authorized because the adb keys file is stored in /data/misc/adb/.
111 Thus, adb_keys file is rewritten so the device does not need to be
115 device: the device to wipe
117 device_authorized
= device
.FileExists(constants
.ADB_KEYS_FILE
)
118 if device_authorized
:
119 adb_keys
= device
.RunShellCommand('cat %s' % constants
.ADB_KEYS_FILE
,
121 device
.RunShellCommand('wipe data', as_root
=True)
122 if device_authorized
:
123 path_list
= constants
.ADB_KEYS_FILE
.split('/')
124 dir_path
= '/'.join(path_list
[:len(path_list
)-1])
125 device
.RunShellCommand('mkdir -p %s' % dir_path
, as_root
=True)
126 device
.RunShellCommand('echo %s > %s' %
127 (adb_keys
[0], constants
.ADB_KEYS_FILE
))
128 for adb_key
in adb_keys
[1:]:
129 device
.RunShellCommand(
130 'echo %s >> %s' % (adb_key
, constants
.ADB_KEYS_FILE
))
133 def ProvisionDevices(options
):
134 is_perf
= 'perf' in os
.environ
.get('BUILDBOT_BUILDERNAME', '').lower()
135 # TODO(jbudorick): Parallelize provisioning of all attached devices after
136 # switching from AndroidCommands.
137 if options
.device
is not None:
138 devices
= [options
.device
]
140 devices
= android_commands
.GetAttachedDevices()
142 # Wipe devices (unless --skip-wipe was specified)
143 if not options
.skip_wipe
:
144 for device_serial
in devices
:
145 device
= device_utils
.DeviceUtils(device_serial
)
146 device
.old_interface
.EnableAdbRoot()
147 WipeDeviceData(device
)
149 device_utils
.DeviceUtils
.parallel(devices
).Reboot(True)
150 except errors
.DeviceUnresponsiveError
:
152 for device_serial
in devices
:
153 device
.WaitUntilFullyBooted(timeout
=90)
156 for device_serial
in devices
:
157 device
= device_utils
.DeviceUtils(device_serial
)
158 device
.old_interface
.EnableAdbRoot()
159 _ConfigureLocalProperties(device
, is_perf
)
160 device_settings_map
= device_settings
.DETERMINISTIC_DEVICE_SETTINGS
161 if options
.disable_location
:
162 device_settings_map
.update(device_settings
.DISABLE_LOCATION_SETTING
)
164 device_settings_map
.update(device_settings
.ENABLE_LOCATION_SETTING
)
165 device_settings
.ConfigureContentSettingsDict(device
, device_settings_map
)
166 device_settings
.SetLockScreenSettings(device
)
168 # TODO(tonyg): We eventually want network on. However, currently radios
169 # can cause perfbots to drain faster than they charge.
170 device_settings
.ConfigureContentSettingsDict(
171 device
, device_settings
.NETWORK_DISABLED_SETTINGS
)
172 # Some perf bots run benchmarks with USB charging disabled which leads
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.
176 battery_info
= device
.old_interface
.GetBatteryInfo()
177 except Exception as e
:
179 logging
.error('Unable to obtain battery info for %s, %s',
182 while int(battery_info
.get('level', 100)) < 95:
183 if not device
.old_interface
.IsDeviceCharging():
184 if device
.old_interface
.CanControlUsbCharging():
185 device
.old_interface
.EnableUsbCharging()
187 logging
.error('Device is not charging')
189 logging
.info('Waiting for device to charge. Current level=%s',
190 battery_info
.get('level', 0))
192 battery_info
= device
.old_interface
.GetBatteryInfo()
193 device
.RunShellCommand('date -u %f' % time
.time(), as_root
=True)
195 device_utils
.DeviceUtils
.parallel(devices
).Reboot(True)
196 except errors
.DeviceUnresponsiveError
:
198 for device_serial
in devices
:
199 device
= device_utils
.DeviceUtils(device_serial
)
200 device
.WaitUntilFullyBooted(timeout
=90)
201 (_
, props
) = device
.old_interface
.GetShellCommandStatusAndOutput('getprop')
204 if options
.auto_reconnect
:
205 PushAndLaunchAdbReboot(devices
, options
.target
)
209 logging
.basicConfig(level
=logging
.INFO
)
211 parser
= optparse
.OptionParser()
212 parser
.add_option('--skip-wipe', action
='store_true', default
=False,
213 help="Don't wipe device data during provisioning.")
214 parser
.add_option('--disable-location', action
='store_true', default
=False,
215 help="Disallow Google location services on devices.")
216 parser
.add_option('-d', '--device',
217 help='The serial number of the device to be provisioned')
218 parser
.add_option('-t', '--target', default
='Debug', help='The build target')
220 '-r', '--auto-reconnect', action
='store_true',
221 help='Push binary which will reboot the device on adb disconnections.')
222 options
, args
= parser
.parse_args(argv
[1:])
223 constants
.SetBuildType(options
.target
)
226 print >> sys
.stderr
, 'Unused args %s' % args
229 ProvisionDevices(options
)
232 if __name__
== '__main__':
233 sys
.exit(main(sys
.argv
))