2017-03-08 19:01:01 +00:00
|
|
|
# Copyright 2016 The Chromium Authors. All rights reserved.
|
|
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
|
|
# found in the LICENSE file.
|
|
|
|
|
|
|
|
from recipe_engine import recipe_api
|
|
|
|
|
2018-05-24 13:14:18 +00:00
|
|
|
from . import android
|
|
|
|
from . import default
|
2017-03-08 19:01:01 +00:00
|
|
|
|
|
|
|
|
2018-05-24 13:14:18 +00:00
|
|
|
"""Chromecast flavor, used for running code on Chromecast"""
|
|
|
|
|
|
|
|
|
|
|
|
class ChromecastFlavor(android.AndroidFlavor):
|
2017-03-08 19:01:01 +00:00
|
|
|
def __init__(self, m):
|
2018-05-24 13:14:18 +00:00
|
|
|
super(ChromecastFlavor, self).__init__(m)
|
2017-03-21 13:25:34 +00:00
|
|
|
self._ever_ran_adb = False
|
2017-06-01 19:49:41 +00:00
|
|
|
self._user_ip = ''
|
|
|
|
|
2018-01-16 21:15:57 +00:00
|
|
|
# Disk space is extremely tight on the Chromecasts (~100M) There is not
|
|
|
|
# enough space on the android_data_dir (/cache/skia) to fit the images,
|
|
|
|
# resources, executable and output the dm images. So, we have dm_out be
|
|
|
|
# on the tempfs (i.e. RAM) /dev/shm. (which is about 140M)
|
2018-05-18 11:36:55 +00:00
|
|
|
data_dir = '/cache/skia/'
|
2018-05-24 13:14:18 +00:00
|
|
|
self.device_dirs = default.DeviceDirs(
|
2018-05-18 11:36:55 +00:00
|
|
|
bin_dir = '/cache/skia/bin',
|
2018-01-16 21:15:57 +00:00
|
|
|
dm_dir = '/dev/shm/skia/dm_out',
|
2018-05-18 11:36:55 +00:00
|
|
|
perf_data_dir = data_dir + 'perf',
|
|
|
|
resource_dir = data_dir + 'resources',
|
|
|
|
images_dir = data_dir + 'images',
|
|
|
|
skp_dir = data_dir + 'skps',
|
|
|
|
svg_dir = data_dir + 'svgs',
|
|
|
|
tmp_dir = data_dir)
|
2018-01-16 21:15:57 +00:00
|
|
|
|
2017-06-01 19:49:41 +00:00
|
|
|
@property
|
|
|
|
def user_ip_host(self):
|
|
|
|
if not self._user_ip:
|
|
|
|
self._user_ip = self.m.run(self.m.python.inline, 'read chromecast ip',
|
|
|
|
program="""
|
|
|
|
import os
|
|
|
|
CHROMECAST_IP_FILE = os.path.expanduser('~/chromecast.txt')
|
|
|
|
with open(CHROMECAST_IP_FILE, 'r') as f:
|
|
|
|
print f.read()
|
|
|
|
""",
|
|
|
|
stdout=self.m.raw_io.output(),
|
|
|
|
infra_step=True).stdout
|
|
|
|
|
|
|
|
return self._user_ip
|
|
|
|
|
|
|
|
@property
|
|
|
|
def user_ip(self):
|
|
|
|
return self.user_ip_host.split(':')[0]
|
2017-03-08 19:01:01 +00:00
|
|
|
|
2017-08-31 14:39:05 +00:00
|
|
|
def install(self):
|
2018-05-24 13:14:18 +00:00
|
|
|
super(ChromecastFlavor, self).install()
|
2018-05-18 11:36:55 +00:00
|
|
|
self._adb('mkdir ' + self.device_dirs.bin_dir,
|
|
|
|
'shell', 'mkdir', '-p', self.device_dirs.bin_dir)
|
2017-03-08 19:01:01 +00:00
|
|
|
|
2017-03-21 13:25:34 +00:00
|
|
|
def _adb(self, title, *cmd, **kwargs):
|
|
|
|
if not self._ever_ran_adb:
|
|
|
|
self._connect_to_remote()
|
|
|
|
|
|
|
|
self._ever_ran_adb = True
|
|
|
|
# The only non-infra adb steps (dm / nanobench) happen to not use _adb().
|
|
|
|
if 'infra_step' not in kwargs:
|
|
|
|
kwargs['infra_step'] = True
|
|
|
|
return self._run(title, 'adb', *cmd, **kwargs)
|
|
|
|
|
|
|
|
def _connect_to_remote(self):
|
2017-06-01 19:49:41 +00:00
|
|
|
self.m.run(self.m.step, 'adb connect %s' % self.user_ip_host, cmd=['adb',
|
|
|
|
'connect', self.user_ip_host], infra_step=True)
|
2017-03-21 13:25:34 +00:00
|
|
|
|
|
|
|
def create_clean_device_dir(self, path):
|
|
|
|
# Note: Chromecast does not support -rf
|
|
|
|
self._adb('rm %s' % path, 'shell', 'rm', '-r', path)
|
|
|
|
self._adb('mkdir %s' % path, 'shell', 'mkdir', '-p', path)
|
|
|
|
|
|
|
|
def copy_directory_contents_to_device(self, host, device):
|
|
|
|
# Copy the tree, avoiding hidden directories and resolving symlinks.
|
|
|
|
# Additionally, due to space restraints, we don't push files > 3 MB
|
|
|
|
# which cuts down the size of the SKP asset to be around 50 MB as of
|
|
|
|
# version 41.
|
|
|
|
self.m.run(self.m.python.inline, 'push %s/* %s' % (host, device),
|
|
|
|
program="""
|
|
|
|
import os
|
|
|
|
import subprocess
|
|
|
|
import sys
|
|
|
|
host = sys.argv[1]
|
|
|
|
device = sys.argv[2]
|
|
|
|
for d, _, fs in os.walk(host):
|
|
|
|
p = os.path.relpath(d, host)
|
|
|
|
if p != '.' and p.startswith('.'):
|
|
|
|
continue
|
|
|
|
for f in fs:
|
|
|
|
print os.path.join(p,f)
|
|
|
|
hp = os.path.realpath(os.path.join(host, p, f))
|
2017-08-31 14:39:05 +00:00
|
|
|
if os.stat(hp).st_size > (1.5 * 1024 * 1024):
|
2017-03-21 13:25:34 +00:00
|
|
|
print "Skipping because it is too big"
|
|
|
|
else:
|
|
|
|
subprocess.check_call(['adb', 'push',
|
|
|
|
hp, os.path.join(device, p, f)])
|
|
|
|
""", args=[host, device], infra_step=True)
|
2017-06-01 19:49:41 +00:00
|
|
|
|
2017-08-31 14:39:05 +00:00
|
|
|
def cleanup_steps(self):
|
|
|
|
if self._ever_ran_adb:
|
|
|
|
# To clean up disk space for next time
|
2018-05-18 11:36:55 +00:00
|
|
|
self._ssh('Delete executables', 'rm', '-r', self.device_dirs.bin_dir,
|
2017-08-31 17:32:44 +00:00
|
|
|
abort_on_failure=False, infra_step=True)
|
2017-08-31 14:39:05 +00:00
|
|
|
# Reconnect if was disconnected
|
2017-08-31 17:32:44 +00:00
|
|
|
self._adb('disconnect', 'disconnect')
|
2017-08-31 14:39:05 +00:00
|
|
|
self._connect_to_remote()
|
|
|
|
self.m.run(self.m.python.inline, 'dump log', program="""
|
|
|
|
import os
|
|
|
|
import subprocess
|
|
|
|
import sys
|
|
|
|
out = sys.argv[1]
|
|
|
|
log = subprocess.check_output(['adb', 'logcat', '-d'])
|
|
|
|
for line in log.split('\\n'):
|
|
|
|
tokens = line.split()
|
|
|
|
if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':
|
|
|
|
addr, path = tokens[-2:]
|
|
|
|
local = os.path.join(out, os.path.basename(path))
|
|
|
|
if os.path.exists(local):
|
|
|
|
sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])
|
|
|
|
line = line.replace(addr, addr + ' ' + sym.strip())
|
|
|
|
print line
|
|
|
|
""",
|
2018-06-01 14:08:53 +00:00
|
|
|
args=[self.host_dirs.bin_dir],
|
2017-08-31 14:39:05 +00:00
|
|
|
infra_step=True,
|
|
|
|
abort_on_failure=False)
|
|
|
|
|
2017-08-31 17:32:44 +00:00
|
|
|
self._adb('disconnect', 'disconnect')
|
2017-08-31 14:39:05 +00:00
|
|
|
self._adb('kill adb server', 'kill-server')
|
|
|
|
|
2017-06-01 19:49:41 +00:00
|
|
|
def _ssh(self, title, *cmd, **kwargs):
|
2018-04-19 13:29:20 +00:00
|
|
|
# Don't use -t -t (Force psuedo-tty allocation) like in the ChromeOS
|
|
|
|
# version because the pseudo-tty allocation seems to fail
|
|
|
|
# instantly when talking to a Chromecast.
|
|
|
|
# This was excacerbated when we migrated to kitchen and was marked by
|
|
|
|
# the symptoms of all the ssh commands instantly failing (even after
|
|
|
|
# connecting and authenticating) with exit code -1 (255)
|
2017-06-01 19:49:41 +00:00
|
|
|
ssh_cmd = ['ssh', '-oConnectTimeout=15', '-oBatchMode=yes',
|
2018-04-19 13:29:20 +00:00
|
|
|
'-T', 'root@%s' % self.user_ip] + list(cmd)
|
2017-06-01 19:49:41 +00:00
|
|
|
|
2017-08-31 17:32:44 +00:00
|
|
|
return self.m.run(self.m.step, title, cmd=ssh_cmd, **kwargs)
|
2017-06-01 19:49:41 +00:00
|
|
|
|
|
|
|
def step(self, name, cmd, **kwargs):
|
2018-06-01 14:08:53 +00:00
|
|
|
app = self.host_dirs.bin_dir.join(cmd[0])
|
2017-08-31 14:39:05 +00:00
|
|
|
|
2017-06-01 19:49:41 +00:00
|
|
|
self._adb('push %s' % cmd[0],
|
2018-05-18 11:36:55 +00:00
|
|
|
'push', app, self.device_dirs.bin_dir)
|
2017-06-01 19:49:41 +00:00
|
|
|
|
2018-05-18 11:36:55 +00:00
|
|
|
cmd[0] = '%s/%s' % (self.device_dirs.bin_dir, cmd[0])
|
2017-08-31 17:32:44 +00:00
|
|
|
self._ssh(str(name), *cmd, infra_step=False)
|