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