1 #!/usr/bin/env python 2 # Copyright 2016 The Chromium Authors. All rights reserved. 3 # Use of this source code is governed by a BSD-style license that can be 4 # found in the LICENSE file. 5 6 """A script to recover devices in a known bad state.""" 7 8 import argparse 9 import logging 10 import os 11 import psutil 12 import signal 13 import sys 14 15 if __name__ == '__main__': 16 sys.path.append( 17 os.path.abspath(os.path.join(os.path.dirname(__file__), 18 '..', '..', '..'))) 19 from devil import devil_env 20 from devil.android import device_blacklist 21 from devil.android import device_errors 22 from devil.android import device_utils 23 from devil.android.tools import device_status 24 from devil.utils import lsusb 25 from devil.utils import reset_usb 26 from devil.utils import run_tests_helper 27 28 29 def KillAllAdb(): 30 def get_all_adb(): 31 for p in psutil.process_iter(): 32 try: 33 if 'adb' in p.name: 34 yield p 35 except (psutil.NoSuchProcess, psutil.AccessDenied): 36 pass 37 38 for sig in [signal.SIGTERM, signal.SIGQUIT, signal.SIGKILL]: 39 for p in get_all_adb(): 40 try: 41 logging.info('kill %d %d (%s [%s])', sig, p.pid, p.name, 42 ' '.join(p.cmdline)) 43 p.send_signal(sig) 44 except (psutil.NoSuchProcess, psutil.AccessDenied): 45 pass 46 for p in get_all_adb(): 47 try: 48 logging.error('Unable to kill %d (%s [%s])', p.pid, p.name, 49 ' '.join(p.cmdline)) 50 except (psutil.NoSuchProcess, psutil.AccessDenied): 51 pass 52 53 54 def RecoverDevice(device, blacklist, should_reboot=lambda device: True): 55 if device_status.IsBlacklisted(device.adb.GetDeviceSerial(), 56 blacklist): 57 logging.debug('%s is blacklisted, skipping recovery.', str(device)) 58 return 59 60 if should_reboot(device): 61 try: 62 device.WaitUntilFullyBooted(retries=0) 63 except (device_errors.CommandTimeoutError, 64 device_errors.CommandFailedError): 65 logging.exception('Failure while waiting for %s. ' 66 'Attempting to recover.', str(device)) 67 try: 68 try: 69 device.Reboot(block=False, timeout=5, retries=0) 70 except device_errors.CommandTimeoutError: 71 logging.warning('Timed out while attempting to reboot %s normally.' 72 'Attempting alternative reboot.', str(device)) 73 # The device drops offline before we can grab the exit code, so 74 # we don't check for status. 75 device.adb.Root() 76 device.adb.Shell('echo b > /proc/sysrq-trigger', expect_status=None, 77 timeout=5, retries=0) 78 except device_errors.CommandFailedError: 79 logging.exception('Failed to reboot %s.', str(device)) 80 if blacklist: 81 blacklist.Extend([device.adb.GetDeviceSerial()], 82 reason='reboot_failure') 83 except device_errors.CommandTimeoutError: 84 logging.exception('Timed out while rebooting %s.', str(device)) 85 if blacklist: 86 blacklist.Extend([device.adb.GetDeviceSerial()], 87 reason='reboot_timeout') 88 89 try: 90 device.WaitUntilFullyBooted(retries=0) 91 except device_errors.CommandFailedError: 92 logging.exception('Failure while waiting for %s.', str(device)) 93 if blacklist: 94 blacklist.Extend([device.adb.GetDeviceSerial()], 95 reason='reboot_failure') 96 except device_errors.CommandTimeoutError: 97 logging.exception('Timed out while waiting for %s.', str(device)) 98 if blacklist: 99 blacklist.Extend([device.adb.GetDeviceSerial()], 100 reason='reboot_timeout') 101 102 103 def RecoverDevices(devices, blacklist): 104 """Attempts to recover any inoperable devices in the provided list. 105 106 Args: 107 devices: The list of devices to attempt to recover. 108 blacklist: The current device blacklist, which will be used then 109 reset. 110 """ 111 112 statuses = device_status.DeviceStatus(devices, blacklist) 113 114 should_restart_usb = set( 115 status['serial'] for status in statuses 116 if (not status['usb_status'] 117 or status['adb_status'] in ('offline', 'missing'))) 118 should_restart_adb = should_restart_usb.union(set( 119 status['serial'] for status in statuses 120 if status['adb_status'] == 'unauthorized')) 121 should_reboot_device = should_restart_adb.union(set( 122 status['serial'] for status in statuses 123 if status['blacklisted'])) 124 125 logging.debug('Should restart USB for:') 126 for d in should_restart_usb: 127 logging.debug(' %s', d) 128 logging.debug('Should restart ADB for:') 129 for d in should_restart_adb: 130 logging.debug(' %s', d) 131 logging.debug('Should reboot:') 132 for d in should_reboot_device: 133 logging.debug(' %s', d) 134 135 if blacklist: 136 blacklist.Reset() 137 138 if should_restart_adb: 139 KillAllAdb() 140 for serial in should_restart_usb: 141 try: 142 reset_usb.reset_android_usb(serial) 143 except IOError: 144 logging.exception('Unable to reset USB for %s.', serial) 145 if blacklist: 146 blacklist.Extend([serial], reason='USB failure') 147 except device_errors.DeviceUnreachableError: 148 logging.exception('Unable to reset USB for %s.', serial) 149 if blacklist: 150 blacklist.Extend([serial], reason='offline') 151 152 device_utils.DeviceUtils.parallel(devices).pMap( 153 RecoverDevice, blacklist, 154 should_reboot=lambda device: device in should_reboot_device) 155 156 157 def main(): 158 parser = argparse.ArgumentParser() 159 parser.add_argument('--adb-path', 160 help='Absolute path to the adb binary to use.') 161 parser.add_argument('--blacklist-file', help='Device blacklist JSON file.') 162 parser.add_argument('--known-devices-file', action='append', default=[], 163 dest='known_devices_files', 164 help='Path to known device lists.') 165 parser.add_argument('-v', '--verbose', action='count', default=1, 166 help='Log more information.') 167 168 args = parser.parse_args() 169 run_tests_helper.SetLogLevel(args.verbose) 170 171 devil_dynamic_config = { 172 'config_type': 'BaseConfig', 173 'dependencies': {}, 174 } 175 176 if args.adb_path: 177 devil_dynamic_config['dependencies'].update({ 178 'adb': { 179 'file_info': { 180 devil_env.GetPlatform(): { 181 'local_paths': [args.adb_path] 182 } 183 } 184 } 185 }) 186 devil_env.config.Initialize(configs=[devil_dynamic_config]) 187 188 blacklist = (device_blacklist.Blacklist(args.blacklist_file) 189 if args.blacklist_file 190 else None) 191 192 expected_devices = device_status.GetExpectedDevices(args.known_devices_files) 193 usb_devices = set(lsusb.get_android_devices()) 194 devices = [device_utils.DeviceUtils(s) 195 for s in expected_devices.union(usb_devices)] 196 197 RecoverDevices(devices, blacklist) 198 199 200 if __name__ == '__main__': 201 sys.exit(main()) 202