blob: 1431c0db7460cecd0678ebdb00fb320c8297786f [file] [log] [blame]
import os
import re
import subprocess
import sys
import time
# Initial number of seconds to wait before retrying a command.
INITIAL_BACKOFF = 2
# Number of retries when a command fails.
RETRIES = 3
# Assets located here.
ASSET_DIR = '/usr/local/share/assets'
# Directory where the dev images are.
DEV_IMG_DIR = ASSET_DIR + '/devimages'
# Path to the provisioning profile.
PROVISIONING_PROF = ASSET_DIR + '/development.mobileprovision'
# Fields that should be ignored when getting the iOS device info
IGNORE_FIELDS = set([
'CompassCalibration'
'ProximitySensorCalibration'
'SoftwareBehavior'
])
def ios_get_device_ids():
"""Returns the ids of all attached devices.
This will also work when the attached device is not fully booted."""
ret = []
# Primary purpose of this, is to catch when the tools are not installed.
try:
output = _run_cmd('idevice_id --list')
for line in output.splitlines():
if line.strip():
ret.append(line.strip())
except (IOError, OSError, subprocess.CalledProcessError):
pass
return ret
def ios_get_devices():
"""Returns instances of IOSDevice for each attached device."""
return [IOSDevice(x) for x in ios_get_device_ids()]
class IOSDevice(object):
def __init__(self, dev_id):
self._id = dev_id
def get_state(self):
"""Returns a dictionary to be used"""
return _get_kv_pairs(_run_cmd('ideviceinfo -u %s' % self._id))
def get_battery_state(self):
cmd = "ideviceinfo -u %s -q %s" % (self._id, 'com.apple.mobile.battery')
return _get_kv_pairs(_run_cmd(cmd))
def reboot(self):
"""Reboots the device."""
_run_cmd('idevicediagnostics restart -u %s' % self._id)
def get_ready(self):
"""Does all the necessary setup to run apps on the device."""
# Pair the device with the host.
ret = _run_ret_value('idevicepair -u %s validate' % self._id)
if ret != 0:
_run_cmd('idevicepair -u %s pair' % self._id)
# Check if a developer images has been mounted already.
kv = _get_kv_pairs(_run_cmd('ideviceimagemounter -u %s -l' % self._id))
# Check if there is an image present.
if not ((kv.get('ImagePresent', '') == 'true') or
kv.get('ImageSignature', [])):
# Mount the developer image.
img, imgSig = self._get_dev_image()
cmd = ['ideviceimagemounter', '-u', self._id, img, imgSig]
kv = _get_kv_pairs(_run_cmd(cmd))
assert(kv['Status'] == 'Complete')
# Install the provisioning profile.
_run_cmd('ideviceprovision -u %s remove-all' % self._id)
cmd = 'ideviceprovision -u %s install %s' % (self._id, PROVISIONING_PROF)
_run_cmd(cmd)
def _get_dev_image(self):
# Get the version of the device
state = self.get_state()
ver = state['ProductVersion'].split('.', 2)[:2]
prefix = '.'.join(ver)
# iterate over the contents of the image dir and find the right image
for dirname in os.listdir(DEV_IMG_DIR):
path = os.path.join(DEV_IMG_DIR, dirname)
if os.path.isdir(path) and dirname.startswith(prefix):
contents = os.listdir(path)
devImage = [x for x in contents if x.endswith('.dmg')][0]
devImageSig = [x for x in contents if x.endswith('.dmg.signature')][0]
return os.path.join(path, devImage), os.path.join(path, devImageSig)
raise ValueError('Unable to find dev images.')
# Utility functions.
def _get_kv_pairs(output):
"""Extract key/value pairs from output."""
ret = {}
nested = None
# Note: This assumes that there is at most one level of nesting
# arrays or dictionaries.
for line in output.splitlines():
if line.strip():
split_line = line.split(":", 1)
if len(split_line) < 2:
continue
k,v = [x.strip() for x in split_line]
if line.startswith(' '):
if isinstance(nested, dict):
nested[k] = v
else:
nested.append(v)
else:
# If the value is an array it has this format: key_name[x]
# where x is an integer indicating the number of elements.
m = re.match(r'^(.*)\[[0-9]*\]$', k)
if m:
k = m.group(1)
nested = []
v = nested
# If 'v' is empty then we expect a hash map.
elif v == '':
nested = {}
v = nested
ret[k] = v
return ret
def _run_ret_value(cmd):
""" Run the given command and return the exit status"""
try:
_run_cmd(cmd)
return 0
except subprocess.CalledProcessError as ex:
return ex.returncode
def _run_cmd(cmd):
"""Run the given command and return the output.
Returns:
Output from the command as a string.
Raises:
subprocess.CalledProcessError: If the global number of retries is
exceeded."""
backoff = INITIAL_BACKOFF
retry = 0
while True:
try:
args = cmd if isinstance(cmd, list) else cmd.strip().split()
output = subprocess.check_output(args, stderr=sys.stderr)
return output
except subprocess.CalledProcessError:
retry += 1
if retry >= RETRIES:
raise
time.sleep(backoff)
backoff *= 2
# TODO(stephana): Move the main program below into a separate script
# or into the skia repo entirely.
if __name__=='__main__':
dev = ios_get_devices()[0]
dev.get_ready()
print "Successfully set up device."