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