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