• 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 re
19import time
20import os
21import threading
22import platform
23import subprocess
24import sys
25from xdevice import DeviceOsType
26from xdevice import FilePermission
27from xdevice import ProductForm
28from xdevice import ReportException
29from xdevice import IDevice
30from xdevice import platform_logger
31from xdevice import Plugin
32from xdevice import exec_cmd
33from xdevice import ConfigConst
34from xdevice import HdcError
35from xdevice import DeviceAllocationState
36from xdevice import DeviceConnectorType
37from xdevice import TestDeviceState
38from xdevice import convert_serial
39from xdevice import check_path_legal
40from xdevice import start_standing_subprocess
41from xdevice import stop_standing_subprocess
42from xdevice import get_cst_time
43from xdevice import Platform
44from xdevice import AppInstallError
45from xdevice import RpcNotRunningError
46
47from ohos.environment.dmlib import HdcHelper
48from ohos.environment.dmlib import CollectingOutputReceiver
49
50__all__ = ["Device"]
51TIMEOUT = 300 * 1000
52RETRY_ATTEMPTS = 2
53DEFAULT_UNAVAILABLE_TIMEOUT = 20 * 1000
54BACKGROUND_TIME = 2 * 60 * 1000
55LOG = platform_logger("Device")
56DEVICETEST_HAP_PACKAGE_NAME = "com.ohos.devicetest"
57UITEST_NAME = "uitest"
58UITEST_SINGLENESS = "singleness"
59UITEST_PATH = "/system/bin/uitest"
60UITEST_SHMF = "/data/app/el2/100/base/{}/cache/shmf".format(DEVICETEST_HAP_PACKAGE_NAME)
61UITEST_COMMAND = "{} start-daemon 0123456789 &".format(UITEST_PATH)
62NATIVE_CRASH_PATH = "/data/log/faultlog/temp"
63JS_CRASH_PATH = "/data/log/faultlog/faultlogger"
64ROOT_PATH = "/data/log/faultlog"
65LOGLEVEL = ["DEBUG", "INFO", "WARN", "ERROR", "FATAL"]
66
67
68def perform_device_action(func):
69    def callback_to_outer(device, msg):
70        # callback to decc ui
71        if getattr(device, "callback_method", None):
72            device.callback_method(msg)
73
74    def device_action(self, *args, **kwargs):
75        if not self.get_recover_state():
76            LOG.debug("Device {} {} is false".format(self.device_sn,
77                                                     ConfigConst.recover_state))
78            return None
79        # avoid infinite recursion, such as device reboot
80        abort_on_exception = bool(kwargs.get("abort_on_exception", False))
81        if abort_on_exception:
82            result = func(self, *args, **kwargs)
83            return result
84
85        tmp = int(kwargs.get("retry", RETRY_ATTEMPTS))
86        retry = tmp + 1 if tmp > 0 else 1
87        exception = None
88        for _ in range(retry):
89            try:
90                result = func(self, *args, **kwargs)
91                return result
92            except ReportException as error:
93                self.log.exception("Generate report error!", exc_info=False)
94                exception = error
95            except (ConnectionResetError,  # pylint:disable=undefined-variable
96                    ConnectionRefusedError,  # pylint:disable=undefined-variable
97                    ConnectionAbortedError) as error:  # pylint:disable=undefined-variable
98                self.log.error("error type: {}, error: {}".format
99                               (error.__class__.__name__, error))
100                # check hdc if is running
101                if not HdcHelper.check_if_hdc_running():
102                    LOG.debug("{} not running, set device {} {} false".format(
103                        HdcHelper.CONNECTOR_NAME, self.device_sn, ConfigConst.recover_state))
104                    self.set_recover_state(False)
105                    callback_to_outer(self, "recover failed")
106                    raise error
107                callback_to_outer(self, "error:{}, prepare to recover".format(error))
108                if not self.recover_device():
109                    LOG.debug("Set device {} {} false".format(
110                        self.device_sn, ConfigConst.recover_state))
111                    self.set_recover_state(False)
112                    callback_to_outer(self, "recover failed")
113                    raise error
114                exception = error
115                callback_to_outer(self, "recover success")
116            except HdcError as error:
117                self.log.error("error type: {}, error: {}".format(error.__class__.__name__, error))
118                callback_to_outer(self, "error:{}, prepare to recover".format(error))
119                if not self.recover_device():
120                    LOG.debug("Set device {} {} false".format(
121                        self.device_sn, ConfigConst.recover_state))
122                    self.set_recover_state(False)
123                    callback_to_outer(self, "recover failed")
124                    raise error
125                exception = error
126                callback_to_outer(self, "recover success")
127            except Exception as error:
128                self.log.exception("error type: {}, error: {}".format(
129                    error.__class__.__name__, error), exc_info=False)
130                exception = error
131        raise exception
132
133    return device_action
134
135
136@Plugin(type=Plugin.DEVICE, id=DeviceOsType.default)
137class Device(IDevice):
138    """
139    Class representing a device.
140
141    Each object of this class represents one device in xDevice,
142    including handles to hdc, fastboot, and test agent (DeviceTest.apk).
143
144    Attributes:
145        device_sn: A string that's the serial number of the device.
146    """
147
148    device_sn = None
149    host = None
150    port = None
151    usb_type = DeviceConnectorType.hdc
152    is_timeout = False
153    device_hilog_proc = None
154    device_os_type = DeviceOsType.default
155    test_device_state = None
156    device_allocation_state = DeviceAllocationState.available
157    label = ProductForm.phone
158    log = platform_logger("Device")
159    device_state_monitor = None
160    reboot_timeout = 2 * 60 * 1000
161    _device_log_collector = None
162
163    _proxy = None
164    _abc_proxy = None
165    _is_abc = False
166    initdevice = True
167    d_port = 8011
168    abc_d_port = 8012
169    _uitestdeamon = None
170    rpc_timeout = 300
171    device_id = None
172    reconnecttimes = 0
173    _h_port = None
174    oh_module_package = None
175    module_ablity_name = None
176    _device_report_path = None
177    test_platform = Platform.ohos
178    _webview = None
179
180    model_dict = {
181        'default': ProductForm.phone,
182        'phone': ProductForm.phone,
183        'car': ProductForm.car,
184        'tv': ProductForm.television,
185        'watch': ProductForm.watch,
186        'tablet': ProductForm.tablet,
187        '2in1': ProductForm._2in1,
188        'nosdcard': ProductForm.phone
189    }
190
191    def __init__(self):
192        self.extend_value = {}
193        self.device_lock = threading.RLock()
194        self.forward_ports = []
195        self.forward_ports_abc = []
196        self.proxy_listener = None
197
198    def __eq__(self, other):
199        return self.device_sn == other.__get_serial__() and \
200            self.device_os_type == other.device_os_type
201
202    def __set_serial__(self, device_sn=""):
203        self.device_sn = device_sn
204        return self.device_sn
205
206    def __get_serial__(self):
207        return self.device_sn
208
209    def get(self, key=None, default=None):
210        if not key:
211            return default
212        value = getattr(self, key, None)
213        if value:
214            return value
215        else:
216            return self.extend_value.get(key, default)
217
218    def recover_device(self):
219        if not self.get_recover_state():
220            LOG.debug("Device %s %s is false, cannot recover device" % (
221                self.device_sn, ConfigConst.recover_state))
222            return False
223
224        result = self.device_state_monitor.wait_for_device_available(self.reboot_timeout)
225        if result:
226            self.device_log_collector.restart_catch_device_log()
227        return result
228
229    def get_device_type(self):
230        model = self.get_property("const.product.devicetype",
231                                  abort_on_exception=True)
232        self.label = self.model_dict.get(model, ProductForm.phone)
233
234    def get_property(self, prop_name, retry=RETRY_ATTEMPTS,
235                     abort_on_exception=False):
236        """
237        Hdc command, ddmlib function.
238        """
239        command = "param get %s" % prop_name
240        stdout = self.execute_shell_command(command, timeout=5 * 1000,
241                                            output_flag=False,
242                                            retry=retry,
243                                            abort_on_exception=abort_on_exception).strip()
244        if stdout:
245            LOG.debug(stdout)
246        return stdout
247
248    @perform_device_action
249    def connector_command(self, command, **kwargs):
250        timeout = int(kwargs.get("timeout", TIMEOUT)) / 1000
251        error_print = bool(kwargs.get("error_print", True))
252        join_result = bool(kwargs.get("join_result", False))
253        timeout_msg = '' if timeout == 300.0 else \
254            " with timeout %ss" % timeout
255        if self.host != "127.0.0.1":
256            cmd = [HdcHelper.CONNECTOR_NAME, "-s", "{}:{}".format(self.host, self.port),
257                   "-t", self.device_sn]
258        else:
259            cmd = [HdcHelper.CONNECTOR_NAME, "-t", self.device_sn]
260        LOG.debug("{} execute command {} {}{}".format(convert_serial(self.device_sn),
261                                                      HdcHelper.CONNECTOR_NAME,
262                                                      command, timeout_msg))
263        if isinstance(command, list):
264            cmd.extend(command)
265        else:
266            command = command.strip()
267            cmd.extend(command.split(" "))
268        result = exec_cmd(cmd, timeout, error_print, join_result)
269        if not result:
270            return result
271        is_print = bool(kwargs.get("is_print", True))
272        if is_print:
273            for line in str(result).split("\n"):
274                if line.strip():
275                    LOG.debug(line.strip())
276        return result
277
278    @perform_device_action
279    def execute_shell_command(self, command, timeout=TIMEOUT,
280                              receiver=None, **kwargs):
281        if not receiver:
282            collect_receiver = CollectingOutputReceiver()
283            HdcHelper.execute_shell_command(
284                self, command, timeout=timeout,
285                receiver=collect_receiver, **kwargs)
286            return collect_receiver.output
287        else:
288            return HdcHelper.execute_shell_command(
289                self, command, timeout=timeout,
290                receiver=receiver, **kwargs)
291
292    def execute_shell_cmd_background(self, command, timeout=TIMEOUT,
293                                     receiver=None):
294        status = HdcHelper.execute_shell_command(self, command,
295                                                 timeout=timeout,
296                                                 receiver=receiver)
297
298        self.wait_for_device_not_available(DEFAULT_UNAVAILABLE_TIMEOUT)
299        self.device_state_monitor.wait_for_device_available(BACKGROUND_TIME)
300        cmd = "target mount"
301        self.connector_command(cmd)
302        self.device_log_collector.restart_catch_device_log()
303        return status
304
305    def wait_for_device_not_available(self, wait_time):
306        return self.device_state_monitor.wait_for_device_not_available(
307            wait_time)
308
309    def _wait_for_device_online(self, wait_time=None):
310        return self.device_state_monitor.wait_for_device_online(wait_time)
311
312    def _do_reboot(self):
313        HdcHelper.reboot(self)
314        self.wait_for_boot_completion()
315
316    def _reboot_until_online(self):
317        self._do_reboot()
318
319    def reboot(self):
320        self._reboot_until_online()
321        self.enable_hdc_root()
322        self.device_log_collector.restart_catch_device_log()
323
324    @perform_device_action
325    def install_package(self, package_path, command=""):
326        if package_path is None:
327            raise HdcError(
328                "install package: package path cannot be None!")
329        return HdcHelper.install_package(self, package_path, command)
330
331    @perform_device_action
332    def uninstall_package(self, package_name):
333        return HdcHelper.uninstall_package(self, package_name)
334
335    @perform_device_action
336    def push_file(self, local, remote, **kwargs):
337        """
338        Push a single file.
339        The top directory won't be created if is_create is False (by default)
340        and vice versa
341        """
342        local = "\"{}\"".format(local)
343        remote = "\"{}\"".format(remote)
344        if local is None:
345            raise HdcError("XDevice Local path cannot be None!")
346
347        remote_is_dir = kwargs.get("remote_is_dir", False)
348        if remote_is_dir:
349            ret = self.execute_shell_command("test -d %s && echo 0" % remote)
350            if not (ret != "" and len(str(ret).split()) != 0 and
351                    str(ret).split()[0] == "0"):
352                self.execute_shell_command("mkdir -p %s" % remote)
353
354        if self.host != "127.0.0.1":
355            self.connector_command("file send {} {}".format(local, remote))
356        else:
357            is_create = kwargs.get("is_create", False)
358            timeout = kwargs.get("timeout", TIMEOUT)
359            HdcHelper.push_file(self, local, remote, is_create=is_create,
360                                timeout=timeout)
361        if not self.is_file_exist(remote):
362            LOG.error("Push %s to %s failed" % (local, remote))
363            raise HdcError("push %s to %s failed" % (local, remote))
364
365    @perform_device_action
366    def pull_file(self, remote, local, **kwargs):
367        """
368        Pull a single file.
369        The top directory won't be created if is_create is False (by default)
370        and vice versa
371        """
372        local = "\"{}\"".format(local)
373        remote = "\"{}\"".format(remote)
374        self.connector_command("file recv {} {}".format(remote, local))
375
376    def enable_hdc_root(self):
377        return True
378
379    def is_directory(self, path):
380        path = check_path_legal(path)
381        output = self.execute_shell_command("ls -ld {}".format(path))
382        if output and output.startswith('d'):
383            return True
384        return False
385
386    def is_file_exist(self, file_path):
387        file_path = check_path_legal(file_path)
388        output = self.execute_shell_command("ls {}".format(file_path))
389        if output and "No such file or directory" not in output:
390            return True
391        return False
392
393    def get_recover_result(self, retry=RETRY_ATTEMPTS):
394        command = "param get bootevent.boot.completed"
395        stdout = self.execute_shell_command(command, timeout=5 * 1000,
396                                            output_flag=False, retry=retry,
397                                            abort_on_exception=True).strip()
398        return stdout
399
400    def set_recover_state(self, state):
401        with self.device_lock:
402            setattr(self, ConfigConst.recover_state, state)
403            if not state:
404                self.test_device_state = TestDeviceState.NOT_AVAILABLE
405                self.device_allocation_state = DeviceAllocationState.unavailable
406                # do proxy clean
407                if self.proxy_listener is not None:
408                    if self._abc_proxy or (not self.is_abc and self.proxy):
409                        self.proxy_listener(is_exception=True)
410
411    def get_recover_state(self, default_state=True):
412        with self.device_lock:
413            state = getattr(self, ConfigConst.recover_state, default_state)
414            return state
415
416    def wait_for_boot_completion(self):
417        """Waits for the device to boot up.
418
419        Returns:
420            True if the device successfully finished booting, False otherwise.
421        """
422        return self.device_state_monitor.wait_for_boot_complete(self.reboot_timeout)
423
424    @classmethod
425    def check_recover_result(cls, recover_result):
426        return "true" in recover_result
427
428    @property
429    def device_log_collector(self):
430        if self._device_log_collector is None:
431            self._device_log_collector = DeviceLogCollector(self)
432        return self._device_log_collector
433
434    def close(self):
435        self.reconnecttimes = 0
436
437    def reset(self):
438        self.log.debug("start reset device...")
439        if self._proxy is not None:
440            self._proxy.close()
441        self._proxy = None
442        if self._uitestdeamon is not None:
443            self._uitestdeamon = None
444        if self.is_abc:
445            self.stop_harmony_rpc(kill_all=False)
446        else:
447            self.stop_harmony_rpc(kill_all=True)
448            # do proxy clean
449        if self.proxy_listener is not None:
450            self.proxy_listener(is_exception=False)
451        self.remove_ports()
452        self.device_log_collector.stop_restart_catch_device_log()
453
454    @property
455    def is_abc(self):
456        # _is_abc init in device test driver
457        return self._is_abc
458
459    @property
460    def proxy(self):
461        """The first rpc session initiated on this device. None if there isn't
462        one.
463        """
464        try:
465            if self._proxy is None:
466                # check uitest
467                self.check_uitest_status()
468                self._proxy = self.get_harmony()
469        except AppInstallError as error:
470            raise error
471        except RpcNotRunningError as error:
472            raise error
473        except Exception as error:
474            self._proxy = None
475            self.log.error("DeviceTest-10012 proxy:%s" % str(error))
476        return self._proxy
477
478    @property
479    def abc_proxy(self):
480        """The first rpc session initiated on this device. None if there isn't
481        one.
482        """
483        try:
484            if self._abc_proxy is None:
485                # check uitest
486                self.check_uitest_status()
487                self._abc_proxy = self.get_harmony(start_abc=True)
488        except RpcNotRunningError as error:
489            raise error
490        except Exception as error:
491            self._abc_proxy = None
492            self.log.error("DeviceTest-10012 abc_proxy:%s" % str(error))
493        return self._abc_proxy
494
495    @property
496    def uitestdeamon(self):
497        from devicetest.controllers.uitestdeamon import \
498            UiTestDeamon
499        if self._uitestdeamon is None:
500            self._uitestdeamon = UiTestDeamon(self)
501        return self._uitestdeamon
502
503    @classmethod
504    def set_module_package(cls, module_packag):
505        cls.oh_module_package = module_packag
506
507    @classmethod
508    def set_moudle_ablity_name(cls, module_ablity_name):
509        cls.module_ablity_name = module_ablity_name
510
511    @property
512    def is_oh(self):
513        return True
514
515    def get_harmony(self, start_abc=False):
516        if self.initdevice:
517            if start_abc:
518                self.start_abc_rpc(re_install_rpc=True)
519            else:
520                self.start_harmony_rpc(re_install_rpc=True)
521        self._h_port = self.get_local_port(start_abc)
522        port = self.d_port if not start_abc else self.abc_d_port
523        # clear old port,because abc and fast mode will not remove port
524        cmd = "fport tcp:{} tcp:{}".format(
525            self._h_port, port)
526        self.connector_command(cmd)
527        self.log.info(
528            "get_proxy d_port:{} {}".format(self._h_port, port))
529        rpc_proxy = None
530        try:
531            from devicetest.controllers.openharmony import OpenHarmony
532            rpc_proxy = OpenHarmony(port=self._h_port, addr=self.host, device=self)
533        except Exception as error:
534            self.log.error(' proxy init error: {}.'.format(str(error)))
535        return rpc_proxy
536
537    def start_uitest(self):
538        result = ""
539        if self.is_abc:
540            result = self.execute_shell_command("{} start-daemon singleness &".format(UITEST_PATH))
541        else:
542            share_mem_mode = False
543            # uitest基础版本号,比该版本号大的用共享内存的方式进行启动
544            base_version = [3, 2, 2, 2]
545            uitest_version = self.execute_shell_command("{} --version".format(UITEST_PATH))
546            if uitest_version and re.match(r'^\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}', uitest_version):
547                uitest_version = uitest_version.split(".")
548                for index, _ in enumerate(uitest_version):
549                    if int(uitest_version[index]) > base_version[index]:
550                        share_mem_mode = True
551                        break
552            if share_mem_mode:
553                if not self.is_file_exist(UITEST_SHMF):
554                    self.log.debug('Path {} not exist, create it.'.format(UITEST_SHMF))
555                    self.execute_shell_command("echo abc > {}".format(UITEST_SHMF))
556                    self.execute_shell_command("chmod -R 666 {}".format(UITEST_SHMF))
557                result = self.execute_shell_command("{} start-daemon {} &".format(UITEST_PATH, UITEST_SHMF))
558            else:
559                result = self.execute_shell_command(UITEST_COMMAND)
560        self.log.debug('start uitest, {}'.format(result))
561
562    def start_harmony_rpc(self, re_install_rpc=False):
563        if self.check_rpc_status(check_abc=False):
564            if (hasattr(sys, ConfigConst.env_pool_cache) and
565                getattr(sys, ConfigConst.env_pool_cache, False)) \
566                    or not re_install_rpc:
567                self.log.debug('Harmony rpc already start!!!!')
568                return
569        from devicetest.core.error_message import ErrorMessage
570        if re_install_rpc:
571            try:
572                from devicetest.controllers.openharmony import OpenHarmony
573                OpenHarmony.install_harmony_rpc(self)
574            except ImportError as error:  # pylint:disable=undefined-variable
575                self.log.debug(str(error))
576                self.log.error('please check devicetest extension module is exist.')
577                raise Exception(ErrorMessage.Error_01437.Topic)
578            except AppInstallError as error:
579                raise error
580            except Exception as error:
581                self.log.debug(str(error))
582                self.log.error('root device init RPC error.')
583                raise Exception(ErrorMessage.Error_01437.Topic)
584        if not self.is_abc:
585            self.stop_harmony_rpc()
586        else:
587            self.log.debug('Abc mode, kill hap if hap is running.')
588            self.stop_harmony_rpc(kill_all=False)
589        cmd = "aa start -a {}.ServiceAbility -b {}".format(DEVICETEST_HAP_PACKAGE_NAME, DEVICETEST_HAP_PACKAGE_NAME)
590        result = self.execute_shell_command(cmd)
591        self.log.debug('start devicetest ability, {}'.format(result))
592        if "successfully" not in result:
593            raise RpcNotRunningError("harmony {} rpc start failed".format("system" if self.is_abc else "normal"),
594                                     error_no=ErrorMessage.Error_01439.Code)
595        if not self.is_abc:
596            self.start_uitest()
597        time.sleep(1)
598        if not self.check_rpc_status(check_abc=False):
599            raise RpcNotRunningError("harmony {} rpc process not found".format("system" if self.is_abc else "normal"),
600                                     error_no=ErrorMessage.Error_01440.Code)
601
602    def start_abc_rpc(self, re_install_rpc=False):
603        if re_install_rpc:
604            try:
605                from devicetest.controllers.openharmony import OpenHarmony
606                OpenHarmony.init_abc_resource(self)
607            except ImportError as error:  # pylint:disable=undefined-variable
608                self.log.debug(str(error))
609                self.log.error('please check devicetest extension module is exist.')
610                raise error
611            except Exception as error:
612                self.log.debug(str(error))
613                self.log.error('root device init abc RPC error.')
614                raise error
615        if self.is_abc and self.check_rpc_status(check_abc=True):
616            self.log.debug('Harmony abc rpc already start!!!!')
617            return
618        self.start_uitest()
619        time.sleep(1)
620        from devicetest.core.error_message import ErrorMessage
621        if not self.check_rpc_status(check_abc=True):
622            raise RpcNotRunningError("harmony abc rpc process not found", error_no=ErrorMessage.Error_01440.Code)
623
624    def stop_harmony_rpc(self, kill_all=True):
625        # only kill devicetest in abc mode, or kill all
626        proc_pids = self.get_devicetest_proc_pid()
627        if not kill_all:
628            proc_pids.pop()
629        for pid in proc_pids:
630            if pid != "":
631                cmd = 'kill {}'.format(pid)
632                self.execute_shell_command(cmd)
633
634    # check uitest if running well, otherwise kill it first
635    def check_uitest_status(self):
636        self.log.debug('Check uitest running status.')
637        proc_pids = self.get_devicetest_proc_pid(print_info=False)
638        if self.is_abc and proc_pids[1] != "":
639            if proc_pids[0] != "":
640                self.execute_shell_command('kill {}'.format(proc_pids[0]))
641            self.execute_shell_command('kill {}'.format(proc_pids[1]))
642            self.log.debug('Uitest is running in normal mode, current mode is abc, wait it exit.')
643        if not self.is_abc and proc_pids[2] != "":
644            if proc_pids[0] != "":
645                self.execute_shell_command('kill {}'.format(proc_pids[0]))
646            self.execute_shell_command('kill {}'.format(proc_pids[2]))
647            self.log.debug('Uitest is running in abc mode, current mode is normal, wait it exit.')
648        self.log.debug('Finish check uitest running status.')
649
650    def get_devicetest_proc_pid(self, print_info=True):
651        uitest = "{} start-daemon".format(UITEST_NAME)
652        cmd = 'ps -ef | grep -E \'{}|{}|{}\''.format(DEVICETEST_HAP_PACKAGE_NAME, UITEST_NAME, UITEST_SINGLENESS)
653        proc_running = self.execute_shell_command(cmd).strip()
654        proc_running = proc_running.split("\n")
655        proc_pids = [""] * 3
656        result = []
657        for data in proc_running:
658            if DEVICETEST_HAP_PACKAGE_NAME in data and "grep" not in data and UITEST_NAME not in data:
659                result.append("{} running status: {}".format(DEVICETEST_HAP_PACKAGE_NAME, data))
660                data = data.split()
661                proc_pids[0] = data[1]
662            if uitest in data and "grep" not in data:
663                if UITEST_SINGLENESS in data:
664                    result.append("{} running status: {}".format(UITEST_SINGLENESS, data))
665                    data = data.split()
666                    proc_pids[2] = data[1]
667                else:
668                    result.append("{} running status: {}".format(UITEST_NAME, data))
669                    data = data.split()
670                    proc_pids[1] = data[1]
671        if print_info:
672            self.log.debug("\n".join(result))
673        return proc_pids
674
675    def is_harmony_rpc_running(self, check_abc=False):
676        proc_pids = self.get_devicetest_proc_pid(print_info=False)
677        if not self.is_abc:
678            self.log.debug('is_proc_running: agent pid: {}, uitest pid: {}'.format(proc_pids[0], proc_pids[1]))
679            if proc_pids[0] != "" and proc_pids[1] != "":
680                return True
681        else:
682            if check_abc:
683                self.log.debug('is_proc_running: uitest pid: {}'.format(proc_pids[2]))
684                if proc_pids[2] != "":
685                    return True
686            else:
687                self.log.debug('is_proc_running: agent pid: {}'.format(proc_pids[0]))
688                if proc_pids[0] != "":
689                    return True
690        return False
691
692    def is_harmony_rpc_socket_running(self, port, check_server=True):
693        out = self.execute_shell_command("netstat -anp | grep {}".format(port))
694        self.log.debug(out)
695        if out:
696            out = out.split("\n")
697            for data in out:
698                if check_server:
699                    if "LISTEN" in data and str(port) in data:
700                        return True
701                else:
702                    if "hdcd" in data and str(port) in data:
703                        return True
704        return False
705
706    def check_rpc_status(self, check_abc=False, check_server=True):
707        port = self.d_port if not check_abc else self.abc_d_port
708        if self.is_harmony_rpc_running(check_abc) and \
709                self.is_harmony_rpc_socket_running(port, check_server=check_server):
710            self.log.debug('Harmony rpc is running!!!! If is check abc: {}'.format(check_abc))
711            return True
712        self.log.debug('Harmony rpc is not running!!!! If is check abc: {}'.format(check_abc))
713        return False
714
715    def install_app(self, remote_path, command):
716        try:
717            ret = self.execute_shell_command(
718                "pm install %s %s" % (command, remote_path))
719            if ret is not None and str(
720                    ret) != "" and "Unknown option: -g" in str(ret):
721                return self.execute_shell_command(
722                    "pm install -r %s" % remote_path)
723            return ret
724        except Exception as error:
725            self.log.error("%s, maybe there has a warning box appears "
726                           "when installing RPC." % error)
727            return False
728
729    def uninstall_app(self, package_name):
730        try:
731            ret = self.execute_shell_command("pm uninstall %s" % package_name)
732            self.log.debug(ret)
733            return ret
734        except Exception as err:
735            self.log.error('DeviceTest-20013 uninstall: %s' % str(err))
736            return False
737
738    def reconnect(self, waittime=60):
739        '''
740        @summary: Reconnect the device.
741        '''
742        if not self.wait_for_boot_completion():
743            self._proxy = None
744            self._uitestdeamon = None
745            raise Exception("Reconnect timed out.")
746        if self._proxy:
747            # do proxy clean
748            if not self.is_abc and self.proxy_listener is not None:
749                self.proxy_listener(is_exception=True)
750            self.start_harmony_rpc(re_install_rpc=True)
751            self._h_port = self.get_local_port(start_abc=False)
752            cmd = "fport tcp:{} tcp:{}".format(
753                self._h_port, self.d_port)
754            self.connector_command(cmd)
755            try:
756                self._proxy.init(port=self._h_port, addr=self.host, device=self)
757            except Exception as _:
758                time.sleep(3)
759                self._proxy.init(port=self._h_port, addr=self.host, device=self)
760
761        if self.is_abc and self._abc_proxy:
762            # do proxy clean
763            if self.proxy_listener is not None:
764                self.proxy_listener(is_exception=True)
765            self.start_abc_rpc(re_install_rpc=True)
766            self._h_port = self.get_local_port(start_abc=True)
767            cmd = "fport tcp:{} tcp:{}".format(
768                self._h_port, self.abc_d_port)
769            self.connector_command(cmd)
770            try:
771                self._abc_proxy.init(port=self._h_port, addr=self.host, device=self)
772            except Exception as _:
773                time.sleep(3)
774                self._abc_proxy.init(port=self._h_port, addr=self.host, device=self)
775
776        if self._uitestdeamon is not None:
777            self._uitestdeamon.init(self)
778
779        if self._proxy:
780            return self._proxy
781        return None
782
783    def get_local_port(self, start_abc):
784        from devicetest.utils.util import get_forward_port
785        host = self.host
786        port = None
787        h_port = get_forward_port(self, host, port)
788        if start_abc:
789            self.forward_ports_abc.append(h_port)
790        else:
791            self.forward_ports.append(h_port)
792        self.log.info("tcp forward port: {} for {}".format(
793            h_port, convert_serial(self.device_sn)))
794        return h_port
795
796    def remove_ports(self):
797        for port in self.forward_ports:
798            cmd = "fport rm tcp:{} tcp:{}".format(
799                port, self.d_port)
800            self.connector_command(cmd)
801        for port in self.forward_ports_abc:
802            cmd = "fport rm tcp:{} tcp:{}".format(
803                port, self.abc_d_port)
804            self.connector_command(cmd)
805        self.forward_ports.clear()
806        self.forward_ports_abc.clear()
807
808    def remove_history_ports(self, port):
809        cmd = "fport ls"
810        res = self.connector_command(cmd, is_print=False)
811        res = res.split("\n")
812        for data in res:
813            if str(port) in data:
814                data = data.split('\t')
815                cmd = "fport rm {}".format(data[0][1:-1])
816                self.connector_command(cmd, is_print=False)
817
818    def take_picture(self, name):
819        '''
820        @summary: 截取手机屏幕图片并保存
821        @param  name: 保存的图片名称,通过getTakePicturePath方法获取保存全路径
822        '''
823        path = ""
824        try:
825            if self._device_report_path is None:
826                from xdevice import EnvPool
827                self._device_report_path = EnvPool.report_path
828            temp_path = os.path.join(self._device_report_path, "temp")
829            if not os.path.exists(temp_path):
830                os.makedirs(temp_path)
831            path = os.path.join(temp_path, name)
832            picture_name = os.path.basename(name)
833            out = self.execute_shell_command("snapshot_display -f /data/local/tmp/{}".format(picture_name))
834            self.log.debug("result: {}".format(out))
835            if "error" in out and "success" not in out:
836                return False
837            else:
838                self.pull_file("/data/local/tmp/{}".format(picture_name), path)
839        except Exception as error:
840            self.log.error("devicetest take_picture: {}".format(str(error)))
841        return path
842
843    def set_device_report_path(self, path):
844        self._device_report_path = path
845
846    def get_device_report_path(self):
847        return self._device_report_path
848
849    def execute_shell_in_daemon(self, command):
850        if self.host != "127.0.0.1":
851            cmd = [HdcHelper.CONNECTOR_NAME, "-s", "{}:{}".format(
852                self.host, self.port), "-t", self.device_sn, "shell"]
853        else:
854            cmd = [HdcHelper.CONNECTOR_NAME, "-t", self.device_sn, "shell"]
855        LOG.debug("{} execute command {} {} in daemon".format(
856            convert_serial(self.device_sn), HdcHelper.CONNECTOR_NAME, command))
857        if isinstance(command, list):
858            cmd.extend(command)
859        else:
860            command = command.strip()
861            cmd.extend(command.split(" "))
862        sys_type = platform.system()
863        process = subprocess.Popen(cmd, stdout=subprocess.PIPE,
864                                   shell=False,
865                                   preexec_fn=None if sys_type == "Windows"
866                                   else os.setsid,
867                                   close_fds=True)
868        return process
869
870    @property
871    def webview(self):
872        from devicetest.controllers.web.webview import WebView
873        if self._webview is None:
874            self._webview = WebView(self)
875        return self._webview
876
877
878class DeviceLogCollector:
879    hilog_file_address = []
880    log_file_address = []
881    device = None
882    restart_proc = []
883    device_log_level = None
884    is_clear = True
885
886    def __init__(self, device):
887        self.device = device
888
889    def _set_device_log_level(self, **kwargs):
890        # set device log level
891        if not self.device_log_level:
892            log_level = kwargs.get("log_level", "INFO")
893            if log_level not in LOGLEVEL:
894                self.device_log_level = "INFO"
895            else:
896                self.device_log_level = log_level
897        cmd = "hilog -b {}".format(self.device_log_level)
898        self.device.execute_shell_command(cmd)
899
900    def restart_catch_device_log(self):
901        self._sync_device_time()
902        for _, path in enumerate(self.hilog_file_address):
903            hilog_open = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_APPEND,
904                                 FilePermission.mode_755)
905            with os.fdopen(hilog_open, "a") as hilog_file_pipe:
906                _, proc = self.start_catch_device_log(hilog_file_pipe=hilog_file_pipe)
907                self.restart_proc.append(proc)
908
909    def stop_restart_catch_device_log(self):
910        # when device free stop restart log proc
911        for _, proc in enumerate(self.restart_proc):
912            self.stop_catch_device_log(proc)
913        self.restart_proc.clear()
914        self.hilog_file_address.clear()
915        self.log_file_address.clear()
916
917    def start_catch_device_log(self, log_file_pipe=None,
918                               hilog_file_pipe=None, **kwargs):
919        """
920        Starts hdc log for each device in separate subprocesses and save
921        the logs in files.
922        """
923        self._sync_device_time()
924        self._set_device_log_level(**kwargs)
925
926        device_hilog_proc = None
927        if hilog_file_pipe:
928            command = "hilog"
929            if self.device.host != "127.0.0.1":
930                cmd = [HdcHelper.CONNECTOR_NAME, "-s", "{}:{}".format(self.device.host, self.device.port),
931                       "-t", self.device.device_sn, command]
932            else:
933                cmd = [HdcHelper.CONNECTOR_NAME, "-t", self.device.device_sn, command]
934            LOG.info("execute command: %s" % " ".join(cmd).replace(
935                self.device.device_sn, convert_serial(self.device.device_sn)))
936            device_hilog_proc = start_standing_subprocess(
937                cmd, hilog_file_pipe)
938        return None, device_hilog_proc
939
940    def stop_catch_device_log(self, proc):
941        """
942        Stops all hdc log subprocesses.
943        """
944        if proc:
945            stop_standing_subprocess(proc)
946            self.device.log.debug("Stop catch device hilog.")
947
948    def start_hilog_task(self, **kwargs):
949        log_size = kwargs.get("log_size", "10M")
950        log_size = log_size.upper()
951        if re.search("^\d+[K?]$", log_size) is None \
952                and re.search("^\d+[M?]$", log_size) is None:
953            self.device.log.debug("hilog task Invalid size string {}. Use default 10M".format(log_size))
954            log_size = "10M"
955        matcher = re.match("^\d+", log_size)
956        if log_size.endswith("K") and int(matcher.group(0)) < 64:
957            self.device.log.debug("hilog task file size should be "
958                                  "in range [64.0K, 512.0M], use min value 64K, now is {}".format(log_size))
959            log_size = "64K"
960        if log_size.endswith("M") and int(matcher.group(0)) > 512:
961            self.device.log.debug("hilog task file size should be "
962                                  "in range [64.0K, 512.0M], use min value 512M, now is {}".format(log_size))
963            log_size = "512M"
964
965        self._sync_device_time()
966        self.is_clear = kwargs.get("is_clear", True)
967        # clear device crash log
968        self.clear_crash_log()
969        # stop hilog task
970        cmd = "hilog -w stop"
971        self.device.execute_shell_command(cmd)
972        # 清空日志
973        cmd = "hilog -r"
974        self.device.execute_shell_command(cmd)
975        cmd = "rm -rf /data/log/hilog/*.gz"
976        # set device log level
977        self._set_device_log_level(**kwargs)
978        # 开始日志任务 设置落盘文件个数最大值1000, 单个文件20M,链接https://gitee.com/openharmony/hiviewdfx_hilog
979        cmd = "hilog -w start -l {} -n 1000".format(log_size)
980        out = self.device.execute_shell_command(cmd)
981        LOG.info("Execute command: {}, result is {}".format(cmd, out))
982        # 开启kmsg日志落盘任务
983        cmd = "hilog -w start -t kmsg -l {} -n 1000".format(log_size)
984        out = self.device.execute_shell_command(cmd)
985        LOG.info("Execute command: {}, result is {}".format(cmd, out))
986
987    def stop_hilog_task(self, log_name, **kwargs):
988        cmd = "hilog -w stop"
989        self.device.execute_shell_command(cmd)
990        module_name = kwargs.get("module_name", None)
991        if module_name:
992            path = "{}/log/{}".format(self.device.get_device_report_path(), module_name)
993        else:
994            path = "{}/log/".format(self.device.get_device_report_path())
995        if not os.path.exists(path):
996            os.makedirs(path)
997        self.device.pull_file("/data/log/hilog/", path)
998        # HDC不支持创建绝对路径,拉取文件夹出来后重命名文件夹
999        try:
1000            new_hilog_dir = "{}/log/{}/hilog_{}".format(self.device.get_device_report_path(), module_name, log_name)
1001            os.rename("{}/log/{}/hilog".format(self.device.get_device_report_path(), module_name), new_hilog_dir)
1002            # 拉出来的文件夹权限可能是650,更改为755,解决在线日志浏览报403问题
1003            os.chmod(new_hilog_dir, FilePermission.mode_755)
1004        except Exception as e:
1005            self.device.log.warning("Rename hilog folder {}_hilog failed. error: {}".format(log_name, e))
1006            # 把hilog文件夹下所有文件拉出来 由于hdc不支持整个文件夹拉出只能采用先压缩再拉取文件
1007            cmd = "cd /data/log/hilog && tar -zcvf /data/log/{}_hilog.tar.gz *".format(log_name)
1008            out = self.device.execute_shell_command(cmd)
1009            LOG.info("Execute command: {}, result is {}".format(cmd, out))
1010            if out is not None and "No space left on device" not in out:
1011                self.device.pull_file("/data/log/{}_hilog.tar.gz".format(log_name), path)
1012                cmd = "rm -f /data/log/{}_hilog.tar.gz".format(log_name)
1013                self.device.execute_shell_command(cmd)
1014        # check if clear log
1015        if self.is_clear:
1016            cmd = "rm -f /data/log/hilog/*.gz"
1017            self.device.execute_shell_command(cmd)
1018        # get crash log
1019        self.start_get_crash_log(log_name, module_name=module_name)
1020        # get extra log
1021        self.pull_extra_log_files(log_name, module_name, kwargs.get("extras_dirs", None))
1022
1023    def _get_log(self, log_path, *params):
1024        def filter_by_name(log_name, args):
1025            for starts_name in args:
1026                if log_name.startswith(starts_name):
1027                    return True
1028            return False
1029
1030        data_list = list()
1031        log_name_array = list()
1032        log_result = self.device.execute_shell_command("ls {}".format(log_path))
1033        if log_result is not None and len(log_result) != 0:
1034            log_name_array = log_result.strip().replace("\r", "").split("\n")
1035        for log_name in log_name_array:
1036            log_name = log_name.strip()
1037            if len(params) == 0 or \
1038                    filter_by_name(log_name, params):
1039                data_list.append("{}/{}".format(log_path, log_name))
1040        return data_list
1041
1042    def start_get_crash_log(self, task_name, **kwargs):
1043        def get_cur_crash_log(local_path, device_path):
1044            if not os.path.exists(local_path):
1045                os.makedirs(local_path)
1046            if "Not support std mode" in device_path:
1047                return
1048
1049            self.device.pull_file(device_path, local_path)
1050            LOG.debug("Finish pull file: %s" % device_path)
1051
1052        module_name = kwargs.get("module_name", None)
1053        log_array = list()
1054
1055        # get crash log
1056        log_array.extend(self._get_log(NATIVE_CRASH_PATH, "cppcrash"))
1057        log_array.extend(self._get_log(JS_CRASH_PATH, "jscrash", "appfreeze", "cppcrash"))
1058        log_array.extend(self._get_log(ROOT_PATH, "SERVICE_BLOCK", "appfreeze"))
1059        LOG.debug("crash log file {}, length is {}".format(str(log_array), str(len(log_array))))
1060        if module_name:
1061            crash_path = "{}/log/{}/crash_log_{}/".format(self.device.get_device_report_path(), module_name, task_name)
1062        else:
1063            crash_path = "{}/log/crash_log_{}/".format(self.device.get_device_report_path(), task_name)
1064        for log_name in log_array:
1065            log_name = log_name.strip()
1066            get_cur_crash_log(crash_path, log_name)
1067
1068    def clear_crash_log(self):
1069        if not self.is_clear:
1070            LOG.debug("No need to clear crash log.")
1071            return
1072
1073        def execute_clear_cmd(path: str, prefix: list):
1074            for pre in prefix:
1075                clear_cmd = "rm -f {}/{}*".format(path, pre)
1076                self.device.execute_shell_command(clear_cmd)
1077
1078        execute_clear_cmd(ROOT_PATH, ["SERVICE_BLOCK", "appfreeze"])
1079        execute_clear_cmd(NATIVE_CRASH_PATH, ["cppcrash"])
1080        execute_clear_cmd(JS_CRASH_PATH, ["jscrash", "appfreeze", "cppcrash"])
1081
1082    def _sync_device_time(self):
1083        # 先同步PC和设备的时间
1084        iso_time_format = '%Y-%m-%d %H:%M:%S'
1085        cur_time = get_cst_time().strftime(iso_time_format)
1086        self.device.execute_shell_command("date '{}'".format(cur_time))
1087
1088    def add_log_address(self, log_file_address, hilog_file_address):
1089        # record to restart catch log when reboot device
1090        if log_file_address:
1091            self.log_file_address.append(log_file_address)
1092        if hilog_file_address:
1093            self.hilog_file_address.append(hilog_file_address)
1094
1095    def remove_log_address(self, log_file_address, hilog_file_address):
1096        if log_file_address and log_file_address in self.log_file_address:
1097            self.log_file_address.remove(log_file_address)
1098        if hilog_file_address and hilog_file_address in self.hilog_file_address:
1099            self.hilog_file_address.remove(hilog_file_address)
1100
1101    def pull_extra_log_files(self, task_name, module_name, dirs: str):
1102        if dirs is None or dirs == 'None':
1103            return
1104        dir_list = dirs.split(";")
1105        if len(dir_list) > 0:
1106            extra_log_path = "{}/log/{}/extra_log_{}/".format(self.device.get_device_report_path(),
1107                                                              module_name, task_name)
1108            if not os.path.exists(extra_log_path):
1109                os.makedirs(extra_log_path)
1110            for dir_path in dir_list:
1111                self.device.pull_file(dir_path, extra_log_path)
1112                # check if delete file
1113                if self.device.is_directory(dir_path):
1114                    clear_cmd = "rm -f {}/*".format(dir_path)
1115                else:
1116                    clear_cmd = "rm -f {}*".format(dir_path)
1117                self.device.execute_shell_command(clear_cmd)