blob: 01a2368756fd56d3f40ead1707324faee91fde35 [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
20import os
21import time
22import traceback
23
24from vts.runners.host import logger as vts_logger
25from vts.runners.host import signals
26from vts.runners.host import utils
27from vts.runners.utils.pythoncontrollers import adb
28from vts.runners.utils.pythoncontrollers import android
29from vts.runners.utils.pythoncontrollers import event_dispatcher
30from vts.runners.utils.pythoncontrollers import fastboot
31
32VTS_CONTROLLER_CONFIG_NAME = "AndroidDevice"
33VTS_CONTROLLER_REFERENCE_NAME = "android_devices"
34
35ANDROID_DEVICE_PICK_ALL_TOKEN = "*"
36# Key name for adb logcat extra params in config file.
37ANDROID_DEVICE_ADB_LOGCAT_PARAM_KEY = "adb_logcat_param"
38ANDROID_DEVICE_EMPTY_CONFIG_MSG = "Configuration is empty, abort!"
39ANDROID_DEVICE_NOT_LIST_CONFIG_MSG = "Configuration should be a list, abort!"
40
Ang Lie2139f12016-05-12 17:39:06 -070041
Ang Li93420002016-05-10 19:11:44 -070042class AndroidDeviceError(signals.ControllerError):
43 pass
44
Ang Lie2139f12016-05-12 17:39:06 -070045
Ang Li93420002016-05-10 19:11:44 -070046def create(configs, logger):
47 if not configs:
48 raise AndroidDeviceError(ANDROID_DEVICE_EMPTY_CONFIG_MSG)
49 elif configs == ANDROID_DEVICE_PICK_ALL_TOKEN:
50 ads = get_all_instances(logger=logger)
51 elif not isinstance(configs, list):
52 raise AndroidDeviceError(ANDROID_DEVICE_NOT_LIST_CONFIG_MSG)
53 elif isinstance(configs[0], str):
54 # Configs is a list of serials.
55 ads = get_instances(configs, logger)
56 else:
57 # Configs is a list of dicts.
58 ads = get_instances_with_configs(configs, logger)
59 connected_ads = list_adb_devices()
60 for ad in ads:
61 if ad.serial not in connected_ads:
Ang Lie2139f12016-05-12 17:39:06 -070062 raise AndroidDeviceError(
63 ("Android device %s is specified in config"
64 " but is not attached.") % ad.serial)
Ang Li93420002016-05-10 19:11:44 -070065 ad.startAdbLogcat()
66 try:
67 ad.getSl4aClient()
68 ad.ed.start()
69 except:
70 # This exception is logged here to help with debugging under py2,
71 # because "exception raised while processing another exception" is
72 # only printed under py3.
73 msg = "Failed to start sl4a on %s" % ad.serial
74 logger.exception(msg)
75 raise AndroidDeviceError(msg)
76 return ads
77
Ang Lie2139f12016-05-12 17:39:06 -070078
Ang Li93420002016-05-10 19:11:44 -070079def destroy(ads):
80 for ad in ads:
81 try:
82 ad.closeAllSl4aSession()
83 except:
84 pass
85 if ad.adb_logcat_process:
86 ad.stopAdbLogcat()
87
Ang Lie2139f12016-05-12 17:39:06 -070088
Ang Li93420002016-05-10 19:11:44 -070089def _parse_device_list(device_list_str, key):
90 """Parses a byte string representing a list of devices. The string is
91 generated by calling either adb or fastboot.
92
93 Args:
94 device_list_str: Output of adb or fastboot.
95 key: The token that signifies a device in device_list_str.
96
97 Returns:
98 A list of android device serial numbers.
99 """
100 clean_lines = str(device_list_str, 'utf-8').strip().split('\n')
101 results = []
102 for line in clean_lines:
103 tokens = line.strip().split('\t')
104 if len(tokens) == 2 and tokens[1] == key:
105 results.append(tokens[0])
106 return results
107
Ang Lie2139f12016-05-12 17:39:06 -0700108
Ang Li93420002016-05-10 19:11:44 -0700109def list_adb_devices():
110 """List all android devices connected to the computer that are detected by
111 adb.
112
113 Returns:
114 A list of android device serials. Empty if there's none.
115 """
116 out = adb.AdbProxy().devices()
117 return _parse_device_list(out, "device")
118
Ang Lie2139f12016-05-12 17:39:06 -0700119
Ang Li93420002016-05-10 19:11:44 -0700120def list_fastboot_devices():
121 """List all android devices connected to the computer that are in in
122 fastboot mode. These are detected by fastboot.
123
124 Returns:
125 A list of android device serials. Empty if there's none.
126 """
127 out = fastboot.FastbootProxy().devices()
128 return _parse_device_list(out, "fastboot")
129
Ang Lie2139f12016-05-12 17:39:06 -0700130
Ang Li93420002016-05-10 19:11:44 -0700131def get_instances(serials, logger=None):
132 """Create AndroidDevice instances from a list of serials.
133
134 Args:
135 serials: A list of android device serials.
136 logger: A logger to be passed to each instance.
137
138 Returns:
139 A list of AndroidDevice objects.
140 """
141 results = []
142 for s in serials:
143 results.append(AndroidDevice(s, logger=logger))
144 return results
145
Ang Lie2139f12016-05-12 17:39:06 -0700146
Ang Li93420002016-05-10 19:11:44 -0700147def get_instances_with_configs(configs, logger=None):
148 """Create AndroidDevice instances from a list of json configs.
149
150 Each config should have the required key-value pair "serial".
151
152 Args:
153 configs: A list of dicts each representing the configuration of one
154 android device.
155 logger: A logger to be passed to each instance.
156
157 Returns:
158 A list of AndroidDevice objects.
159 """
160 results = []
161 for c in configs:
162 try:
163 serial = c.pop("serial")
164 except KeyError:
165 raise AndroidDeviceError(('Required value "serial" is missing in '
Ang Lie2139f12016-05-12 17:39:06 -0700166 'AndroidDevice config %s.') % c)
Ang Li93420002016-05-10 19:11:44 -0700167 ad = AndroidDevice(serial, logger=logger)
168 ad.loadConfig(c)
169 results.append(ad)
170 return results
171
Ang Lie2139f12016-05-12 17:39:06 -0700172
Ang Li93420002016-05-10 19:11:44 -0700173def get_all_instances(include_fastboot=False, logger=None):
174 """Create AndroidDevice instances for all attached android devices.
175
176 Args:
177 include_fastboot: Whether to include devices in bootloader mode or not.
178 logger: A logger to be passed to each instance.
179
180 Returns:
181 A list of AndroidDevice objects each representing an android device
182 attached to the computer.
183 """
184 if include_fastboot:
185 serial_list = list_adb_devices() + list_fastboot_devices()
186 return get_instances(serial_list, logger=logger)
187 return get_instances(list_adb_devices(), logger=logger)
188
Ang Lie2139f12016-05-12 17:39:06 -0700189
Ang Li93420002016-05-10 19:11:44 -0700190def filter_devices(ads, func):
191 """Finds the AndroidDevice instances from a list that match certain
192 conditions.
193
194 Args:
195 ads: A list of AndroidDevice instances.
196 func: A function that takes an AndroidDevice object and returns True
197 if the device satisfies the filter condition.
198
199 Returns:
200 A list of AndroidDevice instances that satisfy the filter condition.
201 """
202 results = []
203 for ad in ads:
204 if func(ad):
205 results.append(ad)
206 return results
207
Ang Lie2139f12016-05-12 17:39:06 -0700208
Ang Li93420002016-05-10 19:11:44 -0700209def get_device(ads, **kwargs):
210 """Finds a unique AndroidDevice instance from a list that has specific
211 attributes of certain values.
212
213 Example:
214 get_device(android_devices, label="foo", phone_number="1234567890")
215 get_device(android_devices, model="angler")
216
217 Args:
218 ads: A list of AndroidDevice instances.
219 kwargs: keyword arguments used to filter AndroidDevice instances.
220
221 Returns:
222 The target AndroidDevice instance.
223
224 Raises:
225 AndroidDeviceError is raised if none or more than one device is
226 matched.
227 """
Ang Lie2139f12016-05-12 17:39:06 -0700228
Ang Li93420002016-05-10 19:11:44 -0700229 def _get_device_filter(ad):
230 for k, v in kwargs.items():
231 if not hasattr(ad, k):
232 return False
233 elif getattr(ad, k) != v:
234 return False
235 return True
Ang Lie2139f12016-05-12 17:39:06 -0700236
Ang Li93420002016-05-10 19:11:44 -0700237 filtered = filter_devices(ads, _get_device_filter)
238 if not filtered:
239 raise AndroidDeviceError(("Could not find a target device that matches"
240 " condition: %s.") % kwargs)
241 elif len(filtered) == 1:
242 return filtered[0]
243 else:
244 serials = [ad.serial for ad in filtered]
245 raise AndroidDeviceError("More than one device matched: %s" % serials)
246
Ang Lie2139f12016-05-12 17:39:06 -0700247
Ang Li93420002016-05-10 19:11:44 -0700248def takeBugReports(ads, test_name, begin_time):
249 """Takes bug reports on a list of android devices.
250
251 If you want to take a bug report, call this function with a list of
252 android_device objects in on_fail. But reports will be taken on all the
253 devices in the list concurrently. Bug report takes a relative long
254 time to take, so use this cautiously.
255
256 Args:
257 ads: A list of AndroidDevice instances.
258 test_name: Name of the test case that triggered this bug report.
259 begin_time: Logline format timestamp taken when the test started.
260 """
261 begin_time = vts_logger.normalizeLogLineTimestamp(begin_time)
Ang Lie2139f12016-05-12 17:39:06 -0700262
Ang Li93420002016-05-10 19:11:44 -0700263 def take_br(test_name, begin_time, ad):
264 ad.takeBugReport(test_name, begin_time)
Ang Lie2139f12016-05-12 17:39:06 -0700265
Ang Li93420002016-05-10 19:11:44 -0700266 args = [(test_name, begin_time, ad) for ad in ads]
267 utils.concurrent_exec(take_br, args)
268
Ang Lie2139f12016-05-12 17:39:06 -0700269
Ang Li93420002016-05-10 19:11:44 -0700270class AndroidDevice:
271 """Class representing an android device.
272
273 Each object of this class represents one Android device in ACTS, including
274 handles to adb, fastboot, and sl4a clients. In addition to direct adb
275 commands, this object also uses adb port forwarding to talk to the Android
276 device.
277
278 Attributes:
279 serial: A string that's the serial number of the Androi device.
280 h_port: An integer that's the port number for adb port forwarding used
281 on the computer the Android device is connected
282 d_port: An integer that's the port number used on the Android device
283 for adb port forwarding.
284 log: A LoggerProxy object used for the class's internal logging.
285 log_path: A string that is the path where all logs collected on this
286 android device should be stored.
287 adb_logcat_process: A process that collects the adb logcat.
288 adb_logcat_file_path: A string that's the full path to the adb logcat
289 file collected, if any.
290 adb: An AdbProxy object used for interacting with the device via adb.
291 fastboot: A FastbootProxy object used for interacting with the device
292 via fastboot.
293 """
294
Ang Lie2139f12016-05-12 17:39:06 -0700295 def __init__(self,
296 serial="",
297 host_port=None,
298 device_port=8080,
Ang Li93420002016-05-10 19:11:44 -0700299 logger=None):
300 self.serial = serial
301 self.h_port = host_port
302 self.d_port = device_port
303 self.log = logging.getLogger()
304 lp = self.log.log_path
305 self.log_path = os.path.join(lp, "AndroidDevice%s" % serial)
306 self._droid_sessions = {}
307 self._event_dispatchers = {}
308 self.adb_logcat_process = None
309 self.adb_logcat_file_path = None
310 self.adb = adb.AdbProxy(serial)
311 self.fastboot = fastboot.FastbootProxy(serial)
312 if not self.isBootloaderMode:
313 self.rootAdb()
314
315 def __del__(self):
316 if self.h_port:
317 self.adb.forward("--remove tcp:%d" % self.h_port)
318 if self.adb_logcat_process:
319 self.stopAdbLogcat()
320
321 @property
322 def isBootloaderMode(self):
323 """True if the device is in bootloader mode.
324 """
325 return self.serial in list_fastboot_devices()
326
327 @property
328 def isAdbRoot(self):
329 """True if adb is running as root for this device.
330 """
331 return "root" in self.adb.shell("id -u").decode("utf-8")
332
333 @property
334 def model(self):
335 """The Android code name for the device.
336 """
337 # If device is in bootloader mode, get mode name from fastboot.
338 if self.isBootloaderMode:
339 out = self.fastboot.getvar("product").strip()
340 # "out" is never empty because of the "total time" message fastboot
341 # writes to stderr.
342 lines = out.decode("utf-8").split('\n', 1)
343 if lines:
344 tokens = lines[0].split(' ')
345 if len(tokens) > 1:
346 return tokens[1].lower()
347 return None
348 out = self.adb.shell('getprop | grep ro.build.product')
349 model = out.decode("utf-8").strip().split('[')[-1][:-1].lower()
350 if model == "sprout":
351 return model
352 else:
353 out = self.adb.shell('getprop | grep ro.product.name')
354 model = out.decode("utf-8").strip().split('[')[-1][:-1].lower()
355 return model
356
357 @property
358 def droid(self):
359 """The first sl4a session initiated on this device. None if there isn't
360 one.
361 """
362 try:
363 session_id = sorted(self._droid_sessions)[0]
364 return self._droid_sessions[session_id][0]
365 except IndexError:
366 return None
367
368 @property
369 def ed(self):
370 """The first event_dispatcher instance created on this device. None if
371 there isn't one.
372 """
373 try:
374 session_id = sorted(self._event_dispatchers)[0]
375 return self._event_dispatchers[session_id]
376 except IndexError:
377 return None
378
379 @property
380 def droids(self):
381 """A list of the active sl4a sessions on this device.
382
383 If multiple connections exist for the same session, only one connection
384 is listed.
385 """
386 keys = sorted(self._droid_sessions)
387 results = []
388 for k in keys:
389 results.append(self._droid_sessions[k][0])
390 return results
391
392 @property
393 def eds(self):
394 """A list of the event_dispatcher objects on this device.
395
396 The indexing of the list matches that of the droids property.
397 """
398 keys = sorted(self._event_dispatchers)
399 results = []
400 for k in keys:
401 results.append(self._event_dispatchers[k])
402 return results
403
404 @property
405 def isAdbLogcatOn(self):
406 """Whether there is an ongoing adb logcat collection.
407 """
408 if self.adb_logcat_process:
409 return True
410 return False
411
412 def loadConfig(self, config):
413 """Add attributes to the AndroidDevice object based on json config.
414
415 Args:
416 config: A dictionary representing the configs.
417
418 Raises:
419 AndroidDeviceError is raised if the config is trying to overwrite
420 an existing attribute.
421 """
422 for k, v in config.items():
423 if hasattr(self, k):
424 raise AndroidDeviceError(("Attempting to set existing "
Ang Lie2139f12016-05-12 17:39:06 -0700425 "attribute %s on %s") %
426 (k, self.serial))
Ang Li93420002016-05-10 19:11:44 -0700427 setattr(self, k, v)
428
429 def rootAdb(self):
430 """Change adb to root mode for this device.
431 """
432 if not self.isAdbRoot:
433 self.adb.root()
434 self.adb.wait_for_device()
435 self.adb.remount()
436 self.adb.wait_for_device()
437
438 def getSl4aClient(self, handle_event=True):
439 """Create an sl4a connection to the device.
440
441 Return the connection handler 'droid'. By default, another connection
442 on the same session is made for EventDispatcher, and the dispatcher is
443 returned to the caller as well.
444 If sl4a server is not started on the device, try to start it.
445
446 Args:
447 handle_event: True if this droid session will need to handle
448 events.
449
450 Returns:
451 droid: Android object used to communicate with sl4a on the android
452 device.
453 ed: An optional EventDispatcher to organize events for this droid.
454
455 Examples:
456 Don't need event handling:
457 >>> ad = AndroidDevice()
458 >>> droid = ad.getSl4aClient(False)
459
460 Need event handling:
461 >>> ad = AndroidDevice()
462 >>> droid, ed = ad.getSl4aClient()
463 """
464 if not self.h_port or not adb.is_port_available(self.h_port):
465 self.h_port = adb.get_available_host_port()
466 self.adb.tcp_forward(self.h_port, self.d_port)
467 try:
468 droid = self.start_new_session()
469 except:
470 self.adb.start_sl4a()
471 droid = self.start_new_session()
472 if handle_event:
473 ed = self.getSl4aEventDispatcher(droid)
474 return droid, ed
475 return droid
476
477 def getSl4aEventDispatcher(self, droid):
478 """Return an EventDispatcher for an sl4a session
479
480 Args:
481 droid: Session to create EventDispatcher for.
482
483 Returns:
484 ed: An EventDispatcher for specified session.
485 """
486 ed_key = self.serial + str(droid.uid)
487 if ed_key in self._event_dispatchers:
488 if self._event_dispatchers[ed_key] is None:
489 raise AndroidDeviceError("EventDispatcher Key Empty")
490 self.log.debug("Returning existing key %s for event dispatcher!",
491 ed_key)
492 return self._event_dispatchers[ed_key]
493 event_droid = self.add_new_connection_to_session(droid.uid)
494 ed = event_dispatcher.EventDispatcher(event_droid)
495 self._event_dispatchers[ed_key] = ed
496 return ed
497
498 def _is_timestamp_in_range(self, target, begin_time, end_time):
499 low = vts_logger.logLineTimestampComparator(begin_time, target) <= 0
500 high = vts_logger.logLineTimestampComparator(end_time, target) >= 0
501 return low and high
502
503 def takeAdbLogExcerpt(self, tag, begin_time):
504 """Takes an excerpt of the adb logcat log from a certain time point to
505 current time.
506
507 Args:
508 tag: An identifier of the time period, usualy the name of a test.
509 begin_time: Logline format timestamp of the beginning of the time
510 period.
511 """
512 if not self.adb_logcat_file_path:
Ang Lie2139f12016-05-12 17:39:06 -0700513 raise AndroidDeviceError(
514 ("Attempting to cat adb log when none has"
515 " been collected on Android device %s.") % self.serial)
Ang Li93420002016-05-10 19:11:44 -0700516 end_time = vts_logger.getLogLineTimestamp()
517 self.log.debug("Extracting adb log from logcat.")
518 adb_excerpt_path = os.path.join(self.log_path, "AdbLogExcerpts")
519 utils.create_dir(adb_excerpt_path)
520 f_name = os.path.basename(self.adb_logcat_file_path)
521 out_name = f_name.replace("adblog,", "").replace(".txt", "")
522 out_name = ",{},{}.txt".format(begin_time, out_name)
523 tag_len = utils.MAX_FILENAME_LEN - len(out_name)
524 tag = tag[:tag_len]
525 out_name = tag + out_name
526 full_adblog_path = os.path.join(adb_excerpt_path, out_name)
527 with open(full_adblog_path, 'w', encoding='utf-8') as out:
528 in_file = self.adb_logcat_file_path
529 with open(in_file, 'r', encoding='utf-8', errors='replace') as f:
530 in_range = False
531 while True:
532 line = None
533 try:
534 line = f.readline()
535 if not line:
536 break
537 except:
538 continue
539 line_time = line[:vts_logger.log_line_timestamp_len]
540 if not vts_logger.isValidLogLineTimestamp(line_time):
541 continue
542 if self._is_timestamp_in_range(line_time, begin_time,
Ang Lie2139f12016-05-12 17:39:06 -0700543 end_time):
Ang Li93420002016-05-10 19:11:44 -0700544 in_range = True
545 if not line.endswith('\n'):
546 line += '\n'
547 out.write(line)
548 else:
549 if in_range:
550 break
551
552 def startAdbLogcat(self):
553 """Starts a standing adb logcat collection in separate subprocesses and
554 save the logcat in a file.
555 """
556 if self.isAdbLogcatOn:
557 raise AndroidDeviceError(("Android device {} already has an adb "
Ang Lie2139f12016-05-12 17:39:06 -0700558 "logcat thread going on. Cannot start "
559 "another one.").format(self.serial))
Ang Li93420002016-05-10 19:11:44 -0700560 # Disable adb log spam filter.
561 self.adb.shell("logpersist.start")
562 f_name = "adblog,{},{}.txt".format(self.model, self.serial)
563 utils.create_dir(self.log_path)
564 logcat_file_path = os.path.join(self.log_path, f_name)
565 try:
566 extra_params = self.adb_logcat_param
567 except AttributeError:
568 extra_params = "-b all"
569 cmd = "adb -s {} logcat -v threadtime {} >> {}".format(
570 self.serial, extra_params, logcat_file_path)
571 self.adb_logcat_process = utils.start_standing_subprocess(cmd)
572 self.adb_logcat_file_path = logcat_file_path
573
574 def stopAdbLogcat(self):
575 """Stops the adb logcat collection subprocess.
576 """
577 if not self.isAdbLogcatOn:
578 raise AndroidDeviceError(("Android device {} does not have an "
Ang Lie2139f12016-05-12 17:39:06 -0700579 "ongoing adb logcat collection.").format(
580 self.serial))
Ang Li93420002016-05-10 19:11:44 -0700581 utils.stop_standing_subprocess(self.adb_logcat_process)
582 self.adb_logcat_process = None
583
584 def takeBugReport(self, test_name, begin_time):
585 """Takes a bug report on the device and stores it in a file.
586
587 Args:
588 test_name: Name of the test case that triggered this bug report.
589 begin_time: Logline format timestamp taken when the test started.
590 """
591 br_path = os.path.join(self.log_path, "BugReports")
592 utils.create_dir(br_path)
593 base_name = ",{},{}.txt".format(begin_time, self.serial)
594 test_name_len = utils.MAX_FILENAME_LEN - len(base_name)
595 out_name = test_name[:test_name_len] + base_name
596 full_out_path = os.path.join(br_path, out_name.replace(' ', '\ '))
597 self.log.info("Taking bugreport for %s on %s", test_name, self.serial)
598 self.adb.bugreport(" > {}".format(full_out_path))
599 self.log.info("Bugreport for %s taken at %s", test_name, full_out_path)
600
601 def start_new_session(self):
602 """Start a new session in sl4a.
603
604 Also caches the droid in a dict with its uid being the key.
605
606 Returns:
607 An Android object used to communicate with sl4a on the android
608 device.
609
610 Raises:
611 SL4AException: Something is wrong with sl4a and it returned an
612 existing uid to a new session.
613 """
614 droid = android.Android(port=self.h_port)
615 if droid.uid in self._droid_sessions:
616 raise android.SL4AException(("SL4A returned an existing uid for a "
Ang Lie2139f12016-05-12 17:39:06 -0700617 "new session. Abort."))
Ang Li93420002016-05-10 19:11:44 -0700618 self._droid_sessions[droid.uid] = [droid]
619 return droid
620
621 def add_new_connection_to_session(self, session_id):
622 """Create a new connection to an existing sl4a session.
623
624 Args:
625 session_id: UID of the sl4a session to add connection to.
626
627 Returns:
628 An Android object used to communicate with sl4a on the android
629 device.
630
631 Raises:
632 AndroidDeviceError: Raised if the session it's trying to connect to
633 does not exist.
634 """
635 if session_id not in self._droid_sessions:
636 raise AndroidDeviceError("Session %d doesn't exist." % session_id)
Ang Lie2139f12016-05-12 17:39:06 -0700637 droid = android.Android(cmd='continue',
638 uid=session_id,
639 port=self.h_port)
Ang Li93420002016-05-10 19:11:44 -0700640 return droid
641
642 def closeOneSl4aSession(self, session_id):
643 """Terminate a session in sl4a.
644
645 Send terminate signal to sl4a server; stop dispatcher associated with
646 the session. Clear corresponding droids and dispatchers from cache.
647
648 Args:
649 session_id: UID of the sl4a session to terminate.
650 """
651 if self._droid_sessions and (session_id in self._droid_sessions):
652 for droid in self._droid_sessions[session_id]:
653 droid.closeSl4aSession()
654 droid.close()
655 del self._droid_sessions[session_id]
656 ed_key = self.serial + str(session_id)
657 if ed_key in self._event_dispatchers:
658 self._event_dispatchers[ed_key].clean_up()
659 del self._event_dispatchers[ed_key]
660
661 def closeAllSl4aSession(self):
662 """Terminate all sl4a sessions on the AndroidDevice instance.
663
664 Terminate all sessions and clear caches.
665 """
666 if self._droid_sessions:
667 session_ids = list(self._droid_sessions.keys())
668 for session_id in session_ids:
669 try:
670 self.closeOneSl4aSession(session_id)
671 except:
672 msg = "Failed to terminate session %d." % session_id
673 self.log.exception(msg)
674 self.log.error(traceback.format_exc())
675 if self.h_port:
676 self.adb.forward("--remove tcp:%d" % self.h_port)
677 self.h_port = None
678
679 def runIperfClient(self, server_host, extra_args=""):
680 """Start iperf client on the device.
681
682 Return status as true if iperf client start successfully.
683 And data flow information as results.
684
685 Args:
686 server_host: Address of the iperf server.
687 extra_args: A string representing extra arguments for iperf client,
688 e.g. "-i 1 -t 30".
689
690 Returns:
691 status: true if iperf client start successfully.
692 results: results have data flow information
693 """
694 out = self.adb.shell("iperf3 -c {} {}".format(server_host, extra_args))
Ang Lie2139f12016-05-12 17:39:06 -0700695 clean_out = str(out, 'utf-8').strip().split('\n')
Ang Li93420002016-05-10 19:11:44 -0700696 if "error" in clean_out[0].lower():
697 return False, clean_out
698 return True, clean_out
699
700 @utils.timeout(15 * 60)
701 def waitForBootCompletion(self):
702 """Waits for Android framework to broadcast ACTION_BOOT_COMPLETED.
703
704 This function times out after 15 minutes.
705 """
706 self.adb.wait_for_device()
707 while True:
708 try:
709 out = self.adb.shell("getprop sys.boot_completed")
710 completed = out.decode('utf-8').strip()
711 if completed == '1':
712 return
713 except adb.AdbError:
714 # adb shell calls may fail during certain period of booting
715 # process, which is normal. Ignoring these errors.
716 pass
717 time.sleep(5)
718
719 def reboot(self):
720 """Reboots the device.
721
722 Terminate all sl4a sessions, reboot the device, wait for device to
723 complete booting, and restart an sl4a session.
724
725 This is a blocking method.
726
727 This is probably going to print some error messages in console. Only
728 use if there's no other option.
729
730 Example:
731 droid, ed = ad.reboot()
732
733 Returns:
734 An sl4a session with an event_dispatcher.
735
736 Raises:
737 AndroidDeviceError is raised if waiting for completion timed
738 out.
739 """
740 if self.isBootloaderMode:
741 self.fastboot.reboot()
742 return
743 has_adb_log = self.isAdbLogcatOn
744 if has_adb_log:
745 self.stopAdbLogcat()
746 self.closeAllSl4aSession()
747 self.adb.reboot()
748 self.waitForBootCompletion()
749 self.rootAdb()
750 droid, ed = self.getSl4aClient()
751 ed.start()
752 if has_adb_log:
753 self.startAdbLogcat()
754 return droid, ed