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