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