| 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 |