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