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