xref: /aosp_15_r20/tools/asuite/atest/usb_speed_detect.py (revision c2e18aaa1096c836b086f94603d04f4eb9cf37f5)
1*c2e18aaaSAndroid Build Coastguard Worker# Copyright 2024, The Android Open Source Project
2*c2e18aaaSAndroid Build Coastguard Worker#
3*c2e18aaaSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
4*c2e18aaaSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
5*c2e18aaaSAndroid Build Coastguard Worker# You may obtain a copy of the License at
6*c2e18aaaSAndroid Build Coastguard Worker#
7*c2e18aaaSAndroid Build Coastguard Worker#     http://www.apache.org/licenses/LICENSE-2.0
8*c2e18aaaSAndroid Build Coastguard Worker#
9*c2e18aaaSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
10*c2e18aaaSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
11*c2e18aaaSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*c2e18aaaSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
13*c2e18aaaSAndroid Build Coastguard Worker# limitations under the License.
14*c2e18aaaSAndroid Build Coastguard Worker
15*c2e18aaaSAndroid Build Coastguard Worker"""Module that detects device attributes and USB speed using adb commands."""
16*c2e18aaaSAndroid Build Coastguard Worker
17*c2e18aaaSAndroid Build Coastguard Workerimport enum
18*c2e18aaaSAndroid Build Coastguard Workerimport logging
19*c2e18aaaSAndroid Build Coastguard Workerimport subprocess
20*c2e18aaaSAndroid Build Coastguard Workerfrom typing import NamedTuple
21*c2e18aaaSAndroid Build Coastguard Workerfrom atest import atest_utils
22*c2e18aaaSAndroid Build Coastguard Workerfrom atest import constants
23*c2e18aaaSAndroid Build Coastguard Worker
24*c2e18aaaSAndroid Build Coastguard Worker
25*c2e18aaaSAndroid Build Coastguard Worker@enum.unique
26*c2e18aaaSAndroid Build Coastguard Workerclass UsbAttributeName(enum.Enum):
27*c2e18aaaSAndroid Build Coastguard Worker  NEGOTIATED_SPEED = 'current_speed'
28*c2e18aaaSAndroid Build Coastguard Worker  MAXIMUM_SPEED = 'maximum_speed'
29*c2e18aaaSAndroid Build Coastguard Worker
30*c2e18aaaSAndroid Build Coastguard Worker
31*c2e18aaaSAndroid Build Coastguard Workerclass DeviceIds(NamedTuple):
32*c2e18aaaSAndroid Build Coastguard Worker  manufacturer: str
33*c2e18aaaSAndroid Build Coastguard Worker  model: str
34*c2e18aaaSAndroid Build Coastguard Worker  name: str
35*c2e18aaaSAndroid Build Coastguard Worker  serial: str
36*c2e18aaaSAndroid Build Coastguard Worker  address: str
37*c2e18aaaSAndroid Build Coastguard Worker
38*c2e18aaaSAndroid Build Coastguard Worker
39*c2e18aaaSAndroid Build Coastguard Workerdef verify_and_print_usb_speed_warning(
40*c2e18aaaSAndroid Build Coastguard Worker    device_ids: DeviceIds, negotiated_speed: int, max_speed: int
41*c2e18aaaSAndroid Build Coastguard Worker) -> bool:
42*c2e18aaaSAndroid Build Coastguard Worker  """Checks whether the connection speed is optimal for the given device.
43*c2e18aaaSAndroid Build Coastguard Worker
44*c2e18aaaSAndroid Build Coastguard Worker  Args:
45*c2e18aaaSAndroid Build Coastguard Worker      device_ids: Identifiers allowing a user to recognize the device the usb
46*c2e18aaaSAndroid Build Coastguard Worker        speed warning is related to.
47*c2e18aaaSAndroid Build Coastguard Worker      negotiated_speed: The current speed of the device.
48*c2e18aaaSAndroid Build Coastguard Worker      max_speed: The maximum speed that the given device is capable of.
49*c2e18aaaSAndroid Build Coastguard Worker
50*c2e18aaaSAndroid Build Coastguard Worker  Returns:
51*c2e18aaaSAndroid Build Coastguard Worker      True if the warning was printed, False otherwise.
52*c2e18aaaSAndroid Build Coastguard Worker  """
53*c2e18aaaSAndroid Build Coastguard Worker  # If a USB-2 is used with a USB-3 capable device, the speed will be
54*c2e18aaaSAndroid Build Coastguard Worker  # downgraded to 480 Mbps and never 12 Mbps, so this is the only case we
55*c2e18aaaSAndroid Build Coastguard Worker  # check.
56*c2e18aaaSAndroid Build Coastguard Worker  if negotiated_speed == 480 and negotiated_speed < max_speed:
57*c2e18aaaSAndroid Build Coastguard Worker    _print_usb_speed_warning(device_ids, negotiated_speed, max_speed)
58*c2e18aaaSAndroid Build Coastguard Worker    return True
59*c2e18aaaSAndroid Build Coastguard Worker  return False
60*c2e18aaaSAndroid Build Coastguard Worker
61*c2e18aaaSAndroid Build Coastguard Worker
62*c2e18aaaSAndroid Build Coastguard Workerdef _print_usb_speed_warning(
63*c2e18aaaSAndroid Build Coastguard Worker    device_ids: DeviceIds, negotiated_speed: int, max_speed: int
64*c2e18aaaSAndroid Build Coastguard Worker):
65*c2e18aaaSAndroid Build Coastguard Worker  """Prints a warning about the device's operating speed if it's suboptimal.
66*c2e18aaaSAndroid Build Coastguard Worker
67*c2e18aaaSAndroid Build Coastguard Worker  Args:
68*c2e18aaaSAndroid Build Coastguard Worker    device_ids: Identifiers allowing a user to recognize the device the usb
69*c2e18aaaSAndroid Build Coastguard Worker      speed warning is related to.
70*c2e18aaaSAndroid Build Coastguard Worker    negotiated_speed: The negotiated speed (in Mbits per seconds) the device is
71*c2e18aaaSAndroid Build Coastguard Worker      operating at.
72*c2e18aaaSAndroid Build Coastguard Worker    max_speed: The maximum speed (in Mbits per seconds) of which the device is
73*c2e18aaaSAndroid Build Coastguard Worker      capable.
74*c2e18aaaSAndroid Build Coastguard Worker  """
75*c2e18aaaSAndroid Build Coastguard Worker  atest_utils.colorful_print(
76*c2e18aaaSAndroid Build Coastguard Worker      f'Warning: The {device_ids.manufacturer} {device_ids.model} device ('
77*c2e18aaaSAndroid Build Coastguard Worker      f'{device_ids.name}) with address {device_ids.address} and serial '
78*c2e18aaaSAndroid Build Coastguard Worker      f'{device_ids.serial} is using '
79*c2e18aaaSAndroid Build Coastguard Worker      f'{_speed_to_string(negotiated_speed)} while '
80*c2e18aaaSAndroid Build Coastguard Worker      f'{_speed_to_string(max_speed)} capable. Check the USB cables/hubs.',
81*c2e18aaaSAndroid Build Coastguard Worker      constants.MAGENTA,
82*c2e18aaaSAndroid Build Coastguard Worker  )
83*c2e18aaaSAndroid Build Coastguard Worker
84*c2e18aaaSAndroid Build Coastguard Worker
85*c2e18aaaSAndroid Build Coastguard Workerdef _speed_to_string(speed: int) -> str:
86*c2e18aaaSAndroid Build Coastguard Worker  """Converts a speed in Mbps to a string."""
87*c2e18aaaSAndroid Build Coastguard Worker  return {
88*c2e18aaaSAndroid Build Coastguard Worker      480: 'USB-2 (480 Mbps)',
89*c2e18aaaSAndroid Build Coastguard Worker      5000: 'USB-3.0 (5,000 Mbps)',
90*c2e18aaaSAndroid Build Coastguard Worker      10000: 'USB-3.1 (10,000 Mbps)',
91*c2e18aaaSAndroid Build Coastguard Worker      20000: 'USB-3.2 (20,000 Mbps)',
92*c2e18aaaSAndroid Build Coastguard Worker      40000: 'USB-4.0 (40,000 Mbps)',
93*c2e18aaaSAndroid Build Coastguard Worker  }.get(speed, f'{speed:,} Mbps')
94*c2e18aaaSAndroid Build Coastguard Worker
95*c2e18aaaSAndroid Build Coastguard Worker
96*c2e18aaaSAndroid Build Coastguard Workerdef _string_to_speed(speed_str: str) -> int:
97*c2e18aaaSAndroid Build Coastguard Worker  return {
98*c2e18aaaSAndroid Build Coastguard Worker      'UNKNOWN': 0,
99*c2e18aaaSAndroid Build Coastguard Worker      'high-speed': 480,
100*c2e18aaaSAndroid Build Coastguard Worker      'super-speed': 5000,
101*c2e18aaaSAndroid Build Coastguard Worker      'super-speed-plus': 10000,
102*c2e18aaaSAndroid Build Coastguard Worker  }.get(speed_str, 0)
103*c2e18aaaSAndroid Build Coastguard Worker
104*c2e18aaaSAndroid Build Coastguard Worker
105*c2e18aaaSAndroid Build Coastguard Workerdef get_udc_driver_usb_device_dir_name() -> str:
106*c2e18aaaSAndroid Build Coastguard Worker  """Reads the directory where the usb devices attributes are stored.
107*c2e18aaaSAndroid Build Coastguard Worker
108*c2e18aaaSAndroid Build Coastguard Worker  Returns:
109*c2e18aaaSAndroid Build Coastguard Worker      A string corresponding to the directory name.
110*c2e18aaaSAndroid Build Coastguard Worker  """
111*c2e18aaaSAndroid Build Coastguard Worker  return _adb_read_file('/config/usb_gadget/g1/UDC')
112*c2e18aaaSAndroid Build Coastguard Worker
113*c2e18aaaSAndroid Build Coastguard Worker
114*c2e18aaaSAndroid Build Coastguard Workerdef get_udc_driver_usb_device_attribute_speed_value(
115*c2e18aaaSAndroid Build Coastguard Worker    speed_dir_name: str,
116*c2e18aaaSAndroid Build Coastguard Worker    attr_name: UsbAttributeName,
117*c2e18aaaSAndroid Build Coastguard Worker) -> int:
118*c2e18aaaSAndroid Build Coastguard Worker  """Reads the usb speed string from the device and returns the numeric speed.
119*c2e18aaaSAndroid Build Coastguard Worker
120*c2e18aaaSAndroid Build Coastguard Worker  Args:
121*c2e18aaaSAndroid Build Coastguard Worker      speed_dir_name: name of the directory where the usb driver attributes are
122*c2e18aaaSAndroid Build Coastguard Worker        located.
123*c2e18aaaSAndroid Build Coastguard Worker      attr_name: The attribute to read from the device.
124*c2e18aaaSAndroid Build Coastguard Worker
125*c2e18aaaSAndroid Build Coastguard Worker  Returns:
126*c2e18aaaSAndroid Build Coastguard Worker      An int corresponding to the numeric speed value converted from the udc
127*c2e18aaaSAndroid Build Coastguard Worker      driver attribute value. 0 is returned if adb is unable to read the value.
128*c2e18aaaSAndroid Build Coastguard Worker  """
129*c2e18aaaSAndroid Build Coastguard Worker  speed_reading = _adb_read_file(
130*c2e18aaaSAndroid Build Coastguard Worker      '/sys/class/udc/' + speed_dir_name + '/' + attr_name.value
131*c2e18aaaSAndroid Build Coastguard Worker  )
132*c2e18aaaSAndroid Build Coastguard Worker  return _string_to_speed(speed_reading)
133*c2e18aaaSAndroid Build Coastguard Worker
134*c2e18aaaSAndroid Build Coastguard Worker
135*c2e18aaaSAndroid Build Coastguard Workerdef _adb_read_file(file_path: str) -> str:
136*c2e18aaaSAndroid Build Coastguard Worker  cmd = [
137*c2e18aaaSAndroid Build Coastguard Worker      'adb',
138*c2e18aaaSAndroid Build Coastguard Worker      'shell',
139*c2e18aaaSAndroid Build Coastguard Worker      'su',
140*c2e18aaaSAndroid Build Coastguard Worker      '0',
141*c2e18aaaSAndroid Build Coastguard Worker      f'cat {file_path}',
142*c2e18aaaSAndroid Build Coastguard Worker  ]
143*c2e18aaaSAndroid Build Coastguard Worker  try:
144*c2e18aaaSAndroid Build Coastguard Worker    logging.debug('Running command: %s', cmd)
145*c2e18aaaSAndroid Build Coastguard Worker    result = subprocess.check_output(
146*c2e18aaaSAndroid Build Coastguard Worker        cmd,
147*c2e18aaaSAndroid Build Coastguard Worker        encoding='utf-8',
148*c2e18aaaSAndroid Build Coastguard Worker        stderr=subprocess.STDOUT,
149*c2e18aaaSAndroid Build Coastguard Worker    )
150*c2e18aaaSAndroid Build Coastguard Worker    return result.strip()
151*c2e18aaaSAndroid Build Coastguard Worker  except subprocess.CalledProcessError as cpe:
152*c2e18aaaSAndroid Build Coastguard Worker    logging.debug(
153*c2e18aaaSAndroid Build Coastguard Worker        f'Cannot read directory; USB speed will not be read. Error: %s', cpe
154*c2e18aaaSAndroid Build Coastguard Worker    )
155*c2e18aaaSAndroid Build Coastguard Worker  except OSError as ose:
156*c2e18aaaSAndroid Build Coastguard Worker    logging.debug(f'Cannot read usb speed from the device. Error: %s', ose)
157*c2e18aaaSAndroid Build Coastguard Worker  return ''
158*c2e18aaaSAndroid Build Coastguard Worker
159*c2e18aaaSAndroid Build Coastguard Worker
160*c2e18aaaSAndroid Build Coastguard Workerdef get_adb_device_identifiers() -> DeviceIds | None:
161*c2e18aaaSAndroid Build Coastguard Worker  """Fetch the user-facing device identifiers."""
162*c2e18aaaSAndroid Build Coastguard Worker  if not atest_utils.has_command('adb'):
163*c2e18aaaSAndroid Build Coastguard Worker    return None
164*c2e18aaaSAndroid Build Coastguard Worker
165*c2e18aaaSAndroid Build Coastguard Worker  device_serial = _adb_run_cmd(['adb', 'shell', 'getprop', 'ro.serialno'])
166*c2e18aaaSAndroid Build Coastguard Worker  if not device_serial:
167*c2e18aaaSAndroid Build Coastguard Worker    return None
168*c2e18aaaSAndroid Build Coastguard Worker
169*c2e18aaaSAndroid Build Coastguard Worker  device_address_resp = _adb_run_cmd(['adb', 'devices'])
170*c2e18aaaSAndroid Build Coastguard Worker  try:
171*c2e18aaaSAndroid Build Coastguard Worker    device_addresses = device_address_resp.splitlines()
172*c2e18aaaSAndroid Build Coastguard Worker    for line in device_addresses:
173*c2e18aaaSAndroid Build Coastguard Worker      if 'device' in line:
174*c2e18aaaSAndroid Build Coastguard Worker        device_address = line.split()[0].strip()
175*c2e18aaaSAndroid Build Coastguard Worker  except IndexError:
176*c2e18aaaSAndroid Build Coastguard Worker    logging.debug('No devices are connected. USB speed will not be read.')
177*c2e18aaaSAndroid Build Coastguard Worker    return None
178*c2e18aaaSAndroid Build Coastguard Worker
179*c2e18aaaSAndroid Build Coastguard Worker  device_manufacturer = _adb_run_cmd(
180*c2e18aaaSAndroid Build Coastguard Worker      ['adb', 'shell', 'getprop', 'ro.product.manufacturer']
181*c2e18aaaSAndroid Build Coastguard Worker  )
182*c2e18aaaSAndroid Build Coastguard Worker  device_model = _adb_run_cmd(['adb', 'shell', 'getprop', 'ro.product.model'])
183*c2e18aaaSAndroid Build Coastguard Worker  device_name = _adb_run_cmd(['adb', 'shell', 'getprop', 'ro.product.name'])
184*c2e18aaaSAndroid Build Coastguard Worker
185*c2e18aaaSAndroid Build Coastguard Worker  return DeviceIds(
186*c2e18aaaSAndroid Build Coastguard Worker      manufacturer=device_manufacturer,
187*c2e18aaaSAndroid Build Coastguard Worker      model=device_model,
188*c2e18aaaSAndroid Build Coastguard Worker      name=device_name,
189*c2e18aaaSAndroid Build Coastguard Worker      serial=device_serial,
190*c2e18aaaSAndroid Build Coastguard Worker      address=device_address,
191*c2e18aaaSAndroid Build Coastguard Worker  )
192*c2e18aaaSAndroid Build Coastguard Worker
193*c2e18aaaSAndroid Build Coastguard Worker
194*c2e18aaaSAndroid Build Coastguard Workerdef _adb_run_cmd(cmd: list[str]) -> str:
195*c2e18aaaSAndroid Build Coastguard Worker  try:
196*c2e18aaaSAndroid Build Coastguard Worker    logging.debug(f'Running command: %s.', cmd)
197*c2e18aaaSAndroid Build Coastguard Worker    result = subprocess.check_output(
198*c2e18aaaSAndroid Build Coastguard Worker        cmd,
199*c2e18aaaSAndroid Build Coastguard Worker        encoding='utf-8',
200*c2e18aaaSAndroid Build Coastguard Worker        stderr=subprocess.STDOUT,
201*c2e18aaaSAndroid Build Coastguard Worker    )
202*c2e18aaaSAndroid Build Coastguard Worker    return result.strip() if result else ''
203*c2e18aaaSAndroid Build Coastguard Worker  except subprocess.CalledProcessError:
204*c2e18aaaSAndroid Build Coastguard Worker    logging.debug(
205*c2e18aaaSAndroid Build Coastguard Worker        'Exception raised while running `%s`. USB speed will not be read.', cmd
206*c2e18aaaSAndroid Build Coastguard Worker    )
207*c2e18aaaSAndroid Build Coastguard Worker  except OSError:
208*c2e18aaaSAndroid Build Coastguard Worker    logging.debug('Could not find adb. USB speed will not be read.')
209*c2e18aaaSAndroid Build Coastguard Worker  return ''
210