blob: 6997541c9934de3521180babd411a7830fee1e01 [file] [log] [blame]
Eric Boren7e97dc02017-02-02 09:02:37 -05001# Copyright 2016 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
Kevin Lubick90e3cd72017-02-09 10:08:13 -05005from recipe_engine import recipe_api
6
Eric Boren7e97dc02017-02-02 09:02:37 -05007import default_flavor
Eric Borenfe40abc2017-07-20 10:21:38 -04008import re
Eric Boren7e97dc02017-02-02 09:02:37 -05009import subprocess
10
11
12"""GN Android flavor utils, used for building Skia for Android with GN."""
13class GNAndroidFlavorUtils(default_flavor.DefaultFlavorUtils):
14 def __init__(self, m):
15 super(GNAndroidFlavorUtils, self).__init__(m)
16 self._ever_ran_adb = False
Kevin Lubick4fd283e2017-12-07 11:19:31 -050017 self.ADB_BINARY = '/usr/bin/adb.1.0.35'
18 golo_devices = ['Nexus5x']
19 if self.m.vars.builder_cfg.get('model') in golo_devices:
20 self.ADB_BINARY = '/opt/infra-android/tools/adb'
Eric Boren7e97dc02017-02-02 09:02:37 -050021
22 self.device_dirs = default_flavor.DeviceDirs(
23 dm_dir = self.m.vars.android_data_dir + 'dm_out',
24 perf_data_dir = self.m.vars.android_data_dir + 'perf',
25 resource_dir = self.m.vars.android_data_dir + 'resources',
26 images_dir = self.m.vars.android_data_dir + 'images',
27 skp_dir = self.m.vars.android_data_dir + 'skps',
28 svg_dir = self.m.vars.android_data_dir + 'svgs',
29 tmp_dir = self.m.vars.android_data_dir)
30
Eric Boren7e97dc02017-02-02 09:02:37 -050031 def _run(self, title, *cmd, **kwargs):
Robert Iannucci297a7ef2017-05-12 19:09:38 -070032 with self.m.context(cwd=self.m.vars.skia_dir):
Eric Boren53262d02017-03-20 15:40:12 -040033 return self.m.run(self.m.step, title, cmd=list(cmd), **kwargs)
Eric Boren7e97dc02017-02-02 09:02:37 -050034
35 def _py(self, title, script, infra_step=True):
Robert Iannucci297a7ef2017-05-12 19:09:38 -070036 with self.m.context(cwd=self.m.vars.skia_dir):
Eric Boren53262d02017-03-20 15:40:12 -040037 return self.m.run(self.m.python, title, script=script,
38 infra_step=infra_step)
Eric Boren7e97dc02017-02-02 09:02:37 -050039
40 def _adb(self, title, *cmd, **kwargs):
Eric Boren7e97dc02017-02-02 09:02:37 -050041 # The only non-infra adb steps (dm / nanobench) happen to not use _adb().
42 if 'infra_step' not in kwargs:
43 kwargs['infra_step'] = True
Ben Wagnercf9365a2017-09-08 14:06:38 -040044
Kevin Lubick4fd283e2017-12-07 11:19:31 -050045 self._ever_ran_adb = True
Ben Wagnercf9365a2017-09-08 14:06:38 -040046 attempts = 1
47 flaky_devices = ['NexusPlayer', 'PixelC']
48 if self.m.vars.builder_cfg.get('model') in flaky_devices:
49 attempts = 3
50
51 def wait_for_device(attempt):
52 self.m.run(self.m.step,
53 'kill adb server after failure of \'%s\' (attempt %d)' % (
54 title, attempt),
Kevin Lubick4fd283e2017-12-07 11:19:31 -050055 cmd=[self.ADB_BINARY, 'kill-server'],
Ben Wagnercf9365a2017-09-08 14:06:38 -040056 infra_step=True, timeout=30, abort_on_failure=False,
57 fail_build_on_failure=False)
Ben Wagner79a12562017-09-12 12:32:59 -040058 self.m.run(self.m.step,
Ben Wagnercf9365a2017-09-08 14:06:38 -040059 'wait for device after failure of \'%s\' (attempt %d)' % (
60 title, attempt),
Kevin Lubick4fd283e2017-12-07 11:19:31 -050061 cmd=[self.ADB_BINARY, 'wait-for-device'], infra_step=True,
Kevin Lubick587afc92017-10-12 12:21:47 -040062 timeout=180, abort_on_failure=False,
63 fail_build_on_failure=False)
Ben Wagnercf9365a2017-09-08 14:06:38 -040064
65 with self.m.context(cwd=self.m.vars.skia_dir):
Kevin Lubick7c1eae52017-10-13 12:57:49 -040066 return self.m.run.with_retry(self.m.step, title, attempts,
Kevin Lubick4fd283e2017-12-07 11:19:31 -050067 cmd=[self.ADB_BINARY]+list(cmd),
Kevin Lubick7c1eae52017-10-13 12:57:49 -040068 between_attempts_fn=wait_for_device,
69 **kwargs)
Kevin Lubickbca95a52017-11-20 16:06:06 -050070
Kevin Lubickd1bbd5f2017-11-21 16:47:16 -050071 # A list of devices we can't root. If rooting fails and a device is not
72 # on the list, we fail the task to avoid perf inconsistencies.
73 rootable_blacklist = ['GalaxyS6', 'GalaxyS7_G930A', 'GalaxyS7_G930FD',
74 'MotoG4', 'NVIDIA_Shield']
75
Kevin Lubickf1585aa2017-12-12 07:33:48 -050076 # Maps device type -> cpu ids that need to be set when scaling, with the
77 # primary CPU being listed first. CPUs are configured together,
78 # for example, the Nexus 5x has cpu0-3 and cpu4-5 linked together, each
79 # group using the same configuration - anything that happens to cpu0,
80 # happens to cpu1-3.
81 # cpus at index 1+ will be clocked down to powersave during nanobench.
82 # TODO(kjlubick): determine this dynamically - should be able to look
83 # at which cores have the highest frequency and affected_cpus.
84 cpus_to_scale = {
85 'Nexus5x': [4, 0],
86 'NexusPlayer': [2, 0],
87 'Pixel': [2, 0],
88 'Pixel2XL': [4, 0]
89 }
90
Kevin Lubicke3b01302017-12-12 15:57:20 -050091 # Maps device -> number of cores. TODO(kjlubick) if we want to do this
92 # long term, compute this dynamically.
93 total_cpus = {
94 'AndroidOne': 4,
95 'Nexus5': 4,
96 'Nexus7': 4,
97 'Nexus5x': 6,
98 'NexusPlayer': 4,
99 'Pixel': 4,
100 'Pixel2XL': 8,
101 'PixelC': 4,
102 }
103
Kevin Lubickf1585aa2017-12-12 07:33:48 -0500104 def _scale_for_dm(self):
105 device = self.m.vars.builder_cfg.get('model')
106 if (device in self.rootable_blacklist or
Kevin Lubick082f00a2017-11-28 08:33:37 -0500107 self.m.vars.internal_hardware_label):
Kevin Lubickbca95a52017-11-20 16:06:06 -0500108 return
Kevin Lubickf1585aa2017-12-12 07:33:48 -0500109
110 for i in self.cpus_to_scale.get(device, [0]):
Kevin Lubick17634922017-12-12 08:14:19 -0500111 # AndroidOne doesn't support ondemand governor. hotplug is similar.
112 if device == 'AndroidOne':
113 self._set_governor(i, 'hotplug')
114 else:
115 self._set_governor(i, 'ondemand')
Kevin Lubickf1585aa2017-12-12 07:33:48 -0500116
117 def _scale_for_nanobench(self):
118 device = self.m.vars.builder_cfg.get('model')
119 if (device in self.rootable_blacklist or
120 self.m.vars.internal_hardware_label):
121 return
122
Kevin Lubicke3b01302017-12-12 15:57:20 -0500123 # Scale just the first two cpus.
124 self._set_governor(0, 'userspace')
125 self._scale_cpu(0, 0.6)
Kevin Lubickf1585aa2017-12-12 07:33:48 -0500126
Kevin Lubicke3b01302017-12-12 15:57:20 -0500127 for i in range(2, self.total_cpus[device]):
Kevin Lubick17634922017-12-12 08:14:19 -0500128 # NexusPlayer only has "ondemand userspace interactive performance"
Kevin Lubicke3b01302017-12-12 15:57:20 -0500129 self._disable_cpu(i)
Kevin Lubickf1585aa2017-12-12 07:33:48 -0500130
Kevin Lubickf1585aa2017-12-12 07:33:48 -0500131
132 def _set_governor(self, cpu, gov):
133 self._ever_ran_adb = True
Kevin Lubick1c8092ac2017-11-30 08:35:29 -0500134 self.m.run.with_retry(self.m.python.inline,
Kevin Lubickf1585aa2017-12-12 07:33:48 -0500135 "Set CPU %d's governor to %s" % (cpu, gov),
Kevin Lubick1c8092ac2017-11-30 08:35:29 -0500136 3, # attempts
Kevin Lubickd1bbd5f2017-11-21 16:47:16 -0500137 program="""
138import os
139import subprocess
140import sys
Kevin Lubick1c8092ac2017-11-30 08:35:29 -0500141import time
Kevin Lubickd1bbd5f2017-11-21 16:47:16 -0500142ADB = sys.argv[1]
Kevin Lubickf1585aa2017-12-12 07:33:48 -0500143cpu = int(sys.argv[2])
144gov = sys.argv[3]
145
Kevin Lubickd1bbd5f2017-11-21 16:47:16 -0500146log = subprocess.check_output([ADB, 'root'])
147# check for message like 'adbd cannot run as root in production builds'
Kevin Lubick7f5d5d22017-12-08 10:26:56 -0500148print log
Kevin Lubickd1bbd5f2017-11-21 16:47:16 -0500149if 'cannot' in log:
150 raise Exception('adb root failed')
Kevin Lubickbca95a52017-11-20 16:06:06 -0500151
Kevin Lubickf1585aa2017-12-12 07:33:48 -0500152subprocess.check_output([ADB, 'shell', 'echo "%s" > '
153 '/sys/devices/system/cpu/cpu%d/cpufreq/scaling_governor' % (gov, cpu)])
154actual_gov = subprocess.check_output([ADB, 'shell', 'cat '
155 '/sys/devices/system/cpu/cpu%d/cpufreq/scaling_governor' % cpu]).strip()
Kevin Lubick17634922017-12-12 08:14:19 -0500156if actual_gov != gov:
157 raise Exception('(actual, expected) (%s, %s)'
Kevin Lubickf1585aa2017-12-12 07:33:48 -0500158 % (actual_gov, gov))
159""",
160 args = [self.ADB_BINARY, cpu, gov],
161 infra_step=True,
162 timeout=30)
Kevin Lubickbca95a52017-11-20 16:06:06 -0500163
Kevin Lubickf1585aa2017-12-12 07:33:48 -0500164
Kevin Lubicke3b01302017-12-12 15:57:20 -0500165 def _disable_cpu(self, cpu):
166 self._ever_ran_adb = True
167 self.m.run.with_retry(self.m.python.inline,
168 'Disabling CPU %d' % cpu,
169 3, # attempts
170 program="""
171import os
172import subprocess
173import sys
174import time
175ADB = sys.argv[1]
176cpu = int(sys.argv[2])
177
178log = subprocess.check_output([ADB, 'root'])
179# check for message like 'adbd cannot run as root in production builds'
180print log
181if 'cannot' in log:
182 raise Exception('adb root failed')
183
184subprocess.check_output([ADB, 'shell', 'echo 0 > '
185 '/sys/devices/system/cpu/cpu%d/online' % cpu])
186actual_status = subprocess.check_output([ADB, 'shell', 'cat '
187 '/sys/devices/system/cpu/cpu%d/online' % cpu]).strip()
188if actual_status != str(0):
189 raise Exception('(actual, expected) (%s, %d)'
190 % (actual_gov, gov))
191""",
192 args = [self.ADB_BINARY, cpu],
193 infra_step=True,
194 timeout=30)
195
196
Kevin Lubickf1585aa2017-12-12 07:33:48 -0500197 def _scale_cpu(self, cpu, target_percent):
198 self._ever_ran_adb = True
199 self.m.run.with_retry(self.m.python.inline,
200 'Scale CPU %d to %f' % (cpu, target_percent),
201 3, # attempts
202 program="""
203import os
204import subprocess
205import sys
206import time
207ADB = sys.argv[1]
208target_percent = float(sys.argv[2])
209cpu = int(sys.argv[3])
210log = subprocess.check_output([ADB, 'root'])
211# check for message like 'adbd cannot run as root in production builds'
212print log
213if 'cannot' in log:
214 raise Exception('adb root failed')
215
216root = '/sys/devices/system/cpu/cpu%d/cpufreq' %cpu
217
218# All devices we test on give a list of their available frequencies.
219available_freqs = subprocess.check_output([ADB, 'shell',
220 'cat %s/scaling_available_frequencies' % root])
221
222# Check for message like '/system/bin/sh: file not found'
223if available_freqs and '/system/bin/sh' not in available_freqs:
224 available_freqs = sorted(
225 int(i) for i in available_freqs.strip().split())
226else:
227 raise Exception('Could not get list of available frequencies: %s' %
228 available_freqs)
Kevin Lubickbca95a52017-11-20 16:06:06 -0500229
Kevin Lubickd1bbd5f2017-11-21 16:47:16 -0500230maxfreq = available_freqs[-1]
231target = int(round(maxfreq * target_percent))
232freq = maxfreq
233for f in reversed(available_freqs):
234 if f <= target:
235 freq = f
236 break
237
238print 'Setting frequency to %d' % freq
239
Kevin Lubickd1bbd5f2017-11-21 16:47:16 -0500240# If scaling_max_freq is lower than our attempted setting, it won't take.
Kevin Lubick7f5d5d22017-12-08 10:26:56 -0500241# We must set min first, because if we try to set max to be less than min
242# (which sometimes happens after certain devices reboot) it returns a
243# perplexing permissions error.
Kevin Lubick4c6bde22017-12-05 15:54:19 -0500244subprocess.check_output([ADB, 'shell', 'echo 0 > '
Kevin Lubickf1585aa2017-12-12 07:33:48 -0500245 '%s/scaling_min_freq' % root])
Kevin Lubick1c8092ac2017-11-30 08:35:29 -0500246subprocess.check_output([ADB, 'shell', 'echo %d > '
Kevin Lubickf1585aa2017-12-12 07:33:48 -0500247 '%s/scaling_max_freq' % (freq, root)])
Kevin Lubick7f5d5d22017-12-08 10:26:56 -0500248subprocess.check_output([ADB, 'shell', 'echo %d > '
Kevin Lubickf1585aa2017-12-12 07:33:48 -0500249 '%s/scaling_setspeed' % (freq, root)])
Kevin Lubick1c8092ac2017-11-30 08:35:29 -0500250time.sleep(5)
Kevin Lubickd1bbd5f2017-11-21 16:47:16 -0500251actual_freq = subprocess.check_output([ADB, 'shell', 'cat '
Kevin Lubickf1585aa2017-12-12 07:33:48 -0500252 '%s/scaling_cur_freq' % root]).strip()
Kevin Lubickd1bbd5f2017-11-21 16:47:16 -0500253if actual_freq != str(freq):
254 raise Exception('(actual, expected) (%s, %d)'
255 % (actual_freq, freq))
Kevin Lubick7f5d5d22017-12-08 10:26:56 -0500256""",
Kevin Lubickf1585aa2017-12-12 07:33:48 -0500257 args = [self.ADB_BINARY, str(target_percent), cpu],
Kevin Lubickd1bbd5f2017-11-21 16:47:16 -0500258 infra_step=True,
259 timeout=30)
Kevin Lubickbca95a52017-11-20 16:06:06 -0500260
Eric Boren53262d02017-03-20 15:40:12 -0400261 def compile(self, unused_target):
Eric Boren7e97dc02017-02-02 09:02:37 -0500262 compiler = self.m.vars.builder_cfg.get('compiler')
263 configuration = self.m.vars.builder_cfg.get('configuration')
Ben Wagner37491d22017-12-13 13:00:47 -0500264 extra_tokens = self.m.vars.extra_tokens
Eric Boren7e97dc02017-02-02 09:02:37 -0500265 os = self.m.vars.builder_cfg.get('os')
266 target_arch = self.m.vars.builder_cfg.get('target_arch')
267
268 assert compiler == 'Clang' # At this rate we might not ever support GCC.
269
270 extra_cflags = []
271 if configuration == 'Debug':
272 extra_cflags.append('-O1')
273
274 ndk_asset = 'android_ndk_linux'
275 if 'Mac' in os:
276 ndk_asset = 'android_ndk_darwin'
277 elif 'Win' in os:
278 ndk_asset = 'n'
279
280 quote = lambda x: '"%s"' % x
281 args = {
282 'ndk': quote(self.m.vars.slave_dir.join(ndk_asset)),
283 'target_cpu': quote(target_arch),
284 }
285
286 if configuration != 'Debug':
287 args['is_debug'] = 'false'
Ben Wagner37491d22017-12-13 13:00:47 -0500288 if 'Vulkan' in extra_tokens:
Eric Boren7e97dc02017-02-02 09:02:37 -0500289 args['ndk_api'] = 24
290 args['skia_enable_vulkan_debug_layers'] = 'false'
Eric Borenfe40abc2017-07-20 10:21:38 -0400291
292 # If an Android API level is specified, use that.
Ben Wagner37491d22017-12-13 13:00:47 -0500293 for t in extra_tokens:
294 m = re.search(r'API(\d+)', t)
295 if m and len(m.groups()) == 1:
296 args['ndk_api'] = m.groups()[0]
297 break
Eric Borenfe40abc2017-07-20 10:21:38 -0400298
Eric Boren7e97dc02017-02-02 09:02:37 -0500299 if extra_cflags:
300 args['extra_cflags'] = repr(extra_cflags).replace("'", '"')
301
302 gn_args = ' '.join('%s=%s' % (k,v) for (k,v) in sorted(args.iteritems()))
303
304 gn = 'gn.exe' if 'Win' in os else 'gn'
305 ninja = 'ninja.exe' if 'Win' in os else 'ninja'
306 gn = self.m.vars.skia_dir.join('bin', gn)
307
308 self._py('fetch-gn', self.m.vars.skia_dir.join('bin', 'fetch-gn'))
309 self._run('gn gen', gn, 'gen', self.out_dir, '--args=' + gn_args)
Mike Klein7a13d872017-11-06 09:28:59 -0500310 self._run('ninja', ninja, '-k', '0', '-C', self.out_dir)
Eric Boren7e97dc02017-02-02 09:02:37 -0500311
312 def install(self):
313 self._adb('mkdir ' + self.device_dirs.resource_dir,
314 'shell', 'mkdir', '-p', self.device_dirs.resource_dir)
315
Kevin Lubick90e3cd72017-02-09 10:08:13 -0500316
Eric Boren7e97dc02017-02-02 09:02:37 -0500317 def cleanup_steps(self):
318 if self._ever_ran_adb:
Kevin Lubick549638c2017-02-09 16:13:10 -0500319 self.m.run(self.m.python.inline, 'dump log', program="""
Eric Borenb82fdc72017-04-19 13:36:00 -0400320 import os
321 import subprocess
322 import sys
323 out = sys.argv[1]
Kevin Lubick587afc92017-10-12 12:21:47 -0400324 log = subprocess.check_output(['%s', 'logcat', '-d'])
Eric Borenb82fdc72017-04-19 13:36:00 -0400325 for line in log.split('\\n'):
326 tokens = line.split()
327 if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':
328 addr, path = tokens[-2:]
329 local = os.path.join(out, os.path.basename(path))
330 if os.path.exists(local):
331 sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])
332 line = line.replace(addr, addr + ' ' + sym.strip())
333 print line
Kevin Lubick4fd283e2017-12-07 11:19:31 -0500334 """ % self.ADB_BINARY,
Eric Borenb82fdc72017-04-19 13:36:00 -0400335 args=[self.m.vars.skia_out.join(self.m.vars.configuration)],
336 infra_step=True,
Kevin Lubick587afc92017-10-12 12:21:47 -0400337 timeout=300,
Eric Borenb82fdc72017-04-19 13:36:00 -0400338 abort_on_failure=False)
Kevin Lubick90e3cd72017-02-09 10:08:13 -0500339
Kevin Lubick65e8a712017-02-15 10:08:51 -0500340 # Only shutdown the device and quarantine the bot if the first failed step
341 # is an infra step. If, instead, we did this for any infra failures, we
342 # would shutdown too much. For example, if a Nexus 10 died during dm
343 # and the following pull step would also fail "device not found" - causing
344 # us to run the shutdown command when the device was probably not in a
345 # broken state; it was just rebooting.
346 if (self.m.run.failed_steps and
347 isinstance(self.m.run.failed_steps[0], recipe_api.InfraFailure)):
348 self._adb('shut down device to quarantine bot', 'shell', 'reboot', '-p')
Kevin Lubick90e3cd72017-02-09 10:08:13 -0500349
350 if self._ever_ran_adb:
Eric Boren7e97dc02017-02-02 09:02:37 -0500351 self._adb('kill adb server', 'kill-server')
352
Eric Boren53262d02017-03-20 15:40:12 -0400353 def step(self, name, cmd, **kwargs):
Kevin Lubickbca95a52017-11-20 16:06:06 -0500354 if (cmd[0] == 'nanobench'):
Kevin Lubickf1585aa2017-12-12 07:33:48 -0500355 self._scale_for_nanobench()
Kevin Lubickbca95a52017-11-20 16:06:06 -0500356 else:
Kevin Lubickf1585aa2017-12-12 07:33:48 -0500357 self._scale_for_dm()
Eric Boren7e97dc02017-02-02 09:02:37 -0500358 app = self.m.vars.skia_out.join(self.m.vars.configuration, cmd[0])
359 self._adb('push %s' % cmd[0],
360 'push', app, self.m.vars.android_bin_dir)
361
362 sh = '%s.sh' % cmd[0]
363 self.m.run.writefile(self.m.vars.tmp_dir.join(sh),
364 'set -x; %s%s; echo $? >%src' %
365 (self.m.vars.android_bin_dir, subprocess.list2cmdline(map(str, cmd)),
366 self.m.vars.android_bin_dir))
367 self._adb('push %s' % sh,
368 'push', self.m.vars.tmp_dir.join(sh), self.m.vars.android_bin_dir)
369
370 self._adb('clear log', 'logcat', '-c')
371 self.m.python.inline('%s' % cmd[0], """
372 import subprocess
373 import sys
374 bin_dir = sys.argv[1]
375 sh = sys.argv[2]
Kevin Lubick587afc92017-10-12 12:21:47 -0400376 subprocess.check_call(['%s', 'shell', 'sh', bin_dir + sh])
Eric Boren7e97dc02017-02-02 09:02:37 -0500377 try:
Kevin Lubick587afc92017-10-12 12:21:47 -0400378 sys.exit(int(subprocess.check_output(['%s', 'shell', 'cat',
Eric Boren7e97dc02017-02-02 09:02:37 -0500379 bin_dir + 'rc'])))
380 except ValueError:
381 print "Couldn't read the return code. Probably killed for OOM."
382 sys.exit(1)
Kevin Lubick4fd283e2017-12-07 11:19:31 -0500383 """ % (self.ADB_BINARY, self.ADB_BINARY),
384 args=[self.m.vars.android_bin_dir, sh])
Eric Boren7e97dc02017-02-02 09:02:37 -0500385
386 def copy_file_to_device(self, host, device):
387 self._adb('push %s %s' % (host, device), 'push', host, device)
388
389 def copy_directory_contents_to_device(self, host, device):
390 # Copy the tree, avoiding hidden directories and resolving symlinks.
Kevin Lubick549638c2017-02-09 16:13:10 -0500391 self.m.run(self.m.python.inline, 'push %s/* %s' % (host, device),
392 program="""
Eric Boren7e97dc02017-02-02 09:02:37 -0500393 import os
394 import subprocess
395 import sys
396 host = sys.argv[1]
397 device = sys.argv[2]
398 for d, _, fs in os.walk(host):
399 p = os.path.relpath(d, host)
400 if p != '.' and p.startswith('.'):
401 continue
402 for f in fs:
403 print os.path.join(p,f)
Kevin Lubick587afc92017-10-12 12:21:47 -0400404 subprocess.check_call(['%s', 'push',
Eric Boren7e97dc02017-02-02 09:02:37 -0500405 os.path.realpath(os.path.join(host, p, f)),
406 os.path.join(device, p, f)])
Kevin Lubick4fd283e2017-12-07 11:19:31 -0500407 """ % self.ADB_BINARY, args=[host, device], infra_step=True)
Eric Boren7e97dc02017-02-02 09:02:37 -0500408
409 def copy_directory_contents_to_host(self, device, host):
410 self._adb('pull %s %s' % (device, host), 'pull', device, host)
411
Eric Borenbb05f702017-04-24 15:59:55 -0400412 def read_file_on_device(self, path, **kwargs):
413 rv = self._adb('read %s' % path,
414 'shell', 'cat', path, stdout=self.m.raw_io.output(),
415 **kwargs)
416 return rv.stdout.rstrip() if rv and rv.stdout else None
Eric Boren7e97dc02017-02-02 09:02:37 -0500417
418 def remove_file_on_device(self, path):
419 self._adb('rm %s' % path, 'shell', 'rm', '-f', path)
420
421 def create_clean_device_dir(self, path):
422 self._adb('rm %s' % path, 'shell', 'rm', '-rf', path)
423 self._adb('mkdir %s' % path, 'shell', 'mkdir', '-p', path)