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

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

Issue 1401403002: Improve resiliency for Android. (Closed) Base URL: git@github.com:luci/luci-py.git@3_unicode
Patch Set: 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 | appengine/swarming/swarming_bot/api/platforms/android_test.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 # 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
11 import logging 11 import logging
12 import os 12 import os
13 import re 13 import re
14 import subprocess 14 import subprocess
15 import sys 15 import sys
16 import time 16 import time
17 17
18 # This file must be imported from swarming_bot.zip (or with '..' in sys.path). 18 # This file must be imported from swarming_bot.zip (or with '../..' in
19 # libusb1 must have been put in the path already. 19 # sys.path). libusb1 must have been put in the path already.
20 20
21 import adb 21 import adb
22 import adb.adb_commands 22 import adb.adb_commands
23 23
24 24
25 # Find abs path to third_party/ dir in the bot root dir. 25 # Find abs path to third_party/ dir in the bot root dir.
26 import third_party 26 import third_party
27 THIRD_PARTY_DIR = os.path.dirname(os.path.abspath(third_party.__file__)) 27 THIRD_PARTY_DIR = os.path.dirname(os.path.abspath(third_party.__file__))
28 sys.path.insert(0, os.path.join(THIRD_PARTY_DIR, 'rsa')) 28 sys.path.insert(0, os.path.join(THIRD_PARTY_DIR, 'rsa'))
29 sys.path.insert(0, os.path.join(THIRD_PARTY_DIR, 'pyasn1')) 29 sys.path.insert(0, os.path.join(THIRD_PARTY_DIR, 'pyasn1'))
(...skipping 419 matching lines...) Expand 10 before | Expand all | Expand 10 after
449 if line.startswith(u'#') or not line: 449 if line.startswith(u'#') or not line:
450 continue 450 continue
451 key, value = line.split(u'=', 1) 451 key, value = line.split(u'=', 1)
452 properties[key] = value 452 properties[key] = value
453 _BUILD_PROP_ANDROID[device.serial] = properties 453 _BUILD_PROP_ANDROID[device.serial] = properties
454 return _BUILD_PROP_ANDROID[device.serial] 454 return _BUILD_PROP_ANDROID[device.serial]
455 455
456 456
457 def get_temp(device): 457 def get_temp(device):
458 """Returns the device's 2 temperatures if available.""" 458 """Returns the device's 2 temperatures if available."""
459 assert isinstance(device, Device), device
459 # Not all devices export this file. On other devices, the only real way to 460 # Not all devices export this file. On other devices, the only real way to
460 # read it is via Java 461 # read it is via Java
461 # developer.android.com/guide/topics/sensors/sensors_environment.html 462 # developer.android.com/guide/topics/sensors/sensors_environment.html
462 temps = [] 463 temps = []
463 for i in xrange(2): 464 for i in xrange(2):
464 out, _ = device.shell('cat /sys/class/thermal/thermal_zone%d/temp' % i) 465 out, _ = device.shell('cat /sys/class/thermal/thermal_zone%d/temp' % i)
465 if out: 466 if out:
466 temps.append(int(out)) 467 try:
468 temps.append(int(out))
469 except ValueError:
470 pass
ghost stip (do not use) 2015/10/13 21:03:40 should we append a NaN instead? otherwise people w
M-A Ruel 2015/10/13 21:08:57 I prefer to not add invalid values; in this case t
ghost stip (do not use) 2015/10/13 21:21:08 you're doing for i in xrange(2): so if the first
M-A Ruel 2015/10/13 21:22:52 It is ok; the first sensor is on the CPU, the seco
467 return temps 471 return temps
468 472
469 473
470 def get_battery(device): 474 def get_battery(device):
471 """Returns details about the battery's state.""" 475 """Returns details about the battery's state."""
476 assert isinstance(device, Device), device
472 props = {} 477 props = {}
473 out = _dumpsys(device, 'battery') 478 out = _dumpsys(device, 'battery')
474 if not out: 479 if not out:
475 return props 480 return props
476 for line in out.splitlines(): 481 for line in out.splitlines():
477 if line.endswith(u':'): 482 if line.endswith(u':'):
478 continue 483 continue
479 # On Android 4.1.2, it uses "voltage:123" instead of "voltage: 123". 484 # On Android 4.1.2, it uses "voltage:123" instead of "voltage: 123".
480 key, value = line.split(u':', 2) 485 parts = line.split(u':', 2)
481 props[key.lstrip()] = value.strip() 486 if len(parts) == 2:
487 key, value = parts
488 props[key.lstrip()] = value.strip()
482 out = {u'power': []} 489 out = {u'power': []}
483 if props[u'AC powered'] == u'true': 490 if props.get(u'AC powered') == u'true':
484 out[u'power'].append(u'AC') 491 out[u'power'].append(u'AC')
485 if props[u'USB powered'] == u'true': 492 if props.get(u'USB powered') == u'true':
486 out[u'power'].append(u'USB') 493 out[u'power'].append(u'USB')
487 if props.get(u'Wireless powered') == u'true': 494 if props.get(u'Wireless powered') == u'true':
488 out[u'power'].append(u'Wireless') 495 out[u'power'].append(u'Wireless')
489 for key in (u'health', u'level', u'status', u'temperature', u'voltage'): 496 for key in (u'health', u'level', u'status', u'temperature', u'voltage'):
490 out[key] = int(props[key]) 497 out[key] = int(props[key])
491 return out 498 return out
492 499
493 500
494 def get_cpu_scale(device): 501 def get_cpu_scale(device):
495 """Returns the CPU scaling factor.""" 502 """Returns the CPU scaling factor."""
503 assert isinstance(device, Device), device
496 mapping = { 504 mapping = {
497 'cpuinfo_max_freq': u'max', 505 'cpuinfo_max_freq': u'max',
498 'cpuinfo_min_freq': u'min', 506 'cpuinfo_min_freq': u'min',
499 'scaling_cur_freq': u'cur', 507 'scaling_cur_freq': u'cur',
500 'scaling_governor': u'governor', 508 'scaling_governor': u'governor',
501 } 509 }
502 return { 510 return {
503 v: device.shell('cat /sys/devices/system/cpu/cpu0/cpufreq/' + k)[0] 511 v: device.shell('cat /sys/devices/system/cpu/cpu0/cpufreq/' + k)[0]
504 for k, v in mapping.iteritems() 512 for k, v in mapping.iteritems()
505 } 513 }
506 514
507 515
508 def get_uptime(device): 516 def get_uptime(device):
509 """Returns the device's uptime in second.""" 517 """Returns the device's uptime in second."""
518 assert isinstance(device, Device), device
510 out, _ = device.shell('cat /proc/uptime') 519 out, _ = device.shell('cat /proc/uptime')
511 if out: 520 if out:
512 return float(out.split()[1]) 521 return float(out.split()[1])
513 return None 522 return None
514 523
515 524
516 def get_disk(device): 525 def get_disk(device):
517 """Returns details about the battery's state.""" 526 """Returns details about the battery's state."""
527 assert isinstance(device, Device), device
518 props = {} 528 props = {}
519 out = _dumpsys(device, 'diskstats') 529 out = _dumpsys(device, 'diskstats')
520 if not out: 530 if not out:
521 return props 531 return props
522 for line in out.splitlines(): 532 for line in out.splitlines():
523 if line.endswith(u':'): 533 if line.endswith(u':'):
524 continue 534 continue
525 key, value = line.split(u': ', 2) 535 parts = line.split(u': ', 2)
526 match = re.match(u'^(\d+)K / (\d+)K.*', value) 536 if len(parts) == 2:
527 if match: 537 key, value = parts
528 props[key.lstrip()] = { 538 match = re.match(u'^(\d+)K / (\d+)K.*', value)
529 'free_mb': round(float(match.group(1)) / 1024., 1), 539 if match:
530 'size_mb': round(float(match.group(2)) / 1024., 1), 540 props[key.lstrip()] = {
531 } 541 'free_mb': round(float(match.group(1)) / 1024., 1),
542 'size_mb': round(float(match.group(2)) / 1024., 1),
543 }
532 return { 544 return {
533 u'cache': props[u'Cache-Free'], 545 u'cache': props[u'Cache-Free'],
534 u'data': props[u'Data-Free'], 546 u'data': props[u'Data-Free'],
535 u'system': props[u'System-Free'], 547 u'system': props[u'System-Free'],
536 } 548 }
537 549
538 550
539 def get_imei(device): 551 def get_imei(device):
540 """Returns the phone's IMEI.""" 552 """Returns the phone's IMEI."""
553 assert isinstance(device, Device), device
541 # Android <5.0. 554 # Android <5.0.
542 out = _dumpsys(device, 'iphonesubinfo') 555 out = _dumpsys(device, 'iphonesubinfo')
543 if out: 556 if out:
544 match = re.search(' Device ID = (.+)$', out) 557 match = re.search(' Device ID = (.+)$', out)
545 if match: 558 if match:
546 return match.group(1) 559 return match.group(1)
547 560
548 # Android >= 5.0. 561 # Android >= 5.0.
549 out, _ = device.shell('service call iphonesubinfo 1') 562 out, _ = device.shell('service call iphonesubinfo 1')
550 if out: 563 if out:
551 lines = out.splitlines() 564 lines = out.splitlines()
552 if len(lines) >= 4 and lines[0] == 'Result: Parcel(': 565 if len(lines) >= 4 and lines[0] == 'Result: Parcel(':
553 # Process the UTF-16 string. 566 # Process the UTF-16 string.
554 chars = _parcel_to_list(lines[1:])[4:-1] 567 chars = _parcel_to_list(lines[1:])[4:-1]
555 return u''.join(map(unichr, (int(i, 16) for i in chars))) 568 return u''.join(map(unichr, (int(i, 16) for i in chars)))
556 return None 569 return None
557 570
558 571
559 def get_ip(device): 572 def get_ip(device):
560 """Returns the IP address.""" 573 """Returns the IP address."""
574 assert isinstance(device, Device), device
561 # If ever needed, read wifi.interface from /system/build.prop if a device 575 # If ever needed, read wifi.interface from /system/build.prop if a device
562 # doesn't use wlan0. 576 # doesn't use wlan0.
563 ip, _ = device.shell('getprop dhcp.wlan0.ipaddress') 577 ip, _ = device.shell('getprop dhcp.wlan0.ipaddress')
564 if not ip: 578 if not ip:
565 return None 579 return None
566 return ip.strip() 580 return ip.strip()
OLDNEW
« no previous file with comments | « no previous file | appengine/swarming/swarming_bot/api/platforms/android_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698