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