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=3): 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 LOG.debug("Wait for available device founded") 132 self.wait_times += 3 133 if self.wait_times > timeout: 134 self.lock_con.wait(timeout) 135 else: 136 self.lock_con.wait(self.wait_times) 137 LOG.debug("Wait for available device founded") 138 return self.allocate_device_option(device_option) 139 finally: 140 LOG.debug("Apply device: release lock con lock") 141 self.lock_con.release() 142 143 def allocate_device_option(self, device_option): 144 """ 145 Request a device for testing that meets certain criteria. 146 """ 147 148 LOG.debug("Allocate device option: apply list con lock") 149 if not self.list_con.acquire(timeout=5): 150 LOG.debug("Allocate device option: list con wait timeout") 151 return None 152 try: 153 allocated_device = None 154 LOG.debug("Require device label is: %s" % device_option.label) 155 for device in self.devices_list: 156 if device_option.matches(device): 157 self.handle_device_event(device, 158 DeviceEvent.ALLOCATE_REQUEST) 159 LOG.debug("Allocate device sn: %s, type: %s" % ( 160 device.__get_serial__(), device.__class__)) 161 return device 162 return allocated_device 163 164 finally: 165 LOG.debug("Allocate device option: release list con lock") 166 self.list_con.release() 167 168 def release_device(self, device): 169 LOG.debug("Release device: apply list con lock") 170 self.list_con.acquire() 171 try: 172 if device.test_device_state == TestDeviceState.ONLINE: 173 self.handle_device_event(device, DeviceEvent.FREE_AVAILABLE) 174 else: 175 self.handle_device_event(device, DeviceEvent.FREE_UNAVAILABLE) 176 177 device.device_id = None 178 179 LOG.debug("Free device sn: %s, type: %s" % ( 180 device.__get_serial__(), device.__class__.__name__)) 181 182 finally: 183 LOG.debug("Release_device: release list con lock") 184 self.list_con.release() 185 186 def reset_device(self, device): 187 if device and hasattr(device, "reset"): 188 device.reset() 189 190 def find_device(self, device_sn, device_os_type): 191 for device in self.devices_list: 192 if device.device_sn == device_sn and \ 193 device.device_os_type == device_os_type: 194 return device 195 196 def append_device_by_sort(self, device_instance): 197 if (not self.global_device_filter or 198 not self.devices_list or 199 device_instance.device_sn not in self.global_device_filter): 200 self.devices_list.append(device_instance) 201 else: 202 device_dict = dict(zip( 203 self.global_device_filter, 204 list(range(1, len(self.global_device_filter) + 1)))) 205 for index in range(len(self.devices_list)): 206 if self.devices_list[index].device_sn not in \ 207 self.global_device_filter: 208 self.devices_list.insert(index, device_instance) 209 break 210 if device_dict[device_instance.device_sn] < \ 211 device_dict[self.devices_list[index].device_sn]: 212 self.devices_list.insert(index, device_instance) 213 break 214 else: 215 self.devices_list.append(device_instance) 216 217 def find_or_create(self, idevice): 218 LOG.debug("Find or create: apply list con lock") 219 self.list_con.acquire() 220 try: 221 device = self.find_device(idevice.device_sn, 222 idevice.device_os_type) 223 if device is None: 224 device = get_plugin( 225 plugin_type=Plugin.DEVICE, 226 plugin_id=idevice.device_os_type)[0] 227 device_instance = device.__class__() 228 device_instance.__set_serial__(idevice.device_sn) 229 device_instance.host = idevice.host 230 device_instance.port = idevice.port 231 device_instance.usb_type = self.device_connector.usb_type 232 LOG.debug("Create device(%s) host is %s, " 233 "port is %s, device sn is %s, usb type is %s" % 234 (device_instance, device_instance.host, 235 device_instance.port, device_instance.device_sn, 236 device_instance.usb_type)) 237 device_instance.device_state = idevice.device_state 238 device_instance.test_device_state = \ 239 TestDeviceState.get_test_device_state( 240 device_instance.device_state) 241 device_instance.device_state_monitor = \ 242 DeviceStateMonitor(device_instance) 243 if idevice.device_state == DeviceState.ONLINE or \ 244 idevice.device_state == DeviceState.CONNECTED: 245 device_instance.get_device_type() 246 self.append_device_by_sort(device_instance) 247 device = device_instance 248 else: 249 LOG.debug("Find device(%s), host is %s, " 250 "port is %s, device sn is %s, usb type is %s" % 251 (device, device.host, device.port, device.device_sn, 252 device.usb_type)) 253 return device 254 except HdcCommandRejectedException as hcr_error: 255 LOG.debug("%s occurs error. Reason:%s" % 256 (idevice.device_sn, hcr_error)) 257 finally: 258 LOG.debug("Find or create: release list con lock") 259 self.list_con.release() 260 261 def remove(self, idevice): 262 LOG.debug("Remove: apply list con lock") 263 self.list_con.acquire() 264 try: 265 self.devices_list.remove(idevice) 266 finally: 267 LOG.debug("Remove: release list con lock") 268 self.list_con.release() 269 270 def handle_device_event(self, device, event): 271 state_changed = None 272 old_state = device.device_allocation_state 273 new_state = handle_allocation_event(old_state, event) 274 275 if new_state == DeviceAllocationState.checking_availability: 276 if self.global_device_filter and \ 277 device.device_sn not in self.global_device_filter: 278 event = DeviceEvent.AVAILABLE_CHECK_IGNORED 279 else: 280 event = DeviceEvent.AVAILABLE_CHECK_PASSED 281 new_state = handle_allocation_event(new_state, event) 282 283 if old_state != new_state: 284 state_changed = True 285 device.device_allocation_state = new_state 286 287 if state_changed is True and \ 288 new_state == DeviceAllocationState.available: 289 # notify_device_state_change 290 LOG.debug("Handle device event apply lock con") 291 self.lock_con.acquire() 292 LOG.debug("Find available device") 293 self.lock_con.notify_all() 294 LOG.debug("Handle device event release lock con") 295 self.lock_con.release() 296 297 if device.device_allocation_state == \ 298 DeviceAllocationState.unknown: 299 self.remove(device) 300 return 301 302 def launch_emulator(self): 303 pass 304 305 def kill_emulator(self): 306 pass 307 308 def list_devices(self): 309 self.device_connector.monitor_lock.acquire(1) 310 print("devices:") 311 print("{0:<20}{1:<16}{2:<16}{3:<16}{4:<16}{5:<16}{6:<16}".format( 312 "Serial", "OsType", "State", "Allocation", "Product", "host", 313 "port")) 314 for device in self.devices_list: 315 print("{0:<20}{1:<16}{2:<16}{3:<16}{4:<16}{5:<16}{6:<16}".format( 316 convert_serial(device.device_sn), device.device_os_type, 317 device.test_device_state.value, 318 device.device_allocation_state, 319 device.label if device.label else 'None', 320 device.host, device.port)) 321 self.device_connector.monitor_lock.release() 322 323 def __filter_selector__(self, selector): 324 if isinstance(selector, DeviceSelector): 325 return True 326 return False 327 328 def __filter_xml_node__(self, node): 329 if isinstance(node, DeviceNode): 330 if HDC_NAME in node.get_connectors() or\ 331 HDC_STD_NAME in node.get_connectors(): 332 return True 333 return False 334 335 336class ManagedDeviceListener(object): 337 """ 338 A class to listen for and act on device presence updates from ddmlib 339 """ 340 341 def __init__(self, manager): 342 self.manager = manager 343 344 def device_changed(self, idevice): 345 test_device = self.manager.find_or_create(idevice) 346 if test_device is None: 347 return 348 new_state = TestDeviceState.get_test_device_state(idevice.device_state) 349 test_device.test_device_state = new_state 350 if new_state == TestDeviceState.ONLINE: 351 self.manager.handle_device_event(test_device, 352 DeviceEvent.STATE_CHANGE_ONLINE) 353 elif new_state == TestDeviceState.NOT_AVAILABLE: 354 self.manager.handle_device_event(test_device, 355 DeviceEvent.STATE_CHANGE_OFFLINE) 356 test_device.device_state_monitor.set_state( 357 test_device.test_device_state) 358 LOG.debug("Device changed to %s: %s %s %s %s" % ( 359 new_state, convert_serial(idevice.device_sn), 360 idevice.device_os_type, idevice.host, idevice.port)) 361 362 def device_connected(self, idevice): 363 test_device = self.manager.find_or_create(idevice) 364 if test_device is None: 365 return 366 367 new_state = TestDeviceState.get_test_device_state(idevice.device_state) 368 test_device.test_device_state = new_state 369 if test_device.test_device_state == TestDeviceState.ONLINE: 370 self.manager.handle_device_event(test_device, 371 DeviceEvent.CONNECTED_ONLINE) 372 elif new_state == TestDeviceState.NOT_AVAILABLE: 373 self.manager.handle_device_event(test_device, 374 DeviceEvent.CONNECTED_OFFLINE) 375 test_device.device_state_monitor.set_state( 376 test_device.test_device_state) 377 LOG.debug("Device connected: {} {} {} {}, state: {}".format( 378 convert_serial(idevice.device_sn), idevice.device_os_type, 379 idevice.host, idevice.port, test_device.test_device_state)) 380 LOG.debug("Set device %s %s to true" % ( 381 convert_serial(idevice.device_sn), ConfigConst.recover_state)) 382 test_device.set_recover_state(True) 383 384 def device_disconnected(self, disconnected_device): 385 test_device = self.manager.find(disconnected_device) 386 if test_device is not None: 387 test_device.test_device_state = TestDeviceState.NOT_AVAILABLE 388 self.manager.handle_device_event(test_device, 389 DeviceEvent.DISCONNECTED) 390 test_device.device_state_monitor.set_state( 391 TestDeviceState.NOT_AVAILABLE) 392 LOG.debug("Device disconnected: %s %s %s %s" % ( 393 convert_serial(disconnected_device.device_sn), 394 disconnected_device.device_os_type, 395 disconnected_device.host, disconnected_device.port)) 396