1#!/usr/bin/env python3 2# coding=utf-8 3 4# 5# Copyright (c) 2021 Huawei Device Co., Ltd. 6# Licensed under the Apache License, Version 2.0 (the "License"); 7# you may not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17# 18 19import threading 20 21from xdevice import UserConfigManager 22from xdevice import ManagerType 23from xdevice import Plugin 24from xdevice import get_plugin 25from xdevice import IDeviceManager 26from xdevice import platform_logger 27from xdevice import ParamError 28from xdevice import ConfigConst 29 30from xdevice_extension._core.environment.device_monitor import \ 31 DeviceStateMonitor 32from xdevice_extension._core.environment.device_state import DeviceEvent 33from xdevice_extension._core.environment.device_state import TestDeviceState 34from xdevice_extension._core.environment.device_state import DeviceState 35from xdevice_extension._core.environment.device_state import \ 36 handle_allocation_event 37from xdevice_extension._core.environment.device_state import \ 38 DeviceAllocationState 39from xdevice_extension._core.environment.dmlib import DeviceConnector 40from xdevice_extension._core.utils import convert_serial 41from xdevice_extension._core.utils import convert_ip 42from xdevice_extension._core.utils import convert_port 43from xdevice_extension._core.constants import DeviceConnectorType 44from xdevice_extension._core.exception import HdcCommandRejectedException 45 46__all__ = ["ManagerDevice"] 47 48LOG = platform_logger("ManagerDevice") 49 50 51@Plugin(type=Plugin.MANAGER, id=ManagerType.device) 52class ManagerDevice(IDeviceManager): 53 """ 54 Class representing device manager 55 managing the set of available devices for testing 56 """ 57 58 def __init__(self): 59 self.devices_list = [] 60 self.global_device_filter = None 61 self.lock_con = threading.Condition() 62 self.list_con = threading.Condition() 63 self.device_connector = None 64 self.managed_device_listener = None 65 self.support_labels = ["phone", "watch", "car", "tv", "tablet", "ivi"] 66 self.support_types = ["device"] 67 68 def init_environment(self, environment="", user_config_file=""): 69 self._start_device_monitor(environment, user_config_file) 70 71 def env_stop(self): 72 self._stop_device_monitor() 73 74 def _start_device_monitor(self, environment="", user_config_file=""): 75 self.managed_device_listener = ManagedDeviceListener(self) 76 device = UserConfigManager( 77 config_file=user_config_file, env=environment).get_device( 78 "environment/device") 79 if device: 80 try: 81 self.device_connector = DeviceConnector(device.get("ip"), 82 device.get("port"), 83 device.get("usb_type")) 84 self.global_device_filter = UserConfigManager( 85 config_file=user_config_file, env=environment).get_sn_list( 86 device.get("sn")) 87 self.device_connector.add_device_change_listener( 88 self.managed_device_listener) 89 self.device_connector.start() 90 except (ParamError, FileNotFoundError) as error: 91 self.env_stop() 92 LOG.debug("start %s error: %s" % ( 93 device.get("usb_type"), error)) 94 if device.get("usb_type") == DeviceConnectorType.hdc: 95 self.device_connector = DeviceConnector( 96 device.get("ip"), device.get("port"), 97 "usb-hdc") 98 self.device_connector.add_device_change_listener( 99 self.managed_device_listener) 100 self.device_connector.start() 101 else: 102 raise ParamError("Manager device is not supported, please " 103 "check config user_config.xml", error_no="00108") 104 105 def _stop_device_monitor(self): 106 self.device_connector.remove_device_change_listener( 107 self.managed_device_listener) 108 self.device_connector.terminate() 109 110 def find(self, idevice): 111 LOG.debug("find: apply list con lock") 112 self.list_con.acquire() 113 try: 114 for device in self.devices_list: 115 if device.device_sn == idevice.device_sn and \ 116 device.device_os_type == idevice.device_os_type: 117 return device 118 finally: 119 LOG.debug("find: release list con lock") 120 self.list_con.release() 121 122 def apply_device(self, device_option, timeout=10): 123 124 LOG.debug("apply_device: apply lock con lock") 125 self.lock_con.acquire() 126 try: 127 device = self.allocate_device_option(device_option) 128 if device: 129 return device 130 LOG.debug("wait for available device founded") 131 self.lock_con.wait(timeout) 132 return self.allocate_device_option(device_option) 133 finally: 134 LOG.debug("apply_device: release lock con lock") 135 self.lock_con.release() 136 137 def allocate_device_option(self, device_option): 138 """ 139 Request a device for testing that meets certain criteria. 140 """ 141 142 LOG.debug("allocate_device_option: apply list con lock") 143 if not self.list_con.acquire(timeout=5): 144 LOG.debug("allocate_device_option: list con wait timeout") 145 return None 146 try: 147 allocated_device = None 148 149 for device in self.devices_list: 150 if device_option.matches(device): 151 self.handle_device_event(device, 152 DeviceEvent.ALLOCATE_REQUEST) 153 LOG.debug("allocate device sn: %s, type: %s" % ( 154 device.__get_serial__(), device.__class__)) 155 return device 156 return allocated_device 157 158 finally: 159 LOG.debug("allocate_device_option: release list con lock") 160 self.list_con.release() 161 162 def release_device(self, device): 163 LOG.debug("release_device: apply list con lock") 164 self.list_con.acquire() 165 try: 166 if device.test_device_state == TestDeviceState.ONLINE: 167 self.handle_device_event(device, DeviceEvent.FREE_AVAILABLE) 168 else: 169 self.handle_device_event(device, DeviceEvent.FREE_UNAVAILABLE) 170 171 device.device_id = None 172 173 LOG.debug("free device sn: %s, type: %s" % ( 174 device.__get_serial__(), device.__class__.__name__)) 175 176 finally: 177 LOG.debug("release_device: release list con lock") 178 self.list_con.release() 179 180 def find_device(self, device_sn, device_os_type): 181 for device in self.devices_list: 182 if device.device_sn == device_sn and \ 183 device.device_os_type == device_os_type: 184 return device 185 186 def append_device_by_sort(self, device_instance): 187 if (not self.global_device_filter or 188 not self.devices_list or 189 device_instance.device_sn not in self.global_device_filter): 190 self.devices_list.append(device_instance) 191 else: 192 device_dict = dict(zip( 193 self.global_device_filter, 194 list(range(1, len(self.global_device_filter)+1)))) 195 for index in range(len(self.devices_list)): 196 if self.devices_list[index].device_sn not in \ 197 self.global_device_filter: 198 self.devices_list.insert(index, device_instance) 199 break 200 if device_dict[device_instance.device_sn] < \ 201 device_dict[self.devices_list[index].device_sn]: 202 self.devices_list.insert(index, device_instance) 203 break 204 else: 205 self.devices_list.append(device_instance) 206 207 def find_or_create(self, idevice): 208 LOG.debug("find_or_create: apply list con lock") 209 self.list_con.acquire() 210 try: 211 device = self.find_device(idevice.device_sn, 212 idevice.device_os_type) 213 if device is None: 214 device = get_plugin( 215 plugin_type=Plugin.DEVICE, 216 plugin_id=idevice.device_os_type)[0] 217 device_instance = device.__class__() 218 device_instance.__set_serial__(idevice.device_sn) 219 device_instance.host = idevice.host 220 device_instance.port = idevice.port 221 device_instance.usb_type = self.device_connector.usb_type 222 LOG.debug("create device(%s) host is %s, " 223 "port is %s, device sn is %s, usb type is %s" % 224 (device_instance, device_instance.host, 225 device_instance.port, device_instance.device_sn, 226 device_instance.usb_type)) 227 device_instance.device_state = DeviceState.get_state( 228 idevice.device_state) 229 device_instance.test_device_state = \ 230 TestDeviceState.get_test_device_state( 231 device_instance.device_state) 232 device_instance.device_state_monitor = \ 233 DeviceStateMonitor(device_instance) 234 if idevice.device_state == DeviceState.ONLINE: 235 device_instance.get_device_type() 236 self.append_device_by_sort(device_instance) 237 device = device_instance 238 else: 239 LOG.debug("find device(%s), host is %s, " 240 "port is %s, device sn is %s, usb type is %s" % 241 (device, device.host, device.port, device.device_sn, 242 device.usb_type)) 243 return device 244 except HdcCommandRejectedException as hcr_error: 245 LOG.debug("%s occurs error. Reason:%s" % 246 (idevice.device_sn, hcr_error)) 247 finally: 248 LOG.debug("find_or_create: release list con lock") 249 self.list_con.release() 250 251 def remove(self, idevice): 252 LOG.debug("remove: apply list con lock") 253 self.list_con.acquire() 254 try: 255 self.devices_list.remove(idevice) 256 finally: 257 LOG.debug("remove: release list con lock") 258 self.list_con.release() 259 260 def handle_device_event(self, device, event): 261 state_changed = None 262 old_state = device.device_allocation_state 263 new_state = handle_allocation_event(old_state, event) 264 265 if new_state == DeviceAllocationState.checking_availability: 266 if self.global_device_filter and \ 267 device.device_sn not in self.global_device_filter: 268 event = DeviceEvent.AVAILABLE_CHECK_IGNORED 269 else: 270 event = DeviceEvent.AVAILABLE_CHECK_PASSED 271 new_state = handle_allocation_event(new_state, event) 272 273 if old_state != new_state: 274 state_changed = True 275 device.device_allocation_state = new_state 276 277 if state_changed is True and \ 278 new_state == DeviceAllocationState.available: 279 # notify_device_state_change 280 LOG.debug("handle_device_event apply lock_con") 281 self.lock_con.acquire() 282 LOG.debug("find available device") 283 self.lock_con.notify_all() 284 LOG.debug("handle_device_event release lock_con") 285 self.lock_con.release() 286 287 if device.device_allocation_state == \ 288 DeviceAllocationState.unknown: 289 self.remove(device) 290 return 291 292 def launch_emulator(self): 293 pass 294 295 def kill_emulator(self): 296 pass 297 298 def list_devices(self): 299 print("devices:") 300 print("{0:<20}{1:<16}{2:<16}{3:<16}{4:<16}{5:<16}{6:<16}".format( 301 "Serial", "OsType", "State", "Allocation", "Product", "host", 302 "port")) 303 for device in self.devices_list: 304 print("{0:<20}{1:<16}{2:<16}{3:<16}{4:<16}{5:<16}{6:<16}".format( 305 convert_serial(device.device_sn), device.device_os_type, 306 device.test_device_state.value, 307 device.device_allocation_state, 308 device.label if device.label else 'None', 309 convert_ip(device.host), convert_port(device.port))) 310 311 312class ManagedDeviceListener(object): 313 """ 314 A class to listen for and act on device presence updates from ddmlib 315 """ 316 317 def __init__(self, manager): 318 self.manager = manager 319 320 def device_changed(self, idevice): 321 test_device = self.manager.find_or_create(idevice) 322 if test_device is None: 323 return 324 new_state = TestDeviceState.get_test_device_state(idevice.device_state) 325 test_device.test_device_state = new_state 326 if new_state == TestDeviceState.ONLINE: 327 self.manager.handle_device_event(test_device, 328 DeviceEvent.STATE_CHANGE_ONLINE) 329 elif new_state == TestDeviceState.NOT_AVAILABLE: 330 self.manager.handle_device_event(test_device, 331 DeviceEvent.STATE_CHANGE_OFFLINE) 332 test_device.device_state_monitor.set_state( 333 test_device.test_device_state) 334 LOG.debug("device changed to %s: %s %s %s %s" % ( 335 new_state, convert_serial(idevice.device_sn), 336 idevice.device_os_type, idevice.host, idevice.port)) 337 338 def device_connected(self, idevice): 339 test_device = self.manager.find_or_create(idevice) 340 if test_device is None: 341 return 342 new_state = TestDeviceState.get_test_device_state(idevice.device_state) 343 test_device.test_device_state = new_state 344 if test_device.test_device_state == TestDeviceState.ONLINE: 345 self.manager.handle_device_event(test_device, 346 DeviceEvent.CONNECTED_ONLINE) 347 elif new_state == TestDeviceState.NOT_AVAILABLE: 348 self.manager.handle_device_event(test_device, 349 DeviceEvent.CONNECTED_OFFLINE) 350 test_device.device_state_monitor.set_state( 351 test_device.test_device_state) 352 LOG.debug("device connected: %s %s %s %s" % ( 353 convert_serial(idevice.device_sn), idevice.device_os_type, 354 idevice.host, idevice.port)) 355 LOG.debug("set device %s %s to true" % ( 356 idevice.device_sn, ConfigConst.recover_state)) 357 test_device.set_recover_state(True) 358 359 def device_disconnected(self, disconnected_device): 360 test_device = self.manager.find(disconnected_device) 361 if test_device is not None: 362 test_device.test_device_state = TestDeviceState.NOT_AVAILABLE 363 self.manager.handle_device_event(test_device, 364 DeviceEvent.DISCONNECTED) 365 test_device.device_state_monitor.set_state( 366 TestDeviceState.NOT_AVAILABLE) 367 LOG.debug("device disconnected: %s %s %s %s" % ( 368 convert_serial(disconnected_device.device_sn), 369 disconnected_device.device_os_type, 370 disconnected_device.host, disconnected_device.port)) 371