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