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