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