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

Side by Side Diff: appengine/swarming/swarming_bot/api/platforms/android.py

Issue 1396603003: Small improvements to api.platforms.android. (Closed) Base URL: git@github.com:luci/luci-py.git@master
Patch Set: Trimmed down Created 5 years, 2 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 | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2015 The Swarming Authors. All rights reserved. 1 # Copyright 2015 The Swarming Authors. All rights reserved.
2 # Use of this source code is governed by the Apache v2.0 license that can be 2 # Use of this source code is governed by the Apache v2.0 license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """Android specific utility functions. 5 """Android specific utility functions.
6 6
7 This file serves as an API to bot_config.py. bot_config.py can be replaced on 7 This file serves as an API to bot_config.py. bot_config.py can be replaced on
8 the server to allow additional server-specific functionality. 8 the server to allow additional server-specific functionality.
9 """ 9 """
10 10
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
45 def __init__(self): 45 def __init__(self):
46 self._buf = '' 46 self._buf = ''
47 def update(self, msg): 47 def update(self, msg):
48 self._buf += msg 48 self._buf += msg
49 def digest(self): 49 def digest(self):
50 return self._buf 50 return self._buf
51 pkcs1.HASH_METHODS['SHA-1-PREHASHED'] = _Accum 51 pkcs1.HASH_METHODS['SHA-1-PREHASHED'] = _Accum
52 pkcs1.HASH_ASN1['SHA-1-PREHASHED'] = pkcs1.HASH_ASN1['SHA-1'] 52 pkcs1.HASH_ASN1['SHA-1-PREHASHED'] = pkcs1.HASH_ASN1['SHA-1']
53 53
54 54
55 # Set when ADB is initialized. It contains one or multiple key used to 55 # Both following are set when ADB is initialized.
56 # authenticate to Android debug protocol (adb). 56 # _ADB_KEYS is set to a list of _PythonRSASigner instances. It contains one or
57 # multiple key used to authenticate to Android debug protocol (adb).
57 _ADB_KEYS = None 58 _ADB_KEYS = None
59 # _ADB_KEYS_RAW is set to a dict(pub: priv) when initialized, with the raw
60 # content of each key.
61 _ADB_KEYS_RAW = None
58 62
59 63
60 # Cache of /system/build.prop on Android devices connected to this host. 64 # Cache of /system/build.prop on Android devices connected to this host.
61 _BUILD_PROP_ANDROID = {} 65 _BUILD_PROP_ANDROID = {}
62 66
63 67
64 class _PythonRSASigner(object): 68 class _PythonRSASigner(object):
65 """Implements adb_protocol.AuthSigner using http://stuvel.eu/rsa.""" 69 """Implements adb_protocol.AuthSigner using http://stuvel.eu/rsa."""
66 def __init__(self, pub, priv): 70 def __init__(self, pub, priv):
67 self.priv_key = _load_rsa_private_key(priv) 71 self.priv_key = _load_rsa_private_key(priv)
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
178 logging.warning( 182 logging.warning(
179 'Trying unpluging and pluging it back: %s: %s', port_path, e) 183 'Trying unpluging and pluging it back: %s: %s', port_path, e)
180 # Always create a Device, even if it points to nothing. It makes using the 184 # Always create a Device, even if it points to nothing. It makes using the
181 # client code much easier. 185 # client code much easier.
182 return Device(cmd, port_path) 186 return Device(cmd, port_path)
183 187
184 188
185 ### Public API. 189 ### Public API.
186 190
187 191
192 # List of known CPU scaling governor values.
193 KNOWN_CPU_SCALING_GOVERNOR_VALUES = (
194 'conservative', # Not available on Nexus 10
195 'interactive', # Default on Nexus 10.
196 'ondemand', # Not available on Nexus 10. Default on Nexus 4 and later.
197 'performance',
198 'powersave', # Not available on Nexus 10.
199 'userspace',
200 )
201
202
188 class Device(object): 203 class Device(object):
189 """Wraps an AdbCommands to make it exception safe. 204 """Wraps an AdbCommands to make it exception safe.
190 205
191 The fact that exceptions can be thrown any time makes the client code really 206 The fact that exceptions can be thrown any time makes the client code really
192 hard to write safely. Convert USBError* to None return value. 207 hard to write safely. Convert USBError* to None return value.
193 """ 208 """
194 def __init__(self, adb_cmd, port_path): 209 def __init__(self, adb_cmd, port_path):
195 if adb_cmd: 210 if adb_cmd:
196 assert isinstance(adb_cmd, adb.adb_commands.AdbCommands), adb_cmd 211 assert isinstance(adb_cmd, adb.adb_commands.AdbCommands), adb_cmd
197 self.adb_cmd = adb_cmd 212 self.adb_cmd = adb_cmd
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
278 return '', int(parts[0]) 293 return '', int(parts[0])
279 294
280 295
281 def initialize(pub_key, priv_key): 296 def initialize(pub_key, priv_key):
282 """Initialize Android support through adb. 297 """Initialize Android support through adb.
283 298
284 You can steal pub_key, priv_key pair from ~/.android/adbkey and 299 You can steal pub_key, priv_key pair from ~/.android/adbkey and
285 ~/.android/adbkey.pub. 300 ~/.android/adbkey.pub.
286 """ 301 """
287 global _ADB_KEYS 302 global _ADB_KEYS
303 global _ADB_KEYS_RAW
304 if _ADB_KEYS is not None:
305 assert False, 'initialize() was called repeatedly: ignoring keys'
288 assert bool(pub_key) == bool(priv_key) 306 assert bool(pub_key) == bool(priv_key)
289 if _ADB_KEYS is None: 307 pub_key = pub_key.strip() if pub_key else pub_key
290 _ADB_KEYS = [] 308 priv_key = priv_key.strip() if priv_key else priv_key
291 if pub_key:
292 try:
293 _ADB_KEYS.append(_PythonRSASigner(pub_key, priv_key))
294 except ValueError as exc:
295 logging.warning('Ignoring adb private key: %s', exc)
296 309
297 # Try to add local adb keys if available. 310 _ADB_KEYS = []
298 path = os.path.expanduser('~/.android/adbkey') 311 _ADB_KEYS_RAW = {}
299 if os.path.isfile(path) and os.path.isfile(path+'.pub'): 312 if pub_key:
300 with open(path + '.pub', 'rb') as f: 313 _ADB_KEYS.append(_PythonRSASigner(pub_key, priv_key))
301 pub = f.read() 314 _ADB_KEYS_RAW[pub_key] = priv_key
302 with open(path, 'rb') as f:
303 priv = f.read()
304 try:
305 _ADB_KEYS.append(_PythonRSASigner(pub, priv))
306 except ValueError as exc:
307 logging.warning('Ignoring adb private key %s: %s', path, exc)
308 315
309 if not _ADB_KEYS: 316 # Try to add local adb keys if available.
310 return False 317 path = os.path.expanduser('~/.android/adbkey')
311 else: 318 if os.path.isfile(path) and os.path.isfile(path + '.pub'):
312 if pub_key: 319 with open(path + '.pub', 'rb') as f:
313 logging.warning('initialize() was called repeatedly: ignoring keys') 320 pub = f.read().strip()
321 with open(path, 'rb') as f:
322 priv = f.read().strip()
323 _ADB_KEYS.append(_PythonRSASigner(pub, priv))
324 _ADB_KEYS_RAW[pub] = priv
325
314 return bool(_ADB_KEYS) 326 return bool(_ADB_KEYS)
315 327
316 328
317 def kill_adb(): 329 def kill_adb():
318 """Stops the adb daemon. 330 """Stops the adb daemon.
319 331
320 The Swarming bot doesn't use the Android's SDK adb. It uses python-adb to 332 The Swarming bot doesn't use the Android's SDK adb. It uses python-adb to
321 directly communicate with the devices. This works much better when a lot of 333 directly communicate with the devices. This works much better when a lot of
322 devices are connected to the host. 334 devices are connected to the host.
323 335
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
383 _BUILD_PROP_ANDROID.pop(key) 395 _BUILD_PROP_ANDROID.pop(key)
384 return devices 396 return devices
385 397
386 398
387 def close_devices(devices): 399 def close_devices(devices):
388 """Closes all devices opened by get_devices().""" 400 """Closes all devices opened by get_devices()."""
389 for _, device in sorted((devices or {}).iteritems()): 401 for _, device in sorted((devices or {}).iteritems()):
390 device.close() 402 device.close()
391 403
392 404
393 class CpuScalingGovernor(object):
394 """List of valid CPU scaling governor values."""
395 ON_DEMAND = 'ondemand'
396 PERFORMANCE = 'performance'
397 CONSERVATIVE = 'conservative'
398 POWER_SAVE = 'powersave'
399 USER_DEFINED = 'userspace'
400
401 @classmethod
402 def is_valid(cls, name):
403 return name in [getattr(cls, m) for m in dir(cls) if m[0].isupper()]
404
405
406 def set_cpu_scaling_governor(device, governor): 405 def set_cpu_scaling_governor(device, governor):
407 """Sets the CPU scaling governor to the one specified. 406 """Sets the CPU scaling governor to the one specified.
408 407
409 Returns: 408 Returns:
410 True on success. 409 True on success.
411 """ 410 """
412 assert isinstance(device, Device), device 411 assert isinstance(device, Device), device
413 assert CpuScalingGovernor.is_valid(governor), governor 412 assert governor in KNOWN_CPU_SCALING_GOVERNOR_VALUES, governor
414 _, exit_code = device.shell( 413 _, exit_code = device.shell(
415 'echo "%s">/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor' % 414 'echo "%s">/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor' %
416 governor) 415 governor)
417 return exit_code == 0 416 return exit_code == 0
418 417
419 418
420 def set_cpu_scaling(device, speed): 419 def set_cpu_scaling(device, speed):
421 """Enforces strict CPU speed and disable the CPU scaling governor. 420 """Enforces strict CPU speed and disable the CPU scaling governor.
422 421
423 Returns: 422 Returns:
424 True on success. 423 True on success.
425 """ 424 """
426 assert isinstance(device, Device), device 425 assert isinstance(device, Device), device
427 assert isinstance(speed, int), speed 426 assert isinstance(speed, int), speed
428 assert 10000 <= speed <= 10000000, speed 427 assert 10000 <= speed <= 10000000, speed
429 success = set_cpu_scaling_governor(device, CpuScalingGovernor.USER_DEFINED) 428 success = set_cpu_scaling_governor(device, 'userspace')
430 _, exit_code = device.shell( 429 _, exit_code = device.shell(
431 'echo "%d">/sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed' % 430 'echo "%d">/sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed' %
432 speed) 431 speed)
433 return success and exit_code == 0 432 return success and exit_code == 0
434 433
435 434
436 def get_build_prop(device): 435 def get_build_prop(device):
437 """Returns the system properties for a device. 436 """Returns the system properties for a device.
438 437
439 This isn't really changing through the lifetime of a bot. One corner case is 438 This isn't really changing through the lifetime of a bot. One corner case is
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after
571 570
572 def get_ip(device): 571 def get_ip(device):
573 """Returns the IP address.""" 572 """Returns the IP address."""
574 assert isinstance(device, Device), device 573 assert isinstance(device, Device), device
575 # If ever needed, read wifi.interface from /system/build.prop if a device 574 # If ever needed, read wifi.interface from /system/build.prop if a device
576 # doesn't use wlan0. 575 # doesn't use wlan0.
577 ip, _ = device.shell('getprop dhcp.wlan0.ipaddress') 576 ip, _ = device.shell('getprop dhcp.wlan0.ipaddress')
578 if not ip: 577 if not ip:
579 return None 578 return None
580 return ip.strip() 579 return ip.strip()
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698