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