1# Copyright 2014 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4import logging
5import os
6import re
7import subprocess
8
9from telemetry.core import util
10from telemetry.internal.platform import cros_device
11from telemetry.internal.platform import device
12from telemetry.internal.platform.profiler import monsoon
13
14from devil.android import device_blacklist
15from devil.android import device_errors
16from devil.android import device_utils
17from devil.android.sdk import adb_wrapper
18
19
20class AndroidDevice(device.Device):
21  """ Class represents information for connecting to an android device.
22
23  Attributes:
24    device_id: the device's serial string created by adb to uniquely
25      identify an emulator/device instance. This string can be found by running
26      'adb devices' command
27    enable_performance_mode: when this is set to True, android platform will be
28    set to high performance mode after browser is started.
29  """
30  def __init__(self, device_id, enable_performance_mode=True):
31    super(AndroidDevice, self).__init__(
32        name='Android device %s' % device_id, guid=device_id)
33    self._device_id = device_id
34    self._enable_performance_mode = enable_performance_mode
35
36  @classmethod
37  def GetAllConnectedDevices(cls, blacklist):
38    device_serials = GetDeviceSerials(blacklist)
39    return [cls(s) for s in device_serials]
40
41  @property
42  def device_id(self):
43    return self._device_id
44
45  @property
46  def enable_performance_mode(self):
47    return self._enable_performance_mode
48
49
50def _ListSerialsOfHealthyOnlineDevices(blacklist):
51  return [d.adb.GetDeviceSerial()
52          for d in device_utils.DeviceUtils.HealthyDevices(blacklist)
53          if d.IsOnline()]
54
55
56def GetDeviceSerials(blacklist):
57  """Return the list of device serials of healthy devices.
58
59  If a preferred device has been set with ANDROID_SERIAL, it will be first in
60  the returned list. The arguments specify what devices to include in the list.
61  """
62
63  device_serials = _ListSerialsOfHealthyOnlineDevices(blacklist)
64
65  # The monsoon provides power for the device, so for devices with no
66  # real battery, we need to turn them on after the monsoon enables voltage
67  # output to the device.
68  if not device_serials:
69    try:
70      m = monsoon.Monsoon(wait=False)
71      m.SetUsbPassthrough(1)
72      m.SetVoltage(3.8)
73      m.SetMaxCurrent(8)
74      logging.warn("""
75Monsoon power monitor detected, but no Android devices.
76
77The Monsoon's power output has been enabled. Please now ensure that:
78
79  1. The Monsoon's front and back USB are connected to the host.
80  2. The device is connected to the Monsoon's main and USB channels.
81  3. The device is turned on.
82
83Waiting for device...
84""")
85      util.WaitFor(_ListSerialsOfHealthyOnlineDevices(blacklist), 600)
86      device_serials = _ListSerialsOfHealthyOnlineDevices(blacklist)
87    except IOError:
88      return []
89
90  preferred_device = os.environ.get('ANDROID_SERIAL')
91  if preferred_device in device_serials:
92    logging.warn(
93        'ANDROID_SERIAL is defined. Put %s in the first of the'
94        'discovered devices list.' % preferred_device)
95    device_serials.remove(preferred_device)
96    device_serials.insert(0, preferred_device)
97  return device_serials
98
99
100def GetDevice(finder_options):
101  """Return a Platform instance for the device specified by |finder_options|."""
102  if not CanDiscoverDevices():
103    logging.info(
104        'No adb command found. Will not try searching for Android browsers.')
105    return None
106
107  if finder_options.android_blacklist_file:
108    blacklist = device_blacklist.Blacklist(
109        finder_options.android_blacklist_file)
110  else:
111    blacklist = None
112
113  if (finder_options.device
114      and finder_options.device in GetDeviceSerials(blacklist)):
115    return AndroidDevice(
116        finder_options.device,
117        enable_performance_mode=not finder_options.no_performance_mode)
118
119  devices = AndroidDevice.GetAllConnectedDevices(blacklist)
120  if len(devices) == 0:
121    logging.warn('No android devices found.')
122    return None
123  if len(devices) > 1:
124    logging.warn(
125        'Multiple devices attached. Please specify one of the following:\n' +
126        '\n'.join(['  --device=%s' % d.device_id for d in devices]))
127    return None
128  return devices[0]
129
130
131def _HasValidAdb():
132  """Returns true if adb is present.
133
134  Note that this currently will return True even if the adb that's present
135  cannot run on this system.
136  """
137  if os.name != 'posix' or cros_device.IsRunningOnCrOS():
138    return False
139
140  try:
141    adb_path = adb_wrapper.AdbWrapper.GetAdbPath()
142  except device_errors.NoAdbError:
143    return False
144
145  if os.path.isabs(adb_path) and not os.path.exists(adb_path):
146    return False
147
148  return True
149
150
151def CanDiscoverDevices():
152  """Returns true if devices are discoverable via adb."""
153  if not _HasValidAdb():
154    return False
155
156  try:
157    with open(os.devnull, 'w') as devnull:
158      adb_process = subprocess.Popen(
159          ['adb', 'devices'], stdout=subprocess.PIPE, stderr=subprocess.PIPE,
160          stdin=devnull)
161      stdout = adb_process.communicate()[0]
162    if re.search(re.escape('????????????\tno permissions'), stdout) != None:
163      logging.warn('adb devices gave a permissions error. '
164                   'Consider running adb as root:')
165      logging.warn('  adb kill-server')
166      logging.warn('  sudo `which adb` devices\n\n')
167    return True
168  except OSError:
169    pass
170  try:
171    adb_path = adb_wrapper.AdbWrapper.GetAdbPath()
172    os.environ['PATH'] = os.pathsep.join(
173        [os.path.dirname(adb_path), os.environ['PATH']])
174    device_utils.DeviceUtils.HealthyDevices(None)
175    return True
176  except (device_errors.CommandFailedError, device_errors.CommandTimeoutError,
177          device_errors.NoAdbError, OSError):
178    return False
179
180
181def FindAllAvailableDevices(options):
182  """Returns a list of available devices.
183  """
184  devices = []
185  try:
186    if CanDiscoverDevices():
187      blacklist = None
188      if options.android_blacklist_file:
189        blacklist = device_blacklist.Blacklist(options.android_blacklist_file)
190      devices = AndroidDevice.GetAllConnectedDevices(blacklist)
191  finally:
192    if not devices and _HasValidAdb():
193      try:
194        adb_wrapper.AdbWrapper.KillServer()
195      except device_errors.NoAdbError as e:
196        logging.warning(
197            'adb reported as present, but NoAdbError thrown: %s', str(e))
198
199  return devices
200