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