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