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