• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2016 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5from recipe_engine import recipe_api
6
7from . import android
8from . import default
9
10
11"""Chromecast flavor, used for running code on Chromecast"""
12
13
14class ChromecastFlavor(android.AndroidFlavor):
15  def __init__(self, m):
16    super(ChromecastFlavor, self).__init__(m)
17    self._ever_ran_adb = False
18    self._user_ip = ''
19
20    # Disk space is extremely tight on the Chromecasts (~100M) There is not
21    # enough space on the android_data_dir (/cache/skia) to fit the images,
22    # resources, executable and output the dm images.  So, we have dm_out be
23    # on the tempfs (i.e. RAM) /dev/shm. (which is about 140M)
24    data_dir = '/cache/skia/'
25    self.device_dirs = default.DeviceDirs(
26        bin_dir       = '/cache/skia/bin',
27        dm_dir        = '/dev/shm/skia/dm_out',
28        perf_data_dir = data_dir + 'perf',
29        resource_dir  = data_dir + 'resources',
30        images_dir    = data_dir + 'images',
31        lotties_dir   = data_dir + 'lotties',
32        skp_dir       = data_dir + 'skps',
33        svg_dir       = data_dir + 'svgs',
34        mskp_dir      = data_dir + 'mskp',
35        tmp_dir       = data_dir)
36
37  @property
38  def user_ip_host(self):
39    if not self._user_ip:
40      self._user_ip = self.m.run(self.m.python.inline, 'read chromecast ip',
41                                 program="""
42      import os
43      CHROMECAST_IP_FILE = os.path.expanduser('~/chromecast.txt')
44      with open(CHROMECAST_IP_FILE, 'r') as f:
45        print f.read()
46      """,
47      stdout=self.m.raw_io.output(),
48      infra_step=True).stdout
49
50    return self._user_ip
51
52  @property
53  def user_ip(self):
54    return self.user_ip_host.split(':')[0]
55
56  def install(self):
57    super(ChromecastFlavor, self).install()
58    self._adb('mkdir ' + self.device_dirs.bin_dir,
59              'shell', 'mkdir', '-p', self.device_dirs.bin_dir)
60
61  def _adb(self, title, *cmd, **kwargs):
62    if not self._ever_ran_adb:
63      self._connect_to_remote()
64
65    self._ever_ran_adb = True
66    # The only non-infra adb steps (dm / nanobench) happen to not use _adb().
67    if 'infra_step' not in kwargs:
68      kwargs['infra_step'] = True
69    return self._run(title, 'adb', *cmd, **kwargs)
70
71  def _connect_to_remote(self):
72    self.m.run(self.m.step, 'adb connect %s' % self.user_ip_host, cmd=['adb',
73      'connect', self.user_ip_host], infra_step=True)
74
75  def create_clean_device_dir(self, path):
76    # Note: Chromecast does not support -rf
77    self._adb('rm %s' % path, 'shell', 'rm', '-r', path)
78    self._adb('mkdir %s' % path, 'shell', 'mkdir', '-p', path)
79
80  def copy_directory_contents_to_device(self, host, device):
81    # Copy the tree, avoiding hidden directories and resolving symlinks.
82    # Additionally, due to space restraints, we don't push files > 3 MB
83    # which cuts down the size of the SKP asset to be around 50 MB as of
84    # version 41.
85    self.m.run(self.m.python.inline, 'push %s/* %s' % (host, device),
86               program="""
87    import os
88    import subprocess
89    import sys
90    host   = sys.argv[1]
91    device = sys.argv[2]
92    for d, _, fs in os.walk(host):
93      p = os.path.relpath(d, host)
94      if p != '.' and p.startswith('.'):
95        continue
96      for f in fs:
97        print os.path.join(p,f)
98        hp = os.path.realpath(os.path.join(host, p, f))
99        if os.stat(hp).st_size > (1.5 * 1024 * 1024):
100          print "Skipping because it is too big"
101        else:
102          subprocess.check_call(['adb', 'push',
103                                hp, os.path.join(device, p, f)])
104    """, args=[host, device], infra_step=True)
105
106  def cleanup_steps(self):
107    if self._ever_ran_adb:
108      # To clean up disk space for next time
109      self._ssh('Delete executables', 'rm', '-r', self.device_dirs.bin_dir,
110                abort_on_failure=False, infra_step=True)
111      # Reconnect if was disconnected
112      self._adb('disconnect', 'disconnect')
113      self._connect_to_remote()
114      self.m.run(self.m.python.inline, 'dump log', program="""
115          import os
116          import subprocess
117          import sys
118          out = sys.argv[1]
119          log = subprocess.check_output(['adb', 'logcat', '-d'])
120          for line in log.split('\\n'):
121            tokens = line.split()
122            if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':
123              addr, path = tokens[-2:]
124              local = os.path.join(out, os.path.basename(path))
125              if os.path.exists(local):
126                sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])
127                line = line.replace(addr, addr + ' ' + sym.strip())
128            print line
129          """,
130          args=[self.host_dirs.bin_dir],
131          infra_step=True,
132          abort_on_failure=False)
133
134      self._adb('disconnect', 'disconnect')
135      self._adb('kill adb server', 'kill-server')
136
137  def _ssh(self, title, *cmd, **kwargs):
138    # Don't use -t -t (Force psuedo-tty allocation) like in the ChromeOS
139    # version because the pseudo-tty allocation seems to fail
140    # instantly when talking to a Chromecast.
141    # This was excacerbated when we migrated to kitchen and was marked by
142    # the symptoms of all the ssh commands instantly failing (even after
143    # connecting and authenticating) with exit code -1 (255)
144    ssh_cmd = ['ssh', '-oConnectTimeout=15', '-oBatchMode=yes',
145               '-T', 'root@%s' % self.user_ip] + list(cmd)
146
147    return self.m.run(self.m.step, title, cmd=ssh_cmd, **kwargs)
148
149  def step(self, name, cmd, **kwargs):
150    app = self.host_dirs.bin_dir.join(cmd[0])
151
152    self._adb('push %s' % cmd[0],
153              'push', app, self.device_dirs.bin_dir)
154
155    cmd[0] = '%s/%s' % (self.device_dirs.bin_dir, cmd[0])
156    self._ssh(str(name), *cmd, infra_step=False)
157