Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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() |
| OLD | NEW |