• 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#
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