blob: e7c33ca19b30fa8871fc203df83a657a182b4559 [file] [log] [blame]
Ang Li93420002016-05-10 19:11:44 -07001#!/usr/bin/env python3.4
2#
3# Copyright 2016 - The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17from builtins import str
18from builtins import open
19
Ang Li7f0e1c72016-06-14 11:23:49 -070020import logging
Ang Li93420002016-05-10 19:11:44 -070021import os
22import time
23import traceback
24
25from vts.runners.host import logger as vts_logger
26from vts.runners.host import signals
27from vts.runners.host import utils
Ang Li7f0e1c72016-06-14 11:23:49 -070028from vts.utils.python.controllers import adb
29from vts.utils.python.controllers import event_dispatcher
30from vts.utils.python.controllers import fastboot
31
32from vts.utils.python.mirror import hal_mirror
Ang Li93420002016-05-10 19:11:44 -070033
34VTS_CONTROLLER_CONFIG_NAME = "AndroidDevice"
35VTS_CONTROLLER_REFERENCE_NAME = "android_devices"
36
37ANDROID_DEVICE_PICK_ALL_TOKEN = "*"
38# Key name for adb logcat extra params in config file.
39ANDROID_DEVICE_ADB_LOGCAT_PARAM_KEY = "adb_logcat_param"
40ANDROID_DEVICE_EMPTY_CONFIG_MSG = "Configuration is empty, abort!"
41ANDROID_DEVICE_NOT_LIST_CONFIG_MSG = "Configuration should be a list, abort!"
42
Ang Lie2139f12016-05-12 17:39:06 -070043
Ang Li93420002016-05-10 19:11:44 -070044class AndroidDeviceError(signals.ControllerError):
45 pass
46
Ang Lie2139f12016-05-12 17:39:06 -070047
Ang Li7f0e1c72016-06-14 11:23:49 -070048def create(configs):
Ang Li93420002016-05-10 19:11:44 -070049 if not configs:
50 raise AndroidDeviceError(ANDROID_DEVICE_EMPTY_CONFIG_MSG)
51 elif configs == ANDROID_DEVICE_PICK_ALL_TOKEN:
Ang Li7f0e1c72016-06-14 11:23:49 -070052 ads = get_all_instances()
Ang Li93420002016-05-10 19:11:44 -070053 elif not isinstance(configs, list):
54 raise AndroidDeviceError(ANDROID_DEVICE_NOT_LIST_CONFIG_MSG)
55 elif isinstance(configs[0], str):
56 # Configs is a list of serials.
Ang Li7f0e1c72016-06-14 11:23:49 -070057 ads = get_instances(configs)
Ang Li93420002016-05-10 19:11:44 -070058 else:
59 # Configs is a list of dicts.
Ang Li7f0e1c72016-06-14 11:23:49 -070060 ads = get_instances_with_configs(configs)
Ang Li93420002016-05-10 19:11:44 -070061 connected_ads = list_adb_devices()
62 for ad in ads:
63 if ad.serial not in connected_ads:
Ang Lie2139f12016-05-12 17:39:06 -070064 raise AndroidDeviceError(
65 ("Android device %s is specified in config"
66 " but is not attached.") % ad.serial)
Ang Li93420002016-05-10 19:11:44 -070067 ad.startAdbLogcat()
Ang Li93420002016-05-10 19:11:44 -070068 return ads
69
Ang Lie2139f12016-05-12 17:39:06 -070070
Ang Li93420002016-05-10 19:11:44 -070071def destroy(ads):
72 for ad in ads:
73 try:
74 ad.closeAllSl4aSession()
75 except:
76 pass
77 if ad.adb_logcat_process:
78 ad.stopAdbLogcat()
79
Ang Lie2139f12016-05-12 17:39:06 -070080
Ang Li93420002016-05-10 19:11:44 -070081def _parse_device_list(device_list_str, key):
82 """Parses a byte string representing a list of devices. The string is
83 generated by calling either adb or fastboot.
84
85 Args:
86 device_list_str: Output of adb or fastboot.
87 key: The token that signifies a device in device_list_str.
88
89 Returns:
90 A list of android device serial numbers.
91 """
92 clean_lines = str(device_list_str, 'utf-8').strip().split('\n')
93 results = []
94 for line in clean_lines:
95 tokens = line.strip().split('\t')
96 if len(tokens) == 2 and tokens[1] == key:
97 results.append(tokens[0])
98 return results
99
Ang Lie2139f12016-05-12 17:39:06 -0700100
Ang Li93420002016-05-10 19:11:44 -0700101def list_adb_devices():
102 """List all android devices connected to the computer that are detected by
103 adb.
104
105 Returns:
106 A list of android device serials. Empty if there's none.
107 """
108 out = adb.AdbProxy().devices()
109 return _parse_device_list(out, "device")
110
Ang Lie2139f12016-05-12 17:39:06 -0700111
Ang Li93420002016-05-10 19:11:44 -0700112def list_fastboot_devices():
113 """List all android devices connected to the computer that are in in
114 fastboot mode. These are detected by fastboot.
115
116 Returns:
117 A list of android device serials. Empty if there's none.
118 """
119 out = fastboot.FastbootProxy().devices()
120 return _parse_device_list(out, "fastboot")
121
Ang Lie2139f12016-05-12 17:39:06 -0700122
Ang Li7f0e1c72016-06-14 11:23:49 -0700123def get_instances(serials):
Ang Li93420002016-05-10 19:11:44 -0700124 """Create AndroidDevice instances from a list of serials.
125
126 Args:
127 serials: A list of android device serials.
Ang Li93420002016-05-10 19:11:44 -0700128
129 Returns:
130 A list of AndroidDevice objects.
131 """
132 results = []
133 for s in serials:
Ang Li7f0e1c72016-06-14 11:23:49 -0700134 results.append(AndroidDevice(s))
Ang Li93420002016-05-10 19:11:44 -0700135 return results
136
Ang Lie2139f12016-05-12 17:39:06 -0700137
Ang Li7f0e1c72016-06-14 11:23:49 -0700138def get_instances_with_configs(configs):
Ang Li93420002016-05-10 19:11:44 -0700139 """Create AndroidDevice instances from a list of json configs.
140
141 Each config should have the required key-value pair "serial".
142
143 Args:
144 configs: A list of dicts each representing the configuration of one
145 android device.
Ang Li93420002016-05-10 19:11:44 -0700146
147 Returns:
148 A list of AndroidDevice objects.
149 """
150 results = []
151 for c in configs:
152 try:
153 serial = c.pop("serial")
154 except KeyError:
155 raise AndroidDeviceError(('Required value "serial" is missing in '
Ang Lie2139f12016-05-12 17:39:06 -0700156 'AndroidDevice config %s.') % c)
Ang Li7f0e1c72016-06-14 11:23:49 -0700157 ad = AndroidDevice(serial)
Ang Li93420002016-05-10 19:11:44 -0700158 ad.loadConfig(c)
159 results.append(ad)
160 return results
161
Ang Lie2139f12016-05-12 17:39:06 -0700162
Ang Li7f0e1c72016-06-14 11:23:49 -0700163def get_all_instances(include_fastboot=False):
Ang Li93420002016-05-10 19:11:44 -0700164 """Create AndroidDevice instances for all attached android devices.
165
166 Args:
167 include_fastboot: Whether to include devices in bootloader mode or not.
Ang Li93420002016-05-10 19:11:44 -0700168
169 Returns:
170 A list of AndroidDevice objects each representing an android device
171 attached to the computer.
172 """
173 if include_fastboot:
174 serial_list = list_adb_devices() + list_fastboot_devices()
Ang Li7f0e1c72016-06-14 11:23:49 -0700175 return get_instances(serial_list)
176 return get_instances(list_adb_devices())
Ang Li93420002016-05-10 19:11:44 -0700177
Ang Lie2139f12016-05-12 17:39:06 -0700178
Ang Li93420002016-05-10 19:11:44 -0700179def filter_devices(ads, func):
180 """Finds the AndroidDevice instances from a list that match certain
181 conditions.
182
183 Args:
184 ads: A list of AndroidDevice instances.
185 func: A function that takes an AndroidDevice object and returns True
186 if the device satisfies the filter condition.
187
188 Returns:
189 A list of AndroidDevice instances that satisfy the filter condition.
190 """
191 results = []
192 for ad in ads:
193 if func(ad):
194 results.append(ad)
195 return results
196
Ang Lie2139f12016-05-12 17:39:06 -0700197
Ang Li93420002016-05-10 19:11:44 -0700198def get_device(ads, **kwargs):
199 """Finds a unique AndroidDevice instance from a list that has specific
200 attributes of certain values.
201
202 Example:
203 get_device(android_devices, label="foo", phone_number="1234567890")
204 get_device(android_devices, model="angler")
205
206 Args:
207 ads: A list of AndroidDevice instances.
208 kwargs: keyword arguments used to filter AndroidDevice instances.
209
210 Returns:
211 The target AndroidDevice instance.
212
213 Raises:
214 AndroidDeviceError is raised if none or more than one device is
215 matched.
216 """
Ang Lie2139f12016-05-12 17:39:06 -0700217
Ang Li93420002016-05-10 19:11:44 -0700218 def _get_device_filter(ad):
219 for k, v in kwargs.items():
220 if not hasattr(ad, k):
221 return False
222 elif getattr(ad, k) != v:
223 return False
224 return True
Ang Lie2139f12016-05-12 17:39:06 -0700225
Ang Li93420002016-05-10 19:11:44 -0700226 filtered = filter_devices(ads, _get_device_filter)
227 if not filtered:
228 raise AndroidDeviceError(("Could not find a target device that matches"
229 " condition: %s.") % kwargs)
230 elif len(filtered) == 1:
231 return filtered[0]
232 else:
233 serials = [ad.serial for ad in filtered]
234 raise AndroidDeviceError("More than one device matched: %s" % serials)
235
Ang Lie2139f12016-05-12 17:39:06 -0700236
Ang Li93420002016-05-10 19:11:44 -0700237def takeBugReports(ads, test_name, begin_time):
238 """Takes bug reports on a list of android devices.
239
240 If you want to take a bug report, call this function with a list of
241 android_device objects in on_fail. But reports will be taken on all the
242 devices in the list concurrently. Bug report takes a relative long
243 time to take, so use this cautiously.
244
245 Args:
246 ads: A list of AndroidDevice instances.
247 test_name: Name of the test case that triggered this bug report.
248 begin_time: Logline format timestamp taken when the test started.
249 """
250 begin_time = vts_logger.normalizeLogLineTimestamp(begin_time)
Ang Lie2139f12016-05-12 17:39:06 -0700251
Ang Li93420002016-05-10 19:11:44 -0700252 def take_br(test_name, begin_time, ad):
253 ad.takeBugReport(test_name, begin_time)
Ang Lie2139f12016-05-12 17:39:06 -0700254
Ang Li93420002016-05-10 19:11:44 -0700255 args = [(test_name, begin_time, ad) for ad in ads]
256 utils.concurrent_exec(take_br, args)
257
Ang Lie2139f12016-05-12 17:39:06 -0700258
Ang Li7f0e1c72016-06-14 11:23:49 -0700259class AndroidDevice(object):
Ang Li93420002016-05-10 19:11:44 -0700260 """Class representing an android device.
261
262 Each object of this class represents one Android device in ACTS, including
263 handles to adb, fastboot, and sl4a clients. In addition to direct adb
264 commands, this object also uses adb port forwarding to talk to the Android
265 device.
266
267 Attributes:
268 serial: A string that's the serial number of the Androi device.
Ang Li93420002016-05-10 19:11:44 -0700269 d_port: An integer that's the port number used on the Android device
270 for adb port forwarding.
Ang Li93420002016-05-10 19:11:44 -0700271 log_path: A string that is the path where all logs collected on this
272 android device should be stored.
273 adb_logcat_process: A process that collects the adb logcat.
274 adb_logcat_file_path: A string that's the full path to the adb logcat
275 file collected, if any.
276 adb: An AdbProxy object used for interacting with the device via adb.
277 fastboot: A FastbootProxy object used for interacting with the device
278 via fastboot.
279 """
280
Ang Li7f0e1c72016-06-14 11:23:49 -0700281 def __init__(self, serial="", device_port=5001):
Ang Li93420002016-05-10 19:11:44 -0700282 self.serial = serial
Ang Li7f0e1c72016-06-14 11:23:49 -0700283 self.device_port = device_port
Ang Li93420002016-05-10 19:11:44 -0700284 self.log = logging.getLogger()
Ang Li7f0e1c72016-06-14 11:23:49 -0700285 base_log_path = getattr(self.log, "log_path", "/tmp/logs/")
286 self.log_path = os.path.join(base_log_path, "AndroidDevice%s" % serial)
Ang Li93420002016-05-10 19:11:44 -0700287 self.adb_logcat_process = None
288 self.adb_logcat_file_path = None
289 self.adb = adb.AdbProxy(serial)
290 self.fastboot = fastboot.FastbootProxy(serial)
291 if not self.isBootloaderMode:
292 self.rootAdb()
Ang Li7f0e1c72016-06-14 11:23:49 -0700293 self.host_port = adb.get_available_host_port()
294 self.adb.tcp_forward(self.host_port, self.device_port)
295 self.hal = hal_mirror.HalMirror(self.host_port)
Ang Li93420002016-05-10 19:11:44 -0700296
297 def __del__(self):
Ang Li7f0e1c72016-06-14 11:23:49 -0700298 if self.host_port:
299 self.adb.forward("--remove tcp:%s" % self.host_port)
Ang Li93420002016-05-10 19:11:44 -0700300 if self.adb_logcat_process:
301 self.stopAdbLogcat()
302
303 @property
304 def isBootloaderMode(self):
305 """True if the device is in bootloader mode.
306 """
307 return self.serial in list_fastboot_devices()
308
309 @property
310 def isAdbRoot(self):
311 """True if adb is running as root for this device.
312 """
313 return "root" in self.adb.shell("id -u").decode("utf-8")
314
315 @property
316 def model(self):
317 """The Android code name for the device.
318 """
319 # If device is in bootloader mode, get mode name from fastboot.
320 if self.isBootloaderMode:
321 out = self.fastboot.getvar("product").strip()
322 # "out" is never empty because of the "total time" message fastboot
323 # writes to stderr.
324 lines = out.decode("utf-8").split('\n', 1)
325 if lines:
326 tokens = lines[0].split(' ')
327 if len(tokens) > 1:
328 return tokens[1].lower()
329 return None
330 out = self.adb.shell('getprop | grep ro.build.product')
331 model = out.decode("utf-8").strip().split('[')[-1][:-1].lower()
332 if model == "sprout":
333 return model
334 else:
335 out = self.adb.shell('getprop | grep ro.product.name')
336 model = out.decode("utf-8").strip().split('[')[-1][:-1].lower()
337 return model
338
339 @property
Ang Li93420002016-05-10 19:11:44 -0700340 def isAdbLogcatOn(self):
341 """Whether there is an ongoing adb logcat collection.
342 """
343 if self.adb_logcat_process:
344 return True
345 return False
346
347 def loadConfig(self, config):
348 """Add attributes to the AndroidDevice object based on json config.
349
350 Args:
351 config: A dictionary representing the configs.
352
353 Raises:
354 AndroidDeviceError is raised if the config is trying to overwrite
355 an existing attribute.
356 """
357 for k, v in config.items():
358 if hasattr(self, k):
Ang Li7f0e1c72016-06-14 11:23:49 -0700359 raise AndroidDeviceError(
360 "Attempting to set existing attribute %s on %s" %
361 (k, self.serial))
Ang Li93420002016-05-10 19:11:44 -0700362 setattr(self, k, v)
363
364 def rootAdb(self):
365 """Change adb to root mode for this device.
366 """
367 if not self.isAdbRoot:
368 self.adb.root()
369 self.adb.wait_for_device()
370 self.adb.remount()
371 self.adb.wait_for_device()
372
Ang Li93420002016-05-10 19:11:44 -0700373 def startAdbLogcat(self):
374 """Starts a standing adb logcat collection in separate subprocesses and
375 save the logcat in a file.
376 """
377 if self.isAdbLogcatOn:
Ang Li7f0e1c72016-06-14 11:23:49 -0700378 raise AndroidDeviceError(("Android device %s already has an adb "
Ang Lie2139f12016-05-12 17:39:06 -0700379 "logcat thread going on. Cannot start "
Ang Li7f0e1c72016-06-14 11:23:49 -0700380 "another one.") % self.serial)
Ang Li93420002016-05-10 19:11:44 -0700381 # Disable adb log spam filter.
382 self.adb.shell("logpersist.start")
Ang Li7f0e1c72016-06-14 11:23:49 -0700383 f_name = "adblog,%s,%s.txt" % (self.model, self.serial)
Ang Li93420002016-05-10 19:11:44 -0700384 utils.create_dir(self.log_path)
385 logcat_file_path = os.path.join(self.log_path, f_name)
386 try:
387 extra_params = self.adb_logcat_param
388 except AttributeError:
389 extra_params = "-b all"
Ang Li7f0e1c72016-06-14 11:23:49 -0700390 cmd = "adb -s %s logcat -v threadtime %s >> %s" % (
Ang Li93420002016-05-10 19:11:44 -0700391 self.serial, extra_params, logcat_file_path)
392 self.adb_logcat_process = utils.start_standing_subprocess(cmd)
393 self.adb_logcat_file_path = logcat_file_path
394
395 def stopAdbLogcat(self):
396 """Stops the adb logcat collection subprocess.
397 """
398 if not self.isAdbLogcatOn:
Ang Li7f0e1c72016-06-14 11:23:49 -0700399 raise AndroidDeviceError(
400 "Android device %s does not have an ongoing adb logcat collection."
401 % self.serial)
Ang Li93420002016-05-10 19:11:44 -0700402 utils.stop_standing_subprocess(self.adb_logcat_process)
403 self.adb_logcat_process = None
404
405 def takeBugReport(self, test_name, begin_time):
406 """Takes a bug report on the device and stores it in a file.
407
408 Args:
409 test_name: Name of the test case that triggered this bug report.
410 begin_time: Logline format timestamp taken when the test started.
411 """
412 br_path = os.path.join(self.log_path, "BugReports")
413 utils.create_dir(br_path)
Ang Li7f0e1c72016-06-14 11:23:49 -0700414 base_name = ",%s,%s.txt" % (begin_time, self.serial)
Ang Li93420002016-05-10 19:11:44 -0700415 test_name_len = utils.MAX_FILENAME_LEN - len(base_name)
416 out_name = test_name[:test_name_len] + base_name
417 full_out_path = os.path.join(br_path, out_name.replace(' ', '\ '))
418 self.log.info("Taking bugreport for %s on %s", test_name, self.serial)
Ang Li7f0e1c72016-06-14 11:23:49 -0700419 self.adb.bugreport(" > %s" % full_out_path)
Ang Li93420002016-05-10 19:11:44 -0700420 self.log.info("Bugreport for %s taken at %s", test_name, full_out_path)
421
Ang Li93420002016-05-10 19:11:44 -0700422 @utils.timeout(15 * 60)
423 def waitForBootCompletion(self):
424 """Waits for Android framework to broadcast ACTION_BOOT_COMPLETED.
425
426 This function times out after 15 minutes.
427 """
428 self.adb.wait_for_device()
429 while True:
430 try:
431 out = self.adb.shell("getprop sys.boot_completed")
432 completed = out.decode('utf-8').strip()
433 if completed == '1':
434 return
435 except adb.AdbError:
436 # adb shell calls may fail during certain period of booting
437 # process, which is normal. Ignoring these errors.
438 pass
439 time.sleep(5)
440
441 def reboot(self):
Ang Li7f0e1c72016-06-14 11:23:49 -0700442 """Reboots the device and wait for device to complete booting.
Ang Li93420002016-05-10 19:11:44 -0700443
444 This is probably going to print some error messages in console. Only
445 use if there's no other option.
446
Ang Li93420002016-05-10 19:11:44 -0700447 Raises:
448 AndroidDeviceError is raised if waiting for completion timed
449 out.
450 """
451 if self.isBootloaderMode:
452 self.fastboot.reboot()
453 return
454 has_adb_log = self.isAdbLogcatOn
455 if has_adb_log:
456 self.stopAdbLogcat()
Ang Li93420002016-05-10 19:11:44 -0700457 self.adb.reboot()
458 self.waitForBootCompletion()
459 self.rootAdb()
Ang Li93420002016-05-10 19:11:44 -0700460 if has_adb_log:
461 self.startAdbLogcat()
Ang Li7f0e1c72016-06-14 11:23:49 -0700462