OLD | NEW |
1 # Copyright 2014 The Chromium Authors. All rights reserved. | 1 # Copyright 2014 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 | 5 |
6 # pylint: disable=W0201 | 6 # pylint: disable=W0201 |
7 | 7 |
8 | 8 |
9 import android_devices | |
10 import copy | 9 import copy |
11 import default_flavor | 10 import default_flavor |
12 | 11 |
13 | 12 |
14 """Android flavor utils, used for building for and running tests on Android.""" | 13 """Android flavor utils, used for building for and running tests on Android.""" |
15 | 14 |
16 | 15 |
17 class _ADBWrapper(object): | 16 class _ADBWrapper(object): |
18 """Wrapper for the ADB recipe module. | 17 """Wrapper for the ADB recipe module. |
19 | 18 |
20 The ADB recipe module looks for the ADB binary at a path we don't have checked | 19 The ADB recipe module looks for the ADB binary at a path we don't have checked |
21 out on our bots. This wrapper ensures that we set a custom ADB path before | 20 out on our bots. This wrapper ensures that we set a custom ADB path before |
22 attempting to use the module. | 21 attempting to use the module. |
23 """ | 22 """ |
24 def __init__(self, adb_api, path_to_adb, serial_args, android_flavor): | 23 def __init__(self, m, path_to_adb, serial_args, android_flavor): |
25 self._adb = adb_api | 24 self.m = m |
26 self._adb.set_adb_path(path_to_adb) | 25 self.m.adb.set_adb_path(path_to_adb) |
27 self._has_root = False # This is set in install(). | 26 self._has_root = False # This is set in install(). |
28 self._serial_args = serial_args | 27 self._serial_args = serial_args |
29 self._wait_count = 0 | 28 self._wait_count = 0 |
30 self._android_flavor = android_flavor | 29 self._android_flavor = android_flavor |
31 | 30 |
32 def wait_for_device(self): | 31 def wait_for_device(self): |
33 """Run 'adb wait-for-device'.""" | 32 """Run 'adb wait-for-device'.""" |
34 self._wait_count += 1 | 33 self._wait_count += 1 |
35 cmd = [ | 34 cmd = [ |
36 self._android_flavor.android_bin.join('adb_wait_for_device') | 35 self._android_flavor.android_bin.join('adb_wait_for_device') |
37 ] + self._serial_args | 36 ] + self._serial_args |
38 self._android_flavor._skia_api.run( | 37 self.m.run( |
39 self._android_flavor._skia_api.m.step, | 38 self.m.step, |
40 name='wait for device (%d)' % self._wait_count, | 39 name='wait for device (%d)' % self._wait_count, |
41 cmd=cmd, | 40 cmd=cmd, |
42 env=self._android_flavor._default_env, | 41 env=self._android_flavor._default_env, |
43 infra_step=True) | 42 infra_step=True) |
44 | 43 |
45 cmd = [ | 44 cmd = [ |
46 self._android_flavor.android_bin.join('adb_wait_for_charge'), | 45 self._android_flavor.android_bin.join('adb_wait_for_charge'), |
47 ] + self._serial_args | 46 ] + self._serial_args |
48 self._android_flavor._skia_api.run( | 47 self.m.run( |
49 self._android_flavor._skia_api.m.step, | 48 self.m.step, |
50 name='wait for charge (%d)' % self._wait_count, | 49 name='wait for charge (%d)' % self._wait_count, |
51 cmd=cmd, | 50 cmd=cmd, |
52 env=self._android_flavor._default_env, | 51 env=self._android_flavor._default_env, |
53 infra_step=True) | 52 infra_step=True) |
54 | 53 |
55 def maybe_wait_for_device(self): | 54 def maybe_wait_for_device(self): |
56 """Run 'adb wait-for-device' if it hasn't already been run.""" | 55 """Run 'adb wait-for-device' if it hasn't already been run.""" |
57 if self._wait_count == 0: | 56 if self._wait_count == 0: |
58 self.wait_for_device() | 57 self.wait_for_device() |
59 | 58 |
60 def __call__(self, *args, **kwargs): | 59 def __call__(self, *args, **kwargs): |
61 self.maybe_wait_for_device() | 60 self.maybe_wait_for_device() |
62 return self._android_flavor._skia_api.run(self._adb, *args, **kwargs) | 61 return self.m.run(self.m.adb, *args, **kwargs) |
63 | 62 |
64 | 63 |
65 class AndroidFlavorUtils(default_flavor.DefaultFlavorUtils): | 64 class AndroidFlavorUtils(default_flavor.DefaultFlavorUtils): |
66 def __init__(self, skia_api): | 65 def __init__(self, m): |
67 super(AndroidFlavorUtils, self).__init__(skia_api) | 66 super(AndroidFlavorUtils, self).__init__(m) |
68 self.device = self._skia_api.builder_spec['device_cfg'] | 67 self.device = self.m.vars.builder_spec['device_cfg'] |
69 self.android_bin = self._skia_api.skia_dir.join( | 68 self.android_bin = self.m.vars.skia_dir.join( |
70 'platform_tools', 'android', 'bin') | 69 'platform_tools', 'android', 'bin') |
71 self._android_sdk_root = self._skia_api.slave_dir.join( | 70 self._android_sdk_root = self.m.vars.slave_dir.join( |
72 'android_sdk', 'android-sdk') | 71 'android_sdk', 'android-sdk') |
73 self.serial = None | 72 self.serial = None |
74 self.serial_args = [] | 73 self.serial_args = [] |
75 try: | 74 try: |
76 path_to_adb = self._skia_api.m.step( | 75 path_to_adb = self.m.step( |
77 'which adb', | 76 'which adb', |
78 cmd=['which', 'adb'], | 77 cmd=['which', 'adb'], |
79 stdout=self._skia_api.m.raw_io.output(), | 78 stdout=self.m.raw_io.output(), |
80 infra_step=True).stdout.rstrip() | 79 infra_step=True).stdout.rstrip() |
81 except self._skia_api.m.step.StepFailure: | 80 except self.m.step.StepFailure: |
82 path_to_adb = self._skia_api.m.path.join(self._android_sdk_root, | 81 path_to_adb = self.m.path.join(self._android_sdk_root, |
83 'platform-tools', 'adb') | 82 'platform-tools', 'adb') |
84 self._adb = _ADBWrapper( | 83 self._adb = _ADBWrapper( |
85 self._skia_api.m.adb, path_to_adb, self.serial_args, self) | 84 self.m, path_to_adb, self.serial_args, self) |
86 self._default_env = {'ANDROID_SDK_ROOT': self._android_sdk_root, | 85 self._default_env = {'ANDROID_SDK_ROOT': self._android_sdk_root, |
87 'ANDROID_HOME': self._android_sdk_root, | 86 'ANDROID_HOME': self._android_sdk_root, |
88 'SKIA_ANDROID_VERBOSE_SETUP': 1} | 87 'SKIA_ANDROID_VERBOSE_SETUP': 1} |
89 | 88 |
90 def step(self, name, cmd, env=None, **kwargs): | 89 def step(self, name, cmd, env=None, **kwargs): |
91 self._adb.maybe_wait_for_device() | 90 self._adb.maybe_wait_for_device() |
92 args = [ | 91 args = [ |
93 self.android_bin.join('android_run_skia'), | 92 self.android_bin.join('android_run_skia'), |
94 '--verbose', | 93 '--verbose', |
95 '--logcat', | 94 '--logcat', |
96 '-d', self.device, | 95 '-d', self.device, |
97 ] + self.serial_args + [ | 96 ] + self.serial_args + [ |
98 '-t', self._skia_api.configuration, | 97 '-t', self.m.vars.configuration, |
99 ] | 98 ] |
100 env = dict(env or {}) | 99 env = dict(env or {}) |
101 env.update(self._default_env) | 100 env.update(self._default_env) |
102 | 101 |
103 return self._skia_api.run(self._skia_api.m.step, name=name, cmd=args + cmd, | 102 return self.m.run(self.m.step, name=name, cmd=args + cmd, |
104 env=env, **kwargs) | 103 env=env, **kwargs) |
105 | 104 |
106 def compile(self, target): | 105 def compile(self, target): |
107 """Build the given target.""" | 106 """Build the given target.""" |
108 env = dict(self._default_env) | 107 env = dict(self._default_env) |
109 ccache = self._skia_api.ccache() | 108 ccache = self.m.run.ccache() |
110 if ccache: | 109 if ccache: |
111 env['ANDROID_MAKE_CCACHE'] = ccache | 110 env['ANDROID_MAKE_CCACHE'] = ccache |
112 | 111 |
113 cmd = [self.android_bin.join('android_ninja'), target, '-d', self.device] | 112 cmd = [self.android_bin.join('android_ninja'), target, '-d', self.device] |
114 if 'Clang' in self._skia_api.builder_name: | 113 if 'Clang' in self.m.vars.builder_name: |
115 cmd.append('--clang') | 114 cmd.append('--clang') |
116 if 'GCC' in self._skia_api.builder_name: | 115 if 'GCC' in self.m.vars.builder_name: |
117 cmd.append('--gcc') | 116 cmd.append('--gcc') |
118 if 'Vulkan' in self._skia_api.builder_name: | 117 if 'Vulkan' in self.m.vars.builder_name: |
119 cmd.append('--vulkan') | 118 cmd.append('--vulkan') |
120 self._skia_api.run(self._skia_api.m.step, 'build %s' % target, cmd=cmd, | 119 self.m.run(self.m.step, 'build %s' % target, cmd=cmd, |
121 env=env, cwd=self._skia_api.m.path['checkout']) | 120 env=env, cwd=self.m.path['checkout']) |
122 | 121 |
123 def device_path_join(self, *args): | 122 def device_path_join(self, *args): |
124 """Like os.path.join(), but for paths on a connected Android device.""" | 123 """Like os.path.join(), but for paths on a connected Android device.""" |
125 return '/'.join(args) | 124 return '/'.join(args) |
126 | 125 |
127 def device_path_exists(self, path): | 126 def device_path_exists(self, path): |
128 """Like os.path.exists(), but for paths on a connected device.""" | 127 """Like os.path.exists(), but for paths on a connected device.""" |
129 exists_str = 'FILE_EXISTS' | 128 exists_str = 'FILE_EXISTS' |
130 return exists_str in self._adb( | 129 return exists_str in self._adb( |
131 name='exists %s' % self._skia_api.m.path.basename(path), | 130 name='exists %s' % self.m.path.basename(path), |
132 serial=self.serial, | 131 serial=self.serial, |
133 cmd=['shell', 'if', '[', '-e', path, '];', | 132 cmd=['shell', 'if', '[', '-e', path, '];', |
134 'then', 'echo', exists_str + ';', 'fi'], | 133 'then', 'echo', exists_str + ';', 'fi'], |
135 stdout=self._skia_api.m.raw_io.output(), | 134 stdout=self.m.raw_io.output(), |
136 infra_step=True | 135 infra_step=True |
137 ).stdout | 136 ).stdout |
138 | 137 |
139 def _remove_device_dir(self, path): | 138 def _remove_device_dir(self, path): |
140 """Remove the directory on the device.""" | 139 """Remove the directory on the device.""" |
141 self._adb(name='rmdir %s' % self._skia_api.m.path.basename(path), | 140 self._adb(name='rmdir %s' % self.m.path.basename(path), |
142 serial=self.serial, | 141 serial=self.serial, |
143 cmd=['shell', 'rm', '-r', path], | 142 cmd=['shell', 'rm', '-r', path], |
144 infra_step=True) | 143 infra_step=True) |
145 # Sometimes the removal fails silently. Verify that it worked. | 144 # Sometimes the removal fails silently. Verify that it worked. |
146 if self.device_path_exists(path): | 145 if self.device_path_exists(path): |
147 raise Exception('Failed to remove %s!' % path) # pragma: no cover | 146 raise Exception('Failed to remove %s!' % path) # pragma: no cover |
148 | 147 |
149 def _create_device_dir(self, path): | 148 def _create_device_dir(self, path): |
150 """Create the directory on the device.""" | 149 """Create the directory on the device.""" |
151 self._adb(name='mkdir %s' % self._skia_api.m.path.basename(path), | 150 self._adb(name='mkdir %s' % self.m.path.basename(path), |
152 serial=self.serial, | 151 serial=self.serial, |
153 cmd=['shell', 'mkdir', '-p', path], | 152 cmd=['shell', 'mkdir', '-p', path], |
154 infra_step=True) | 153 infra_step=True) |
155 | 154 |
156 def copy_directory_contents_to_device(self, host_dir, device_dir): | 155 def copy_directory_contents_to_device(self, host_dir, device_dir): |
157 """Like shutil.copytree(), but for copying to a connected device.""" | 156 """Like shutil.copytree(), but for copying to a connected device.""" |
158 self._skia_api.run( | 157 self.m.run( |
159 self._skia_api.m.step, | 158 self.m.step, |
160 name='push %s' % self._skia_api.m.path.basename(host_dir), | 159 name='push %s' % self.m.path.basename(host_dir), |
161 cmd=[ | 160 cmd=[ |
162 self.android_bin.join('adb_push_if_needed'), '--verbose', | 161 self.android_bin.join('adb_push_if_needed'), '--verbose', |
163 ] + self.serial_args + [ | 162 ] + self.serial_args + [ |
164 host_dir, device_dir, | 163 host_dir, device_dir, |
165 ], | 164 ], |
166 env=self._default_env, | 165 env=self._default_env, |
167 infra_step=True) | 166 infra_step=True) |
168 | 167 |
169 def copy_directory_contents_to_host(self, device_dir, host_dir): | 168 def copy_directory_contents_to_host(self, device_dir, host_dir): |
170 """Like shutil.copytree(), but for copying from a connected device.""" | 169 """Like shutil.copytree(), but for copying from a connected device.""" |
171 self._skia_api.run( | 170 self.m.run( |
172 self._skia_api.m.step, | 171 self.m.step, |
173 name='pull %s' % self._skia_api.m.path.basename(device_dir), | 172 name='pull %s' % self.m.path.basename(device_dir), |
174 cmd=[ | 173 cmd=[ |
175 self.android_bin.join('adb_pull_if_needed'), '--verbose', | 174 self.android_bin.join('adb_pull_if_needed'), '--verbose', |
176 ] + self.serial_args + [ | 175 ] + self.serial_args + [ |
177 device_dir, host_dir, | 176 device_dir, host_dir, |
178 ], | 177 ], |
179 env=self._default_env, | 178 env=self._default_env, |
180 infra_step=True) | 179 infra_step=True) |
181 | 180 |
182 def copy_file_to_device(self, host_path, device_path): | 181 def copy_file_to_device(self, host_path, device_path): |
183 """Like shutil.copyfile, but for copying to a connected device.""" | 182 """Like shutil.copyfile, but for copying to a connected device.""" |
184 self._adb(name='push %s' % self._skia_api.m.path.basename(host_path), | 183 self._adb(name='push %s' % self.m.path.basename(host_path), |
185 serial=self.serial, | 184 serial=self.serial, |
186 cmd=['push', host_path, device_path], | 185 cmd=['push', host_path, device_path], |
187 infra_step=True) | 186 infra_step=True) |
188 | 187 |
189 def create_clean_device_dir(self, path): | 188 def create_clean_device_dir(self, path): |
190 """Like shutil.rmtree() + os.makedirs(), but on a connected device.""" | 189 """Like shutil.rmtree() + os.makedirs(), but on a connected device.""" |
191 self._remove_device_dir(path) | 190 self._remove_device_dir(path) |
192 self._create_device_dir(path) | 191 self._create_device_dir(path) |
193 | 192 |
194 def has_root(self): | 193 def has_root(self): |
195 """Determine if we have root access on this device.""" | 194 """Determine if we have root access on this device.""" |
196 # Special case: GalaxyS3 hangs on `adb root`. Don't bother. | 195 # Special case: GalaxyS3 hangs on `adb root`. Don't bother. |
197 if 'GalaxyS3' in self._skia_api.builder_name: | 196 if 'GalaxyS3' in self.m.vars.builder_name: |
198 return False | 197 return False |
199 | 198 |
200 # Determine if we have root access. | 199 # Determine if we have root access. |
201 has_root = False | 200 has_root = False |
202 try: | 201 try: |
203 output = self._adb(name='adb root', | 202 output = self._adb(name='adb root', |
204 serial=self.serial, | 203 serial=self.serial, |
205 cmd=['root'], | 204 cmd=['root'], |
206 stdout=self._skia_api.m.raw_io.output(), | 205 stdout=self.m.raw_io.output(), |
207 infra_step=True).stdout.rstrip() | 206 infra_step=True).stdout.rstrip() |
208 if ('restarting adbd as root' in output or | 207 if ('restarting adbd as root' in output or |
209 'adbd is already running as root' in output): | 208 'adbd is already running as root' in output): |
210 has_root = True | 209 has_root = True |
211 except self._skia_api.m.step.StepFailure: # pragma: nocover | 210 except self.m.step.StepFailure: # pragma: nocover |
212 pass | 211 pass |
213 # Wait for the device to reconnect. | 212 # Wait for the device to reconnect. |
214 self._skia_api.run( | 213 self.m.run( |
215 self._skia_api.m.step, | 214 self.m.step, |
216 name='wait', | 215 name='wait', |
217 cmd=['sleep', '10'], | 216 cmd=['sleep', '10'], |
218 infra_step=True) | 217 infra_step=True) |
219 self._adb.wait_for_device() | 218 self._adb.wait_for_device() |
220 return has_root | 219 return has_root |
221 | 220 |
222 def install(self): | 221 def install(self): |
223 """Run device-specific installation steps.""" | 222 """Run device-specific installation steps.""" |
| 223 device_scratch_dir = self._adb( |
| 224 name='get EXTERNAL_STORAGE dir', |
| 225 serial=self.serial, |
| 226 cmd=['shell', 'echo', '$EXTERNAL_STORAGE'], |
| 227 stdout=self.m.raw_io.output(), |
| 228 infra_step=True, |
| 229 ).stdout.rstrip() |
| 230 prefix = self.device_path_join(device_scratch_dir, 'skiabot', 'skia_') |
| 231 self.device_dirs = default_flavor.DeviceDirs( |
| 232 dm_dir=prefix + 'dm', |
| 233 perf_data_dir=prefix + 'perf', |
| 234 resource_dir=prefix + 'resources', |
| 235 images_dir=prefix + 'images', |
| 236 skp_dir=prefix + 'skp/skps', |
| 237 tmp_dir=prefix + 'tmp_dir') |
| 238 |
224 self._has_root = self.has_root() | 239 self._has_root = self.has_root() |
225 self._skia_api.run(self._skia_api.m.step, | 240 self.m.run(self.m.step, |
226 name='kill skia', | 241 name='kill skia', |
227 cmd=[ | 242 cmd=[ |
228 self.android_bin.join('android_kill_skia'), | 243 self.android_bin.join('android_kill_skia'), |
229 '--verbose', | 244 '--verbose', |
230 ] + self.serial_args, | 245 ] + self.serial_args, |
231 env=self._default_env, | 246 env=self._default_env, |
232 infra_step=True) | 247 infra_step=True) |
233 if self._has_root: | 248 if self._has_root: |
234 self._adb(name='stop shell', | 249 self._adb(name='stop shell', |
235 serial=self.serial, | 250 serial=self.serial, |
(...skipping 14 matching lines...) Expand all Loading... |
250 '/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor'], | 265 '/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor'], |
251 infra_step=True) | 266 infra_step=True) |
252 self._adb(name='cat cpu_freq', | 267 self._adb(name='cat cpu_freq', |
253 serial=self.serial, | 268 serial=self.serial, |
254 cmd=['shell', 'cat', | 269 cmd=['shell', 'cat', |
255 '/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq'], | 270 '/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq'], |
256 infra_step=True) | 271 infra_step=True) |
257 | 272 |
258 def cleanup_steps(self): | 273 def cleanup_steps(self): |
259 """Run any device-specific cleanup steps.""" | 274 """Run any device-specific cleanup steps.""" |
260 if self._skia_api.do_test_steps or self._skia_api.do_perf_steps: | 275 if self.m.vars.do_test_steps or self.m.vars.do_perf_steps: |
261 self._adb(name='final battery stats', | 276 self._adb(name='final battery stats', |
262 serial=self.serial, | 277 serial=self.serial, |
263 cmd=['shell', 'dumpsys', 'batteryproperties'], | 278 cmd=['shell', 'dumpsys', 'batteryproperties'], |
264 infra_step=True) | 279 infra_step=True) |
265 self._adb(name='reboot', | 280 self._adb(name='reboot', |
266 serial=self.serial, | 281 serial=self.serial, |
267 cmd=['reboot'], | 282 cmd=['reboot'], |
268 infra_step=True) | 283 infra_step=True) |
269 self._skia_api.run( | 284 self.m.run( |
270 self._skia_api.m.step, | 285 self.m.step, |
271 name='wait for reboot', | 286 name='wait for reboot', |
272 cmd=['sleep', '10'], | 287 cmd=['sleep', '10'], |
273 infra_step=True) | 288 infra_step=True) |
274 self._adb.wait_for_device() | 289 self._adb.wait_for_device() |
275 # The ADB binary conflicts with py-adb used by swarming. Kill it | 290 # The ADB binary conflicts with py-adb used by swarming. Kill it |
276 # when finished to play nice. | 291 # when finished to play nice. |
277 self._adb(name='kill-server', | 292 self._adb(name='kill-server', |
278 serial=self.serial, | 293 serial=self.serial, |
279 cmd=['kill-server'], | 294 cmd=['kill-server'], |
280 infra_step=True) | 295 infra_step=True) |
281 | 296 |
282 def read_file_on_device(self, path, *args, **kwargs): | 297 def read_file_on_device(self, path, *args, **kwargs): |
283 """Read the given file.""" | 298 """Read the given file.""" |
284 return self._adb(name='read %s' % self._skia_api.m.path.basename(path), | 299 return self._adb(name='read %s' % self.m.path.basename(path), |
285 serial=self.serial, | 300 serial=self.serial, |
286 cmd=['shell', 'cat', path], | 301 cmd=['shell', 'cat', path], |
287 stdout=self._skia_api.m.raw_io.output(), | 302 stdout=self.m.raw_io.output(), |
288 infra_step=True).stdout.rstrip() | 303 infra_step=True).stdout.rstrip() |
289 | 304 |
290 def remove_file_on_device(self, path, *args, **kwargs): | 305 def remove_file_on_device(self, path, *args, **kwargs): |
291 """Delete the given file.""" | 306 """Delete the given file.""" |
292 return self._adb(name='rm %s' % self._skia_api.m.path.basename(path), | 307 return self._adb(name='rm %s' % self.m.path.basename(path), |
293 serial=self.serial, | 308 serial=self.serial, |
294 cmd=['shell', 'rm', '-f', path], | 309 cmd=['shell', 'rm', '-f', path], |
295 infra_step=True, | 310 infra_step=True, |
296 *args, | 311 *args, |
297 **kwargs) | 312 **kwargs) |
298 | |
299 def get_device_dirs(self): | |
300 """ Set the directories which will be used by the build steps.""" | |
301 device_scratch_dir = self._adb( | |
302 name='get EXTERNAL_STORAGE dir', | |
303 serial=self.serial, | |
304 cmd=['shell', 'echo', '$EXTERNAL_STORAGE'], | |
305 stdout=self._skia_api.m.raw_io.output(), | |
306 infra_step=True, | |
307 ).stdout.rstrip() | |
308 prefix = self.device_path_join(device_scratch_dir, 'skiabot', 'skia_') | |
309 return default_flavor.DeviceDirs( | |
310 dm_dir=prefix + 'dm', | |
311 perf_data_dir=prefix + 'perf', | |
312 resource_dir=prefix + 'resources', | |
313 images_dir=prefix + 'images', | |
314 skp_dir=prefix + 'skp/skps', | |
315 tmp_dir=prefix + 'tmp_dir') | |
316 | |
OLD | NEW |