OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 # |
| 3 # Copyright 2016 Google Inc. |
| 4 # |
| 5 # Use of this source code is governed by a BSD-style license that can be |
| 6 # found in the LICENSE file. |
| 7 |
| 8 |
| 9 import android_devices |
| 10 import default_flavor |
| 11 import os |
| 12 |
| 13 |
| 14 """Android flavor utils, used for building for and running tests on Android.""" |
| 15 |
| 16 |
| 17 class _ADBWrapper(object): |
| 18 """Wrapper for ADB.""" |
| 19 def __init__(self, path_to_adb, serial, android_flavor): |
| 20 self._adb = path_to_adb |
| 21 self._serial = serial |
| 22 self._wait_count = 0 |
| 23 self._android_flavor = android_flavor |
| 24 |
| 25 def wait_for_device(self): |
| 26 """Run 'adb wait-for-device'.""" |
| 27 self._wait_count += 1 |
| 28 cmd = [ |
| 29 os.path.join(self._android_flavor.android_bin, 'adb_wait_for_device'), |
| 30 '-s', self._serial, |
| 31 ] |
| 32 self._android_flavor._bot_info.run( |
| 33 cmd, env=self._android_flavor._default_env) |
| 34 |
| 35 def maybe_wait_for_device(self): |
| 36 """Run 'adb wait-for-device' if it hasn't already been run.""" |
| 37 if self._wait_count == 0: |
| 38 self.wait_for_device() |
| 39 |
| 40 def __call__(self, *args, **kwargs): |
| 41 self.maybe_wait_for_device() |
| 42 return self._android_flavor._bot_info.run(self._adb + args, **kwargs) |
| 43 |
| 44 |
| 45 class AndroidFlavorUtils(default_flavor.DefaultFlavorUtils): |
| 46 def __init__(self, skia_api): |
| 47 super(AndroidFlavorUtils, self).__init__(skia_api) |
| 48 self.device = self._bot_info.spec['device_cfg'] |
| 49 slave_info = android_devices.SLAVE_INFO.get( |
| 50 self._bot_info.slave_name, |
| 51 android_devices.SLAVE_INFO['default']) |
| 52 self.serial = slave_info.serial |
| 53 self.android_bin = os.path.join( |
| 54 self._bot_info.skia_dir, 'platform_tools', 'android', 'bin') |
| 55 self._android_sdk_root = slave_info.android_sdk_root |
| 56 self._adb = _ADBWrapper( |
| 57 os.path.join(self._android_sdk_root, 'platform-tools', 'adb'), |
| 58 self.serial, |
| 59 self) |
| 60 self._has_root = slave_info.has_root |
| 61 self._default_env = {'ANDROID_SDK_ROOT': self._android_sdk_root, |
| 62 'ANDROID_HOME': self._android_sdk_root, |
| 63 'SKIA_ANDROID_VERBOSE_SETUP': '1'} |
| 64 |
| 65 def step(self, name, cmd, env=None, **kwargs): |
| 66 self._adb.maybe_wait_for_device() |
| 67 args = [self.android_bin.join('android_run_skia'), |
| 68 '--verbose', |
| 69 '--logcat', |
| 70 '-d', self.device, |
| 71 '-s', self.serial, |
| 72 '-t', self._bot_info.configuration, |
| 73 ] |
| 74 env = dict(env or {}) |
| 75 env.update(self._default_env) |
| 76 |
| 77 return self._bot_info.run(self._bot_info.m.step, name=name, cmd=args + cmd, |
| 78 env=env, **kwargs) |
| 79 |
| 80 def compile(self, target): |
| 81 """Build the given target.""" |
| 82 env = dict(self._default_env) |
| 83 ccache = self._bot_info.ccache |
| 84 if ccache: |
| 85 env['ANDROID_MAKE_CCACHE'] = ccache |
| 86 |
| 87 cmd = [os.path.join(self.android_bin, 'android_ninja'), target, |
| 88 '-d', self.device] |
| 89 if 'Clang' in self._bot_info.name: |
| 90 cmd.append('--clang') |
| 91 self._bot_info.run(cmd, env=env) |
| 92 |
| 93 def device_path_join(self, *args): |
| 94 """Like os.path.join(), but for paths on a connected Android device.""" |
| 95 return '/'.join(args) |
| 96 |
| 97 def device_path_exists(self, path): |
| 98 """Like os.path.exists(), but for paths on a connected device.""" |
| 99 exists_str = 'FILE_EXISTS' |
| 100 return exists_str in self._adb( |
| 101 name='exists %s' % self._bot_info.m.path.basename(path), |
| 102 serial=self.serial, |
| 103 cmd=['shell', 'if', '[', '-e', path, '];', |
| 104 'then', 'echo', exists_str + ';', 'fi'], |
| 105 stdout=self._bot_info.m.raw_io.output(), |
| 106 infra_step=True |
| 107 ).stdout |
| 108 |
| 109 def _remove_device_dir(self, path): |
| 110 """Remove the directory on the device.""" |
| 111 self._adb(name='rmdir %s' % self._bot_info.m.path.basename(path), |
| 112 serial=self.serial, |
| 113 cmd=['shell', 'rm', '-r', path], |
| 114 infra_step=True) |
| 115 # Sometimes the removal fails silently. Verify that it worked. |
| 116 if self.device_path_exists(path): |
| 117 raise Exception('Failed to remove %s!' % path) # pragma: no cover |
| 118 |
| 119 def _create_device_dir(self, path): |
| 120 """Create the directory on the device.""" |
| 121 self._adb(name='mkdir %s' % self._bot_info.m.path.basename(path), |
| 122 serial=self.serial, |
| 123 cmd=['shell', 'mkdir', '-p', path], |
| 124 infra_step=True) |
| 125 |
| 126 def copy_directory_contents_to_device(self, host_dir, device_dir): |
| 127 """Like shutil.copytree(), but for copying to a connected device.""" |
| 128 self._bot_info.run( |
| 129 self._bot_info.m.step, |
| 130 name='push %s' % self._bot_info.m.path.basename(host_dir), |
| 131 cmd=[self.android_bin.join('adb_push_if_needed'), '--verbose', |
| 132 '-s', self.serial, host_dir, device_dir], |
| 133 env=self._default_env, |
| 134 infra_step=True) |
| 135 |
| 136 def copy_directory_contents_to_host(self, device_dir, host_dir): |
| 137 """Like shutil.copytree(), but for copying from a connected device.""" |
| 138 self._bot_info.run( |
| 139 self._bot_info.m.step, |
| 140 name='pull %s' % self._bot_info.m.path.basename(device_dir), |
| 141 cmd=[self.android_bin.join('adb_pull_if_needed'), '--verbose', |
| 142 '-s', self.serial, device_dir, host_dir], |
| 143 env=self._default_env, |
| 144 infra_step=True) |
| 145 |
| 146 def copy_file_to_device(self, host_path, device_path): |
| 147 """Like shutil.copyfile, but for copying to a connected device.""" |
| 148 self._adb(name='push %s' % self._bot_info.m.path.basename(host_path), |
| 149 serial=self.serial, |
| 150 cmd=['push', host_path, device_path], |
| 151 infra_step=True) |
| 152 |
| 153 def create_clean_device_dir(self, path): |
| 154 """Like shutil.rmtree() + os.makedirs(), but on a connected device.""" |
| 155 self._remove_device_dir(path) |
| 156 self._create_device_dir(path) |
| 157 |
| 158 def install(self): |
| 159 """Run device-specific installation steps.""" |
| 160 if self._has_root: |
| 161 self._adb(name='adb root', |
| 162 serial=self.serial, |
| 163 cmd=['root'], |
| 164 infra_step=True) |
| 165 # Wait for the device to reconnect. |
| 166 self._bot_info.run( |
| 167 self._bot_info.m.step, |
| 168 name='wait', |
| 169 cmd=['sleep', '10'], |
| 170 infra_step=True) |
| 171 self._adb.wait_for_device() |
| 172 |
| 173 # TODO(borenet): Set CPU scaling mode to 'performance'. |
| 174 self._bot_info.run(self._bot_info.m.step, |
| 175 name='kill skia', |
| 176 cmd=[self.android_bin.join('android_kill_skia'), |
| 177 '--verbose', '-s', self.serial], |
| 178 env=self._default_env, |
| 179 infra_step=True) |
| 180 if self._has_root: |
| 181 self._adb(name='stop shell', |
| 182 serial=self.serial, |
| 183 cmd=['shell', 'stop'], |
| 184 infra_step=True) |
| 185 |
| 186 # Print out battery stats. |
| 187 self._adb(name='starting battery stats', |
| 188 serial=self.serial, |
| 189 cmd=['shell', 'dumpsys', 'batteryproperties'], |
| 190 infra_step=True) |
| 191 |
| 192 def cleanup_steps(self): |
| 193 """Run any device-specific cleanup steps.""" |
| 194 self._adb(name='final battery stats', |
| 195 serial=self.serial, |
| 196 cmd=['shell', 'dumpsys', 'batteryproperties'], |
| 197 infra_step=True) |
| 198 self._adb(name='reboot', |
| 199 serial=self.serial, |
| 200 cmd=['reboot'], |
| 201 infra_step=True) |
| 202 self._bot_info.run( |
| 203 self._bot_info.m.step, |
| 204 name='wait for reboot', |
| 205 cmd=['sleep', '10'], |
| 206 infra_step=True) |
| 207 self._adb.wait_for_device() |
| 208 |
| 209 def read_file_on_device(self, path, *args, **kwargs): |
| 210 """Read the given file.""" |
| 211 return self._adb(name='read %s' % self._bot_info.m.path.basename(path), |
| 212 serial=self.serial, |
| 213 cmd=['shell', 'cat', path], |
| 214 stdout=self._bot_info.m.raw_io.output(), |
| 215 infra_step=True).stdout.rstrip() |
| 216 |
| 217 def remove_file_on_device(self, path, *args, **kwargs): |
| 218 """Delete the given file.""" |
| 219 return self._adb(name='rm %s' % self._bot_info.m.path.basename(path), |
| 220 serial=self.serial, |
| 221 cmd=['shell', 'rm', '-f', path], |
| 222 infra_step=True, |
| 223 *args, |
| 224 **kwargs) |
| 225 |
| 226 def get_device_dirs(self): |
| 227 """ Set the directories which will be used by the build steps.""" |
| 228 device_scratch_dir = self._adb( |
| 229 name='get EXTERNAL_STORAGE dir', |
| 230 serial=self.serial, |
| 231 cmd=['shell', 'echo', '$EXTERNAL_STORAGE'], |
| 232 ) |
| 233 prefix = self.device_path_join(device_scratch_dir, 'skiabot', 'skia_') |
| 234 return default_flavor.DeviceDirs( |
| 235 dm_dir=prefix + 'dm', |
| 236 perf_data_dir=prefix + 'perf', |
| 237 resource_dir=prefix + 'resources', |
| 238 images_dir=prefix + 'images', |
| 239 skp_dir=prefix + 'skp/skps', |
| 240 tmp_dir=prefix + 'tmp_dir') |
| 241 |
OLD | NEW |