Day4.AndroidAudio初始化

1.AudioServer初始化

AudioServer 是 Android 音频系统的核心服务,负责管理音频硬件资源、音频策略调度、跨进程音频通信等核心功能。它由 Init 进程启动,是系统核心服务之一,直接影响音频播放、录音、音效处理等功能的正常运行。

1.1AudioServer服务创建过程

Init 进程通过解析/system/bin/audioserver/audioserver.rc配置文件,完成 AudioServer 的服务定义与启动参数配置。
请添加图片描述

service audioserver /system/bin/audioserver  
    class core  
    user audioserver  
    # media gid needed for /dev/fm (radio) and for /data/misc/media (tee)  
    group audio camera drmrpc media mediadrm net_bt net_bt_admin net_bw_acct wakelock  
    capabilities BLOCK_SUSPEND  
    # match rtprio cur / max with sensor service as we handle AR/VR HID sensor data.  
    rlimit rtprio 10 10  
    ioprio rt 4  
    task_profiles ProcessCapacityHigh HighPerformance  
    onrestart restart vendor.audio-hal  
    onrestart restart vendor.audio-hal-aidl  
    onrestart restart vendor.audio-effect-hal-aidl  
    onrestart restart vendor.audio-hal-4-0-msd  
    onrestart restart audio_proxy_service  
​  
on property:vts.native_server.on=1  
    stop audioserver  
on property:vts.native_server.on=0  
    start audioserver  
​  
on property:init.svc.audioserver=stopped  
    stop vendor.audio-hal  
    stop vendor.audio-hal-aidl  
    stop vendor.audio-effect-hal-aidl  
    stop vendor.audio-hal-4-0-msd  
    stop audio_proxy_service  
    # See b/155364397. Need to have HAL service running for VTS.  
    # Can't use 'restart' because then HAL service would restart  
    # audioserver bringing it back into running state.  
    start vendor.audio-hal  
    start vendor.audio-hal-aidl  
    start vendor.audio-effect-hal-aidl  
    start vendor.audio-hal-4-0-msd  
    start audio_proxy_service  
​  
on property:init.svc.audioserver=running && property:vts.native_server.on=1  
    # See b/378773354. To ensure the audioserver disable when  
    # running test suite, this would cover the double start  
    # request from init that caused test flaky.  
    stop audioserver  
​  
on property:init.svc.audioserver=running  
    start vendor.audio-hal  
    start vendor.audio-hal-aidl  
    start vendor.audio-effect-hal-aidl  
    start vendor.audio-hal-4-0-msd  
    start audio_proxy_service  
​  
on property:sys.audio.restart.hal=1  
    # See b/159966243. Avoid restart loop between audioserver and HAL.  
    # Keep the original service names for backward compatibility  
    stop vendor.audio-hal  
    stop vendor.audio-hal-aidl  
    stop vendor.audio-effect-hal-aidl  
    stop vendor.audio-hal-4-0-msd  
    stop audio_proxy_service  
    start vendor.audio-hal  
    start vendor.audio-hal-aidl  
    start vendor.audio-effect-hal-aidl  
    start vendor.audio-hal-4-0-msd  
    start audio_proxy_service  
    # reset the property  
    setprop sys.audio.restart.hal 0  
    on init  
    mkdir /dev/socket/audioserver 0775 audioserver audioserver
  1. 服务定义

    service audioserver /system/bin/audioserver

    class core

    user audioserver

    • 定义了 audioserver 服务的启动路径 /system/bin/audioserver

    • class core 表示该服务属于核心服务类。

    • user audioserver 指定该服务以 audioserver 用户身份运行,确保权限隔离。

  2. 权限组

    group audio camera drmrpc media mediadrm net_bt net_bt_admin net_bw_acct wakelock

    • 指定了服务所属的权限组,允许它访问音频设备、摄像头、蓝牙功能、网络带宽计费等资源。
  3. 能力与资源限制

    capabilities BLOCK_SUSPEND

    rlimit rtprio 10 10

    ioprio rt 4

    task_profiles ProcessCapacityHigh HighPerformance

    • BLOCK_SUSPEND:允许服务阻止设备进入休眠状态。

    • rlimit rtprioioprio rt:设置实时优先级和 I/O 优先级,确保服务在处理音频任务时具有较高的性能。

    • task_profiles:定义任务性能配置文件,优化服务的运行效率。

  4. 重启行为

    onrestart restart vendor.audio-hal

    onrestart restart vendor.audio-hal-aidl

    onrestart restart vendor.audio-effect-hal-aidl

    onrestart restart vendor.audio-hal-4-0-msd

    onrestart restart audio_proxy_service

    • audioserver 服务重启时,相关的音频 HAL(硬件抽象层)服务也会被重启,确保音频功能的正常运行。
  5. 属性触发器

    on property:vts.native_server.on=1

    stop audioserver

    on property:vts.native_server.on=0

    start audioserver

    • 根据系统属性 vts.native_server.on 的值,控制 audioserver 的启动或停止。这通常用于测试场景。
  6. 停止与启动逻辑

    on property:init.svc.audioserver=stopped

    stop vendor.audio-hal

    stop vendor.audio-hal-aidl

    stop vendor.audio-effect-hal-aidl

    stop vendor.audio-hal-4-0-msd

    stop audio_proxy_service

    start vendor.audio-hal

    start vendor.audio-hal-aidl

    start vendor.audio-effect-hal-aidl

    start vendor.audio-hal-4-0-msd

    start audio_proxy_service

    • audioserver 停止时,相关 HAL 服务也会停止并重新启动,确保系统的稳定性。
  7. 初始化行为

    on init

    mkdir /dev/socket/audioserver 0775 audioserver audioserver

    • 在系统初始化时,创建 /dev/socket/audioserver 目录,并设置权限和所有者。

1.2.AudioServer启动流程

  • audioserver服务通过/system/bin/audioserver可执行文件启动,核心逻辑在main_audioserver.cppmain函数中,流程如下:

    1. 内存限制与信号处理
    • limitProcessMemory:通过audio.maxmem属性或默认值(512MB)限制进程最大内存,避免音频服务过度占用资源。

    • signal(SIGPIPE, SIG_IGN):忽略管道断裂信号(避免音频流中断时进程异常退出)。

    2. 进程分叉
    • 父进程:运行media.log日志服务,监控子进程状态并输出资源使用日志。

    • 子进程:执行音频服务初始化(核心流程),并通过prctl设置父进程死亡时自毁(避免孤儿进程)。

    3. 线程池初始化
    • configureRpcThreadpool(4, false):初始化 HIDL RPC 线程池(4 个线程),用于与音频 HAL(硬件抽象层)进行跨进程通信(HAL 通常运行在独立进程)。

    • ProcessState::self()->startThreadPool():启动 Binder 线程池,处理应用层或其他服务通过 Binder IPC 发起的音频请求(如播放、录音)。

    4. 核心服务创建与注册
    • 创建服务实例

      • sp<AudioFlinger>::make():创建AudioFlinger实例(负责音频硬件管理、混音、音量控制等核心功能)。

      • sp<AudioPolicyService>::make():创建AudioPolicyService实例(负责音频策略决策,如音量策略、设备切换逻辑)。

    • 注册服务

      1. 本地注册:通过AudioSystem::setLocalAudioFlingersetLocalAudioPolicyService将实例注册到进程内(供本地调用)。

      2. 系统服务注册:通过IServiceManager::addService将实例注册到系统服务管理器(servicemanager),供跨进程调用(服务名称分别为IAudioFlingerAudioPolicyService)。

    5. AAudioService 初始化(低延迟音频)
    • 查询系统 MMAP 策略(

      AudioSystem::getMmapPolicyInfos

      若策略为AUTO\ALWAYS(允许内存映射),则初始化AAudioService

      • AAudioService是 Android O 引入的低延迟音频服务,基于 MMAP(内存映射)机制,支持实时音频流(如游戏音效、乐器应用),仅在策略允许时启动。
    6. 完成初始化并进入线程池
    • 记录初始化耗时(startupFinished),通过IPCThreadState::self()->joinThreadPool()进入 Binder 线程池,等待并处理外部音频请求。

frameworks/av/media/audioserver/main_audioserver.cpp

 int main(int argc __unused, char **argv)  
 {  
     const auto startTime = std::chrono::steady_clock::now();  
     limitProcessMemory(  
         "audio.maxmem", /* "ro.audio.maxmem", property that defines limit */  
         (size_t)512 * (1 << 20), /* SIZE_MAX, upper limit in bytes */  
         20 /* upper limit as percentage of physical RAM */);  
 ​  
     signal(SIGPIPE, SIG_IGN);  
 ​  
 #if 1  
     const bool doLog = false;  
 #else  
     bool doLog = (bool) property_get_bool("ro.test_harness", 0);  
 #endif  
     pid_t childPid;  
     if (doLog && (childPid = fork()) != 0) {  
         strcpy(argv[0], "media.log");  
         sp<ProcessState> proc(ProcessState::self());  
         MediaLogService::instantiate();  
         ProcessState::self()->startThreadPool();  
         IPCThreadState::self()->joinThreadPool();  
         for (;;) {  
             siginfo_t info;  
             int ret = TEMP_FAILURE_RETRY(waitid(P_PID, childPid, &info,  
                                                 WEXITED | WSTOPPED | WCONTINUED));  
             if (ret < 0) {  
                 break;  
             }  
             char buffer[32];  
             const char *code;  
             struct rusage usage;  
             getrusage(RUSAGE_CHILDREN, &usage);  
             ALOG(LOG_ERROR, "media.log", "pid %d status %d code %s user %ld.%03lds sys %ld.%03lds",  
                     info.si_pid, info.si_status, code,  
                     usage.ru_utime.tv_sec, usage.ru_utime.tv_usec / 1000,  
                     usage.ru_stime.tv_sec, usage.ru_stime.tv_usec / 1000);  
             sp<IServiceManager> sm = defaultServiceManager();  
             sp<IBinder> binder = sm->getService(String16("media.log"));  
             if (binder != 0) {  
                 Vector<String16> args;  
                 binder->dump(-1, args);  
             }  
             switch (info.si_code) {  
             case CLD_EXITED:  
             case CLD_KILLED:  
             case CLD_DUMPED: {  
                 ALOG(LOG_INFO, "media.log", "exiting");  
                 _exit(0);  
                 // not reached  
                 }  
             default:  
                 break;  
             }  
         }  
     } else {  
         // all other services  
         if (doLog) {  
             prctl(PR_SET_PDEATHSIG, SIGKILL);   // if parent media.log dies before me, kill me also  
             setpgid(0, 0);                      // but if I die first, don't kill my parent  
         }  
         android::hardware::configureRpcThreadpool(4, false /*callerWillJoin*/);  
         ProcessState::self()->startThreadPool();  
         const auto af = sp<AudioFlinger>::make();  
         const auto afAdapter = sp<AudioFlingerServerAdapter>::make(af);  
         ALOGD("%s: AudioFlinger created", __func__);  
         ALOGW_IF(AudioSystem::setLocalAudioFlinger(af) != OK,  
                 "%s: AudioSystem already has an AudioFlinger instance!", __func__);  
         const auto aps = sp<AudioPolicyService>::make();  
         af->initAudioPolicyLocal(aps);  
         ALOGD("%s: AudioPolicy created", __func__);  
         ALOGW_IF(AudioSystem::setLocalAudioPolicyService(aps) != OK,  
                  "%s: AudioSystem already has an AudioPolicyService instance!", __func__);  
 ​  
         // Start initialization of internally managed audio objects such as Device Effects.  
         aps->onAudioSystemReady();  
 ​  
         // Add AudioFlinger and AudioPolicy to ServiceManager.  
         sp<IServiceManager> sm = defaultServiceManager();  
         sm->addService(String16(IAudioFlinger::DEFAULT_SERVICE_NAME), afAdapter,  
                 false /* allowIsolated */, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);  
         sm->addService(String16(AudioPolicyService::getServiceName()), aps,  
                 false /* allowIsolated */, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);  
         std::vector<AudioMMapPolicyInfo> policyInfos;  
         status_t status = AudioSystem::getMmapPolicyInfos(  
                 AudioMMapPolicyType::DEFAULT, &policyInfos);  
         if (status == NO_ERROR &&  
             std::any_of(policyInfos.begin(), policyInfos.end(), [](const auto& info) {  
                     return info.mmapPolicy == AudioMMapPolicy::AUTO ||  
                            info.mmapPolicy == AudioMMapPolicy::ALWAYS;  
             })) {  
             AAudioService::instantiate();  
         } else {  
             ALOGD("%s: Do not init aaudio service, status %d, policy info size %zu",  
                   __func__, status, policyInfos.size());  
         }  
         const auto endTime = std::chrono::steady_clock::now();  
         af->startupFinished();  
         using FloatMillis = std::chrono::duration<float, std::milli>;  
         const float timeTaken = std::chrono::duration_cast<FloatMillis>(  
                 endTime - startTime).count();  
         ALOGI("%s: initialization done in %.3f ms, joining thread pool", __func__, timeTaken);  
         IPCThreadState::self()->joinThreadPool();  
     }  
 }  

请问这个类中的get_current_activity()方法,在其他用例文件,都有那些调用方式,方法如下: # !/usr/bin/env python # -*- coding: utf-8 -*- import sqlite3 from hytest import * from appium import webdriver import sys import os import re import time import platform import subprocess from appium.webdriver.common.multi_action import MultiAction from appium.webdriver.common.touch_action import TouchAction from selenium.webdriver.common.by import By from appium.webdriver.common.appiumby import AppiumBy from multiprocessing.dummy import Process from selenium.webdriver import ActionChains import yaml, ruamel.yaml import hytest ############################################################################### def get_devices(): """ 获取所有安卓设备的SN号: ['B7EDU17626000398', 'B7EDU17626000233'] """ devices_list = [] str_list = os.popen('adb devices').readlines() # print(str_list) count = 0 for i in str_list: if '\tdevice' in i: device_name = '' device_name = re.sub('\tdevice', '', i).replace('\n', '').strip() # print("Device_{}_name={}".format(count, device_name)) devices_list.append(device_name) count = count + 1 # print("devices={},devices_list={}".format(count,devices_list)) return devices_list ############################################################################### # def get_sn(): # sn = get_devices()[0] # return sn # # udid = get_sn() # sup_udid = "" # host_port,sup_port = 4723,4800 # 从配置文件中获取sn和分配的端口号 device_info_path = os.path.abspath('./conf/device_info.yaml') with open(device_info_path, 'r', encoding='utf-8') as f: device_info = yaml.load(f, yaml.Loader) udid = device_info['当前项目设备详情']['host'] sup_udid = device_info['当前项目设备详情']['sup'] host_port = device_info['端口分配情况'][udid] if sup_udid == '': sup_port = '' else: sup_port = device_info['端口分配情况'][sup_udid] def get_app_driver(package, activity, sn=udid, noreset=True): desired_caps = { 'platformName': 'Android', # 被测手机是安卓 'platformVersion': ADBHelper().get_android_version(), # 手机安卓版本 'deviceName': ADBHelper().get_product_model(), # 设备名,安卓手机可以随意填写 'appPackage': package, # 启动APP Package名称 'appActivity': activity, # 启动Activity名称 'unicodeKeyboard': True, # 使用自带输入法,输入中文时填True 'resetKeyboard': False, # 执行完程序恢复原来输入法 'noReset': noreset, # 不要重置App 'newCommandTimeout': 12000, 'automationName': 'UiAutomator2', 'appWaitForLaunch': False, # 是否等待活动管理器将控制权返回给调用进程。 'udid': sn # 'app': r'd:\apk\bili.apk', } port = '' if sn == udid: port = host_port elif sn == sup_udid: port = sup_port # 连接Appium Server,初始化自动化环境 driver = webdriver.Remote('http://localhost' + ':' + str(port) + '/wd/hub', desired_caps) driver.implicitly_wait(4) time.sleep(2) return driver def get_sup_app_driver(package, activity, noreset=True): desired_caps = { 'platformName': 'Android', # 被测手机是安卓 'platformVersion': '12', # 手机安卓版本 'deviceName': 'ASN-AN00', # 设备名,安卓手机可以随意填写 'appPackage': package, # 启动APP Package名称 'appActivity': activity, # 启动Activity名称 'unicodeKeyboard': True, # 使用自带输入法,输入中文时填True 'resetKeyboard': True, # 执行完程序恢复原来输入法 'noReset': noreset, # 不要重置App 'newCommandTimeout': 6000, 'automationName': 'UiAutomator2', 'udid': sup_udid # 'app': r'd:\apk\bili.apk', } # 连接Appium Server,初始化自动化环境 driver = webdriver.Remote('http://localhost' + ':' + str(sup_port) + '/wd/hub', desired_caps) driver.implicitly_wait(3) return driver def get_third_app_driver(package, activity, sn, noreset=True): desired_caps = { 'platformName': 'Android', # 被测手机是安卓 'platformVersion': ADBHelper().get_android_version(), # 手机安卓版本 'deviceName': ADBHelper().get_product_model(), # 设备名,安卓手机可以随意填写 'appPackage': package, # 启动APP Package名称 'appActivity': activity, # 启动Activity名称 'unicodeKeyboard': True, # 使用自带输入法,输入中文时填True 'resetKeyboard': True, # 执行完程序恢复原来输入法 'noReset': noreset, # 不要重置App 'newCommandTimeout': 12000, 'automationName': 'UiAutomator2', 'udid': sn # 'app': r'd:\apk\bili.apk', } port = '' if sn == udid: port = host_port elif sn == sup_udid: port = sup_port else: port = sup_port + 5 # 连接Appium Server,初始化自动化环境 driver = webdriver.Remote('http://localhost' + ':' + str(port) + '/wd/hub', desired_caps) driver.implicitly_wait(5) return driver def uninstall_appium(sn=udid): ADBHelper(sn).adb('uninstall io.appium.uiautomator2.server.test') ADBHelper(sn).adb('uninstall io.appium.uiautomator2.server') ADBHelper(sn).adb('uninstall io.appium.settings') def catch_all_logs(): desti_path = os.path.abspath('./log/device_logs') ADBHelper().pull_file('/data/log/android_logs', desti_path) ADBHelper().pull_file('/data/log/hilogs', desti_path) ADBHelper().pull_file('/data/system/dropbox', desti_path) ADBHelper().pull_file('/data/anr', desti_path) ADBHelper().pull_file('/data/log/bbox', desti_path) ADBHelper().pull_file('/data/log/charge-log', desti_path) ADBHelper().pull_file('/log/reliability/boot_fail', desti_path) ADBHelper().pull_file('/data/log/reliability/xcollie', desti_path) ADBHelper().pull_file('/data/log/faultlog', desti_path) ADBHelper().pull_file('/data/log/LogService/901/done', desti_path) ADBHelper().pull_file('/data/log/LogService/901/uploading', desti_path) ADBHelper().pull_file('/data/log/reliability/resource_leak', desti_path) ADBHelper().pull_file('/data/log/dropbox', desti_path) ADBHelper().pull_file('/log/recovery', desti_path) ADBHelper().pull_file('/data/hisi_logs', desti_path) ADBHelper().pull_file('/data/tombstones', desti_path) ADBHelper().pull_file('/data/vendor/log/mtklog', desti_path) ADBHelper().pull_file('/data/log/sleeplog', desti_path) ADBHelper().pull_file('/data/log/jank', desti_path) Global = {} ################################################################################################## # 开关相关的类 swith_sn = [''] class Switch: def __init__(self,sn=udid): self.sn = sn swith_sn[0] = self.sn class BT: @classmethod def open(cls): ADBHelper(swith_sn[0]).shell('svc bluetooth enable') @classmethod def close(cls): ADBHelper(swith_sn[0]).shell('svc bluetooth disable') class WIFI: @classmethod def open(cls): ADBHelper(swith_sn[0]).shell('svc wifi enable') @classmethod def close(cls): ADBHelper(swith_sn[0]).shell('svc wifi disable') class DATA: @classmethod def open(cls): ADBHelper(swith_sn[0]).shell('svc data enable') @classmethod def close(cls): ADBHelper(swith_sn[0]).shell('svc data disable') class NFC: @classmethod def open(cls): ADBHelper(swith_sn[0]).shell('svc nfc enable') @classmethod def close(cls): ADBHelper(swith_sn[0]).shell('svc nfc disable') class Airplane_Mode: @classmethod def open(cls): airplanemode = get_app_driver("com.android.settings", "com.android.settings.Settings$WirelessSettingsActivity",sn=swith_sn[0]) switch_button = airplanemode.find_element(By.ID, 'android:id/switch_widget') if switch_button.get_attribute('checked') == 'false': switch_button.click() time.sleep(1) else: return # ADBHelper().shell('settings put global airplane_mode_on 1') @classmethod def close(cls): airplanemode = get_app_driver("com.android.settings", "com.android.settings.Settings$WirelessSettingsActivity",sn=swith_sn[0]) switch_button = airplanemode.find_element(By.ID, 'android:id/switch_widget') if switch_button.get_attribute('checked') == 'true': switch_button.click() time.sleep(1) else: return # ADBHelper().shell('settings put global airplane_mode_on 0') class Display_auto: @classmethod def open(cls): ADBHelper(swith_sn[0]).shell('settings put system screen_brightness_mode 1') @classmethod def close(cls): ADBHelper(swith_sn[0]).shell('settings put system screen_brightness_mode 0') class GPS: @classmethod def open(cls): driver = get_app_driver('com.android.settings', 'com.android.settings.Settings$LocationSettingsActivity',sn=swith_sn[0]) GPS_switch_button = driver.find_element(By.ID, 'android:id/switch_widget') if GPS_switch_button.get_attribute('checked') == 'false': GPS_switch_button.click() try: driver.find_element(By.ID, 'android:id/button1').click() driver.find_element(By.ID, 'android:id/button1').click() except: pass else: return True @classmethod def close(cls): driver = get_app_driver('com.android.settings', 'com.android.settings.Settings$LocationSettingsActivity',sn=swith_sn[0]) GPS_switch_button = driver.find_element(By.ID, 'android:id/switch_widget') if GPS_switch_button.get_attribute('checked') == 'true': GPS_switch_button.click() else: return True ########################################################################################## # 手机操作相关的类 operate_sn = [''] class Operate: def __init__(self,sn=udid): self.sn = sn operate_sn[0] = self.sn # 锁屏 @classmethod def lock_screen(cls): ADBHelper(operate_sn[0]).shell("input keyevent 26") # 解锁 @classmethod def unlock_screen(cls): ADBHelper(operate_sn[0]).shell("input keyevent 82") ADBHelper(operate_sn[0]).shell("wm dismiss-keyguard") # 返回 @classmethod def go_back(cls): ADBHelper(operate_sn[0]).shell('input keyevent 4') # home键 @classmethod def go_home(cls): ADBHelper(operate_sn[0]).shell('input keyevent 3') # 清除后台 @classmethod def clear(cls): ADBHelper(operate_sn[0]).go_home() x_size, y_size = ADBHelper().get_phone_size() ADBHelper(operate_sn[0]).shell(f"input tap {0.7083 * x_size} {0.978125 * y_size}") time.sleep(1) ADBHelper(operate_sn[0]).shell(f"input tap {0.5 * x_size} {0.90625 * y_size}") # 双击元素 @classmethod def double_click_by_ele(cls, driver, ele): ActionChains(driver).double_click(on_element=ele) # 双击坐标 @classmethod def double_click_by_location(cls, x_ratio, y_ratio): phone_x, phone_y = ADBHelper(operate_sn[0]).get_phone_size() ADBHelper(operate_sn[0]).shell(f'input tap {phone_x * x_ratio} {phone_y * y_ratio}') ADBHelper(operate_sn[0]).shell(f'input tap {phone_x * x_ratio} {phone_y * y_ratio}') # 滑动 class Swipe: @classmethod def up(cls): x, y = ADBHelper(operate_sn[0]).get_phone_size() ADBHelper(operate_sn[0]).shell(f'input swipe {x / 2} {5 * y / 8} {x / 2} {y / 4}') @classmethod def down(cls): x, y = ADBHelper(operate_sn[0]).get_phone_size() ADBHelper(operate_sn[0]).shell(f'input swipe {x / 2} {y / 4} {x / 2} {5 * y / 8}') @classmethod def left(cls): x, y = ADBHelper(operate_sn[0]).get_phone_size() ADBHelper(operate_sn[0]).shell(f'input swipe {3 * x / 4} {y / 2} {x / 4} {y / 2}') @classmethod def right(cls): x, y = ADBHelper(operate_sn[0]).get_phone_size() ADBHelper(operate_sn[0]).shell(f'input swipe {x / 4} {y / 2} {3 * x / 4} {y / 2}') # 自定义滑动 @classmethod def customize_swipe(self,start_x_ratio,start_y_ratio,end_x_ratio,end_y_ratio,swipe_times=1): """ start_x_ratio:滑动起始位置的x坐标占手机宽度的比例 start_y_ratio:滑动起始位置的y坐标占手机长度的比例 end_x_ratio: 滑动结束位置的x坐标占手机宽度的比例 end_y_ratio:滑动结束位置的y坐标占手机长度的比例 swipe_times: 滑动次数,默认为1 """ x, y = ADBHelper(operate_sn[0]).get_phone_size() for i in range(swipe_times): ADBHelper(operate_sn[0]).shell(f'input swipe {x*start_x_ratio} {y*start_y_ratio} {x*end_x_ratio} {y*end_y_ratio}') time.sleep(0.5) # 通过坐标点击 @classmethod def click_by_location(cls, x_ratio, y_ratio): phone_x, phone_y = ADBHelper(operate_sn[0]).get_phone_size() ADBHelper(operate_sn[0]).shell(f'input tap {phone_x * x_ratio} {phone_y * y_ratio}') # 提升屏幕亮度 @classmethod def increase_brightness(cls): ADBHelper(operate_sn[0]).shell("input keyevent 221") ADBHelper(operate_sn[0]).go_back() # 降低屏幕亮度 @classmethod def decrease_brightness(cls): ADBHelper(operate_sn[0]).shell("input keyevent 220") ADBHelper(operate_sn[0]).go_back() # 媒体音量调节 @classmethod def adjust_Volume(cls, type, operation): type_code_dict = { 'call': 0, 'ring': 2, 'media': 3, 'alarm': 4, 'assistant': 11 } if type in type_code_dict: if operation == 'increase': ADBHelper().shell(f'service call audio 9 i32 {type_code_dict[type]} i32 1 i32 1') if operation == 'decrease': ADBHelper().shell(f'service call audio 9 i32 {type_code_dict[type]} i32 -1 i32 1') if operation == 'mute': ADBHelper().shell(f'service call audio 9 i32 {type_code_dict[type]} i32 -100 i32 1') if operation == 'cancel_mute': ADBHelper().shell(f'service call audio 9 i32 {type_code_dict[type]} i32 100 i32 1') else: print("声音type参数错误!") # 设置音量 @classmethod def set_Volume(cls, type, level): type_code_dict = { 'call': 0, 'ring': 1, 'media': 3, 'alarm': 4, 'assistant': 11 } if type in type_code_dict: ADBHelper().shell(f'service call audio 10 i32 {type_code_dict[type]} i32 {level} i32 1') # 对一个元素进行长按 @classmethod def long_press(cls, driver, ele, duration=1000): TouchAction(driver).long_press(ele, duration=duration).release().perform() # 将一个元素拖拽到另外一个元素位置 @classmethod def drag_ele_to_ele(cls, driver, ele1, ele2): ActionChains(driver).click_and_hold(ele1).perform() ActionChains(driver).drag_and_drop(ele1, ele2).perform() # 将一个元素缓慢拖拽到另外一个元素位置(无惯性) @classmethod def drag_element_to_another_slowly(cls,driver, source_element, target_element, long_press_duration=2, steps=10): """ 长按源元素并缓慢拖动到目标元素位置 参数: driver: Appium驱动实例 source_element: 需要拖动的源元素 target_element: 目标位置的元素 long_press_duration: 长按时间(秒),默认2秒 steps:步数越多,拖动越慢越平滑 """ # 获取源元素的中心位置 source_location = source_element.location source_size = source_element.size start_x = source_location['x'] + source_size['width'] / 2 start_y = source_location['y'] + source_size['height'] / 2 # 获取目标元素的中心位置 target_location = target_element.location target_size = target_element.size target_x = target_location['x'] + target_size['width'] / 2 target_y = target_location['y'] + target_size['height'] / 2 # 计算需要移动的总距离 delta_x = target_x - start_x delta_y = target_y - start_y # 设置分阶段移动的步数 # 创建TouchAction实例 action = TouchAction(driver) # 执行长按操作 action.long_press(x=start_x, y=start_y, duration=long_press_duration * 1000) # 分阶段缓慢移动 for i in range(steps): # 计算当前步骤需要移动的绝对坐标 current_x = int(start_x + (i + 1) * delta_x / steps) current_y = int(start_y + (i + 1) * delta_y / steps) # 移动到指定坐标 if i == 0: # 第一步使用press操作(避免某些设备上的兼容性问题) action.press(x=current_x, y=current_y) else: # 后续步骤使用move_to action.move_to(x=current_x, y=current_y) # 释放并执行整个操作链 action.release().perform() # 将一个元素拖拽到坐标位置(有惯性) @classmethod def drag_ele_by_offset(cls, driver, ele, x_ratio, y_ratio): x, y = ADBHelper(operate_sn[0]).get_phone_size() ActionChains(driver).click_and_hold(ele).perform() ActionChains(driver).drag_and_drop_by_offset(ele, x * x_ratio, y * y_ratio).perform() @classmethod # 将一个元素缓慢拖拽到坐标位置(无惯性) def drag_ele_by_offset_slowly(cls,driver, element, x_ratio, y_ratio, long_press_duration=2, steps=10): """ 长按元素并缓慢拖动到指定坐标 参数: driver: Appium驱动实例 element: 需要长按的元素 target_x: 目标位置的x坐标 target_y: 目标位置的y坐标 long_press_duration: 长按时间(秒),默认2秒 steps:步数越多,拖动越慢越平滑 """ # 获取元素当前位置 location = element.location start_x = location['x'] start_y = location['y'] x, y = ADBHelper().get_phone_size() target_x, target_y = x * x_ratio, y * y_ratio # 计算需要移动的总距离 delta_x = target_x - start_x delta_y = target_y - start_y # 设置分阶段移动的步数 # 创建TouchAction实例 action = TouchAction(driver) # 执行长按操作 action.long_press(x=start_x, y=start_y, duration=long_press_duration * 1000) # 分阶段缓慢移动(相对于起始点的偏移量) for i in range(steps): # 计算当前步骤需要移动的相对偏移量 current_x = int(start_x + (i + 1) * delta_x / steps) current_y = int(start_y + (i + 1) * delta_y / steps) # 移动到指定坐标(使用相对偏移) if i == 0: # 第一步使用press操作(避免某些设备上的兼容性问题) action.press(x=current_x, y=current_y) else: # 后续步骤使用move_to(相对于上一个位置) action.move_to(x=current_x, y=current_y) # 释放并执行整个操作链 action.release().perform() # 下拉进入控制中心 @classmethod def getdown_ControlCenter(cls): x, y = ADBHelper(operate_sn[0]).get_phone_size() ADBHelper(operate_sn[0]).shell(f'input swipe {3 / 4 * x} 0 {3 / 4 * x} {y / 2} ') @classmethod # 下拉通知栏 def getdown_NotificationBar(cls, driver): driver.open_notifications() @classmethod def set_Virtual_navigation(cls): driver = get_app_driver('com.android.settings', 'com.android.settings.HWSettings',sn=operate_sn[0]) for i in range(3): ADBHelper().swipe('up') try: driver.find_element(By.XPATH, '//android.widget.Button[@text="下一步"]').click() except: pass try: driver.find_element(By.XPATH, '//android.widget.Button[@text="知道了"]').click() except: pass try: driver.find_element(By.XPATH, '//android.widget.Button[@text="同意"]').click() except: pass for i in range(3): ADBHelper().swipe('up') time.sleep(1) driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, ('new UiSelector().text("系统和更新")')).click() driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, ('new UiSelector().text("系统导航方式")')).click() # 选择虚拟导航 navradios = driver.find_elements(AppiumBy.CLASS_NAME, 'android.widget.RadioButton') navradios[1].click() driver.quit() def multi_finger(driver, operation): # 设置MultiAction,可以多点触控 multiaction = MultiAction(driver) t1 = TouchAction(driver) t2 = TouchAction(driver) t3 = TouchAction(driver) if operation == 'enlarge' or operation == 'narrow': if operation == 'enlarge': # t1.press(x=330, y=745).move_to(x=260, y=667).wait(100).release() # t2.press(x=407, y=880).move_to(x=425, y=985).wait(100).release() t1.press(x=500, y=1100).move_to(x=500, y=500).wait(100).release() t2.press(x=500, y=1300).move_to(x=500, y=1900).wait(100).release() else: t1.press(x=260, y=667).move_to(x=330, y=745).wait(100).release() t2.press(x=425, y=985).move_to(x=407, y=880).wait(100).release() # 多点操作对象加上这俩操作 multiaction.add(t1) multiaction.add(t2) if operation == 'screenshot': t1.press(x=180, y=600).move_to(x=180, y=800).wait(1000).release() t2.press(x=360, y=600).move_to(x=360, y=800).wait(1000).release() t3.press(x=600, y=600).move_to(x=600, y=800).wait(1000).release() multiaction.add(t1) multiaction.add(t2) multiaction.add(t3) multiaction.perform() def build_port(port): ADBHelper().execute_command(f'appium -p {port} --session-override') def start_appium(ports): ADBHelper().adb('forward --remove-all') ADBHelper().execute_command(f'adb -s {sup_udid} forward --remove-all') # 构建desired进程组 desired_processes = [] # 加载desired进程 for port in ports: # target="调用的方法",args="传入的参数" desired = Process(target=build_port, args=(port,)) desired_processes.append(desired) # 存储到全局变量 GSTORE['process'] = desired_processes # 开启每个进程 for desired in desired_processes: desired.start() def end_appium(ports): desired_processes = GSTORE['process'] for desired in desired_processes: desired.join() for p in ports: info = ADBHelper().execute_command_with_output(f'netstat -ano | findstr "{p}"') time.sleep(10) print(info) pid = info.splitlines()[0].split(' ')[-1] if pid != 0: wait = 0 r = ADBHelper().execute_command(f'taskkill /pid {pid} /F') while True: if wait <= 60: if r is not True: time.sleep(1) wait += 1 continue else: break else: print('杀appium进程失败!') break def handle_usb_promot(sn=udid): INFO('处理usb连接弹框') ADBHelper().go_home() driver = get_app_driver('com.huawei.android.launcher', 'com.huawei.android.launcher.unihome.UniHomeLauncher',sn) time.sleep(2) ADBHelper().go_home() try: driver.find_element(By.XPATH, '//android.widget.CheckedTextView[@text="仅充电"]').click() except: pass ADBHelper().go_home() return driver def first_getdown_controller(sn=udid): driver = get_app_driver('com.huawei.android.launcher', 'com.huawei.android.launcher.unihome.UniHomeLauncher',sn) x, y = ADBHelper(sn).get_phone_size() # 处理通知栏弹框 noti_count = 0 while noti_count < 2: noti_sign = driver.find_elements(By.ID, 'com.android.systemui:id/header_clock') if noti_sign != []: break else: ADBHelper(sn).shell(f'input swipe {1 / 4 * x} 0 {1 / 4 * x} {y / 4} ') noti_count += 1 # 处理控制中心 control_count = 0 while control_count < 2: control_sign = driver.find_elements(By.ID, 'com.android.systemui:id/settings_button') if control_sign != []: break else: ADBHelper(sn).shell(f'input swipe {3 / 4 * x} 0 {3 / 4 * x} {y / 4} ') control_count += 1 # 处理左右滑动 ADBHelper(sn).shell(f'input swipe {1 / 8 * x} {y / 10} {3 / 4 * x} {y / 10} ') button = driver.find_elements(By.XPATH, '//*[@text="知道了"]') if button != []: button[0].click() ADBHelper(sn).go_home() # 手动设置时间(hm2.0手机插白卡获取时间出错,需设置成手动) def manually_set_time(sn=udid): driver = get_app_driver('com.android.settings', 'com.android.settings.Settings$HwSystemDashboardActivity',sn) swipe_find_element(driver, '日期和时间',sn).click() switch = driver.find_element(By.XPATH, '//*[@text="自动设置"]/ancestor::*[@resource-id="com.android.settings:id/card_area"]//android.widget.Switch') if switch.get_attribute('checked') == 'true': switch.click() else: switch.click() time.sleep(2) switch.click() # 判断时间 now_time = datetime.now() now_year, now_month, now_day = str(now_time.year), str(now_time.month), str(now_time.day) ui_time = driver.find_elements(By.XPATH, '//*[@text="日期"]/../..//android.widget.TextView')[1].text year = ui_time.split('年')[0] month = ui_time.split('年')[1].split('月')[0] day = ui_time.split('年')[1].split('月')[1].split('日')[0] if not (now_year == year and now_month == month and now_day == day): driver.find_element(By.XPATH, '//*[@text="日期"]/ancestor::*[@resource-id="com.android.settings:id/card_area"]//*[@resource-id="com.android.settings:id/arrow"]').click() if now_year != year: count = 1 while count <= 5: year = driver.find_element(By.ID,'com.android.settings:id/hwadvancednumberpicker_textview').get_attribute( 'content-desc').split('年')[0] if int(year) < int(now_year): driver.find_element(By.ID,'com.android.settings:id/hwadvancednumberpicker_decrement').click() elif int(year) > int(now_year): driver.find_element(By.ID, 'com.android.settings:id/hwadvancednumberpicker_increment').click() else: break count += 1 if now_month != month: count = 1 while count <= 5: month = \ driver.find_elements(By.ID, 'com.android.settings:id/hwadvancednumberpicker_textview')[1].get_attribute( 'content-desc').split('月')[0] if int(month) < int(now_month): driver.find_elements(By.ID,'com.android.settings:id/hwadvancednumberpicker_decrement')[1].click() elif int(month) > int(now_month): driver.find_elements(By.ID, 'com.android.settings:id/hwadvancednumberpicker_increment')[1].click() else: break count += 1 if now_day != day: count = 1 while count <= 5: day = \ driver.find_elements(By.ID, 'com.android.settings:id/hwadvancednumberpicker_textview')[2].get_attribute( 'content-desc').split('日')[0] if int(day) < int(now_day): driver.find_elements(By.ID,'com.android.settings:id/hwadvancednumberpicker_decrement')[2].click() elif int(day) > int(now_day): driver.find_elements(By.ID, 'com.android.settings:id/hwadvancednumberpicker_increment')[2].click() else: break count += 1 driver.find_element(By.ID, 'com.android.settings:id/hwdatepicker_dialog_positive_btn').click() else: ADBHelper(sn).go_home() def swipe_find_element(driver, name,sn=udid,circle=5): x, y = ADBHelper(sn).get_phone_size() selector = 'new UiSelector().text("%s")' % name elements = driver.find_elements(AppiumBy.ANDROID_UIAUTOMATOR, selector) swipe = 0 while len(elements) < 1 and swipe < circle: Operate(sn).Swipe.up() elements = driver.find_elements(AppiumBy.ANDROID_UIAUTOMATOR, selector) swipe += 1 swipe = 0 while len(elements) < 1 and swipe < circle: Operate(sn).Swipe.down() elements = driver.find_elements(AppiumBy.ANDROID_UIAUTOMATOR, selector) swipe += 1 if len(elements) >= 1: return elements[0] else: INFO(name + ' not found') return None def check_element(driver, list,sn=udid): for name in list: find = swipe_find_element(driver, name,sn) if find is None: return False return True def add_APN(sn=udid): driver = get_app_driver('com.android.phone', 'com.android.phone.MSimMobileNetworkSettings',sn) apn_eles = driver.find_elements(By.XPATH, '//android.widget.TextView[@text="接入点名称 (APN)"]') # 新建第一个APN apn_eles[0].click() if len(driver.find_elements(By.XPATH, '//android.widget.TextView[@text="huawei.com"]')) == 2: ADBHelper(sn).go_home() return else: driver.find_element(By.ID, 'androidhwext:id/action_menu_more_button').click() driver.find_element(By.XPATH, '//android.widget.TextView[@text="新建 APN"]').click() time.sleep(1) # 输入名称和apn for part in ['名称','APN']: driver.find_element(By.XPATH, f'//android.widget.TextView[@text="{part}"]').click() driver.find_element(By.ID, 'android:id/edit').clear().send_keys('huawei.com') driver.find_element(By.ID, 'android:id/button1').click() driver.find_element(By.ID, 'android:id/icon2').click() driver.find_element(By.ID, 'com.android.settings:id/apn_radiobutton').click() ADBHelper(sn).go_back() # 开启卡2的APN apn_eles = driver.find_elements(By.XPATH, '//android.widget.TextView[@text="接入点名称 (APN)"]') apn_eles[1].click() driver.find_element(By.ID, 'com.android.settings:id/apn_radiobutton').click() ADBHelper(sn).go_home() # =============================================================================== # 命令跳过开机导航 def skip_guide_by_command(sn=udid): # ADBHelper(sn).adb("wait-for-device") # ADBHelper(sn).execute_command(f'adb -s {sn} remount') # ADBHelper(sn).shell("cp -r /eng/system/* /system/") # ADBHelper(sn).shell("cp -r /eng/vendor/* /vendor/") ADBHelper(sn).shell("settings put secure hw_suw_frp_state 0") ADBHelper(sn).shell("pm disable com.huawei.hwstartupguide") # ADBHelper(sn).shell("pm disable com.google.android.setupwizard") # ADBHelper(sn).shell("setting put global device_provisioned 1") # ADBHelper(sn).shell("setting put secure user_setup_complete 1") return True # 操作点击跳过开机导航 def skip_guide_by_click(sn=udid): try: driver = get_app_driver('com.huawei.hwstartupguide', 'com.huawei.hwstartupguide.LanguageSelectActivity', sn) time.sleep(10) INFO('开始启用') driver.find_element(By.ID, 'com.huawei.hwstartupguide:id/next_btn').click() time.sleep(1) INFO('选择地区') driver.find_element(By.ID, 'com.huawei.hwstartupguide:id/continue_btn').click() time.sleep(1) INFO('同意协议与声明') driver.find_element(By.ID, 'com.huawei.hwstartupguide:id/continue_btn').click() time.sleep(1) INFO('数据与隐私') driver.find_element(By.ID, 'com.huawei.hwstartupguide:id/continue_btn').click() time.sleep(1) INFO('插卡提醒跳过') driver.find_element(By.ID, 'com.huawei.hwstartupguide:id/skip_btn').click() time.sleep(5) INFO('wifi') wifi_name = 'ldb24_5G_2' wifi_pwd = 'Hy@1234%' x, y = ADBHelper().get_phone_size() # ADBHelper().shell(f'input swipe {0.5 * x} {0.5 * y} {0.5 * x} {0.8 * y}') # time.sleep(3) for i in range(5): if not driver.find_elements(By.XPATH,f'//*[@text="{wifi_name}"]'): ADBHelper().shell(f'input swipe {0.5 * x} {0.625 * y} {0.5 * x} {0.416 * y}') time.sleep(0.5) continue else: driver.find_element(By.XPATH, f'//*[@text="{wifi_name}"]').click() break driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().className("android.widget.EditText")').send_keys(wifi_pwd) driver.find_element(By.ID, 'com.android.settings:id/btn_wifi_connect').click() time.sleep(5) driver.find_element(By.ID, 'com.android.settings:id/next_btn').click() time.sleep(5) INFO('设备保护') driver.find_element(By.ID, 'com.huawei.hwstartupguide:id/skip_btn').click() time.sleep(2) INFO('钱包') try: driver.find_element(By.ID, 'com.huawei.wallet:id/oobe_add_bank_card').click() except: pass time.sleep(1) INFO('增强服务') driver.find_element(By.ID, 'com.huawei.hwstartupguide:id/continue_btn').click() time.sleep(1) INFO('智慧助手') try: driver.find_element(By.ID, 'com.huawei.vassistant:id/btn_highlight').click() except: pass time.sleep(1) INFO('数据导入') driver.find_element(By.ID, 'com.huawei.hicloud:id/set_new_device_lay').click() time.sleep(2) INFO('手势导航') driver.find_element(By.ID, 'com.android.settings:id/gesture_study_finish').click() time.sleep(10) INFO('天翼云盘') try: driver.find_element(By.ID, 'com.cn21.ecloud:id/start_mm_login_skip').click() except: pass time.sleep(10) INFO('开机导航跳过完成') except: INFO("开机导航界面点击失败,命令跳过") skip_guide_by_command() pass def SetDevelopmentSettingMode(sn=udid): ap = get_app_driver("com.android.settings", "com.android.settings.HWSettings",sn=sn,noreset=False) time.sleep(3) swipe_find_element(ap, '关于手机',sn=sn).click() time.sleep(2) for i in range(8): release = ap.find_elements(By.ID, 'android:id/title')[3] # try: # release = ap.find_elements(By.ID, 'android:id/title')[3] # except: # release = ap.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("软件版本")') # else: # pass release.click() time.sleep(1) ap.quit() def device_reset(sn=udid): """ 手机初始化 手机恢复出厂并将手机环境恢复到执行完测试_st_的环境(各领域st未执行,请自行恢复) """ driver_bar = get_app_driver('com.android.settings', 'com.android.settings.Settings$HwSystemDashboardActivity', sn=sn, noreset=False) driver_bar.find_element(By.XPATH, '//*[@text="重置"]').click() driver_bar.find_element(By.XPATH, '//*[@text="恢复出厂设置"]').click() count = 0 while count < 5: if driver_bar.find_element(By.XPATH, '//*[@text="重置手机"]').get_attribute("enabled") == "false": Operate(sn).Swipe().up() count = count + 1 else: break driver_bar.find_element(By.XPATH, '//*[@text="重置手机"]').click() driver_bar.find_element(By.ID, 'com.android.settings:id/execute_master_clear_new').click() time.sleep(60) # 等待手机恢厂完成adb上线 ADBHelper(sn).wait_device_online(max_delay=600) device_setup(sn) def device_setup(sn=udid,add_apn=True): # 跳过开机导航,恢复手机测试设置 time.sleep(10) skip_guide_by_command(sn) time.sleep(10) ADBHelper(sn).unlock_screen() time.sleep(5) ADBHelper(sn).shell('settings put system screen_off_timeout 600000') ADBHelper(sn).shell('settings put system screen_brightness_mode 0') ADBHelper(sn).shell('settings put system screen_brightness 128') try: driver = handle_usb_promot(sn) except: time.sleep(5) driver = handle_usb_promot(sn) time.sleep(60) try: driver.find_element(By.XPATH, '//android.widget.Button[@text="同意"]').click() time.sleep(1) driver.find_element(By.ID, 'android:id/button1').click() time.sleep(1) driver.find_element(By.ID, 'com.huawei.hiai:id/main_switch_ohos').click() except: pass time.sleep(30) Operate(sn).set_Virtual_navigation() SetDevelopmentSettingMode(sn) Switch(sn).GPS.open() Operate(sn).clear() if add_apn: add_APN(sn) first_getdown_controller(sn) manually_set_time(sn) if platform.system() != 'Windows': QUOTATION_MARKS = "'" else: QUOTATION_MARKS = "\"" USB_TOOLS = "adb" def device_screenshot(sn=udid): """ 截图(用于定位问题) :param sn: 手机SN号 :return: 截图路径 """ info_date = datetime.now().strftime('%Y%m%d%H%M%S') picture_name = "%s.png" % info_date ADBHelper(sn).shell('screencap /sdcard/%s' % picture_name) picture_save_path = os.path.abspath(r'./log/screen_shots') if not picture_save_path: os.mkdir(picture_save_path) ADBHelper(sn).pull(src_path="/sdcard/%s" % picture_name, dst_path=picture_save_path) time.sleep(1) ADBHelper(sn).shell('rm /sdcard/%s' % picture_name) return fr'{picture_save_path}\{picture_name}' # 重构的检查点函数——fail项不退出,继续进行测试,并打印失败时间点 def check_point(str, condition,driver,fail_timestamp): if hytest.common.CHECK_POINT(str, condition,failStop=False,LogSreenWebDriverIfFail=driver) == 'false': timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) INFO(f'失败时间点:{timestamp}') fail_timestamp.append(timestamp) # wifi 压测需要用到的函数 def get_wifi_log(case_name): folder_path = os.path.abspath(f'./log/wifi_log/{case_name}') ADBHelper().execute_command(f'mkdir {folder_path}') ADBHelper().remount() ADBHelper().pull_file('/data/anr/',fr'{folder_path}/anr/') ADBHelper().pull_file('/data/log/hilogs', fr'{folder_path}/hilogs/') ADBHelper().pull_file('/data/vendor/log/wifi', fr'{folder_path}/wifi1/') ADBHelper().pull_file('/data/vendor/wifi', fr'{folder_path}/wifi2/') ADBHelper().execute_command(f'adb -s {udid} shell getprop > {folder_path}/system_prop.txt') time.sleep(60) def decide_capture_log(fail_timestamp,case_name): if len(fail_timestamp) != 0: # 抓日志方法的位置 get_wifi_log(case_name) INFO('失败的时间点为:') INFO(fail_timestamp) # 检查恢复出厂设置过程 def check_reset(sn=udid,time_out=120): INFO('1、Reboot to recovery') wait1 = 0 while wait1 < time_out: state = ADBHelper(sn).get_device_state() print(state) if 'device' not in state: INFO(f'2、Recovery operate') wait2 = 0 while wait2 < time_out - wait1: state2 = ADBHelper().adb_with_output('devices') print(state2) if 'recovery' in state2: INFO(f'3、Quit recovery mode') wait3 = 0 while wait3 < time_out - wait1 - wait2: if 'recovery' not in ADBHelper().adb_with_output('devices'): INFO('recovery sucess!') return True else: time.sleep(5) wait3 += 5 return False else: time.sleep(5) wait2 += 5 return False else: time.sleep(5) wait1 += 5 return False ############################################################################### ############################################################################### # adb命令相关的类 class ADBHelper(object): def __init__(self,device_sn=udid): self.device_sn = device_sn self.device_id = self.get_device_id(device_sn) # @classmethod # def get_device_id(cls, remote_ip="", remote_port="", device_sn=""): # if remote_ip == "" or remote_port == "": # if device_sn == "": # device_id = "" # else: # device_id = "-s %s" % device_sn # else: # if device_sn == "": # device_id = "-H %s -P %s" % (remote_ip, remote_port) # else: # device_id = "-H %s -P %s -s %s" % ( # remote_ip, remote_port, device_sn) # return device_id @classmethod def get_device_id(cls, device_sn): if device_sn != '': device_id = "-s %s" % device_sn return device_id ########################################################################### # 封装的命令操作 def execute_command(self, command, print_flag=True, timeout=120): try: if print_flag: print("command: " + command) if subprocess.call(command, shell=True, timeout=timeout) == 0: print("results: successed") return True except Exception as error: print("Exception: %s" % str(error)) print("Warning: %s failed" % command) return False def execute_command_with_output(self, command, print_flag=True): if print_flag: print("command: " + command) proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) try: stdout_data, stderr_data = proc.communicate() if isinstance(stdout_data, bytes): stdout_data = stdout_data.decode('utf-8', 'ignore') finally: proc.stdout.close() proc.stderr.close() return stdout_data def execute_command_with_list_output(self, command, print_flag=True): if print_flag: print("command: " + command) return os.popen(command).readlines() def execute_command_with_content_output(self, command, print_flag=True): if print_flag: print("command: " + command) return os.popen(command).read() def adb(self, command="devices"): return self.execute_command("%s %s %s" % ( USB_TOOLS, self.device_id, command)) def adb_with_output(self, command=""): return self.execute_command_with_output("%s %s %s" % ( USB_TOOLS, self.device_id, command)).strip() def shell(self, command=""): return self.execute_command("%s %s shell %s%s%s" % ( USB_TOOLS, self.device_id, QUOTATION_MARKS, command, QUOTATION_MARKS)) def shell_with_output(self, command=""): return self.execute_command_with_output("%s %s shell %s%s%s" % ( USB_TOOLS, self.device_id, QUOTATION_MARKS, command, QUOTATION_MARKS)).strip() def pull(self, src_path="", dst_path=""): return self.execute_command("%s %s pull %s%s%s %s%s%s" % ( USB_TOOLS, self.device_id, QUOTATION_MARKS, src_path, QUOTATION_MARKS, QUOTATION_MARKS, dst_path, QUOTATION_MARKS)) ########################################################################### def remount(self): self.adb("remount") def reboot(self): """ 重启设备 """ self.adb("reboot") self.adb("wait-for-device") time.sleep(20) def recovery(self): """ 重启设备并进入recovery模式 """ self.adb('reboot recovery') def fastboot(self): """ 重启设备并进入fastboot模式 """ self.adb('reboot bootloader') def root(self): """ 获取root状态 """ return 'not found' not in self.adb_with_output('su -c ls -l /data/') def push_file(self, srcpath, despath): command = "push %s %s" % (srcpath, despath) self.adb(command) def pull_file(self, srcpath, despath): command = "pull %s %s" % (srcpath, despath) self.adb(command) ########################################################################### def wait_device_online(self, max_delay=300): while (max_delay >= 0): result = self.shell_with_output("getprop sys.boot_completed") if result != "1": max_delay = max_delay - 1 time.sleep(1) print('---not found!') else: print("Info: device is online status.") time.sleep(3) return True print("Info: device is not online status.") return False def wait_device_ready(self, max_delay=0): delay_timse = 0 while (True): window_policy = self.dumpsys_window_policy('isStatusBarKeyguard') if 'isStatusBarKeyguard=true' in window_policy: print("Info: device ready.") return True if len(window_policy) == 0: window_policy = self.dumpsys_window_policy('mShowingLockscreen') if ('mShowingLockscreen=true' in window_policy): print("Info: device ready.") return True time.sleep(1) delay_timse = delay_timse + 1 if delay_timse > max_delay: break print("Info: device is not ready.") return False def dumpsys_window_policy(self, grep_condition=None): if grep_condition == None or grep_condition == "": return self.shell_with_output('dumpsys window policy') else: grep_condition = str(grep_condition).strip().replace(' ', '\ ') return self.shell_with_output('dumpsys window policy|grep ' + grep_condition) def is_file_exist(self, file_path): if file_path != "" and " " in file_path: current_file_path = "\"%s\"" % file_path else: current_file_path = file_path message = self.shell_with_output("ls %s" % current_file_path) message = message.strip() return False if message == "" else True ########################################################################### def init_device(self): self.remount() self.shell("mount -o rw,remount,rw /%s" % "system") def init_test_path(self): test_path = "/%s/%s" % ("data", "test") self.shell('rm -rf %s' % test_path) self.shell('mkdir -p %s' % test_path) self.shell('chmod 777 %s' % test_path) ########################################################################### def get_device_state(self): """ 获取设备状态: offline | bootloader | device """ return self.adb_with_output("get-state") def get_device_serialNo(self): """ 获取设备id号,return serialNo """ return self.adb_with_output("get-serialno") def get_android_version(self): """ 获取设备中的Android版本号,如4.2.2 """ return self.shell_with_output("getprop ro.build.version.release") def get_sdk_version(self): """ 获取设备SDK版本号,如:24 """ return self.shell_with_output("getprop ro.build.version.sdk") def get_product_brand(self): """ 获取设备品牌,如:HUAWEI """ return self.shell_with_output("getprop ro.product.brand") def get_product_model(self): """ 获取设备型号,如:MHA-AL00 """ return self.shell_with_output("getprop ro.product.model") def get_product_rom(self): """ 获取设备ROM名,如:MHA-AL00C00B213 """ return self.shell_with_output("getprop ro.build.display.id") def get_lcd_model(self): """ 获取设备LCD型号 """ return self.shell_with_output("cat sys/class/graphics/fb0/lcd_model") def get_phone_size(self): """ 获取手机分辨率大小 """ str = self.shell_with_output("wm size") try: size = str.split(':')[-1] phone_x = int(size.split('x')[0]) phone_y = int(size.split('x')[1]) except: phone_x,phone_y = '','' return phone_x, phone_y # 手机界面操作 ############################################################################### # 锁屏 def lock_screen(self): self.shell("input keyevent 26") # 解锁 def unlock_screen(self): self.shell("input keyevent 82") self.shell("wm dismiss-keyguard") # 返回 def go_back(self): self.shell('input keyevent 4') # home键 def go_home(self): self.shell('input keyevent 3') # 清除后台 def clear(self): self.go_home() x_size, y_size = self.get_phone_size() self.shell(f"input tap {0.7083 * x_size} {0.978125 * y_size}") time.sleep(1) self.shell(f"input tap {0.5 * x_size} {0.90625 * y_size}") # 滑动 def swipe(self, location): x, y = self.get_phone_size() if location == 'up': self.shell(f'input swipe {x / 2} {5 * y / 8} {x / 2} {y / 4}') if location == 'down': self.shell(f'input swipe {x / 2} {y / 4} {x / 2} {5 * y / 8}') if location == 'left': self.shell(f'input swipe {3 * x / 4} {y / 2} {x / 4} {y / 2}') if location == 'right': self.shell(f'input swipe {x / 4} {y / 2} {3 * x / 4} {y / 2}') # 通过坐标点击 def click_by_location(self, x_ratio, y_ratio): phone_x, phone_y = self.get_phone_size() self.shell(f'input tap {phone_x * x_ratio} {phone_y * y_ratio}') # 提升屏幕亮度 def Increase_Brightness(self): self.shell("input keyevent 221") self.go_back() # 降低屏幕亮度 def Decrease_Brightness(self): self.shell("input keyevent 220") self.go_back() # 媒体音量调节 def Adjust_Volume(self, type, operation): type_code_dict = { 'call': 0, 'ring': 2, 'media': 3, 'alarm': 4, 'assistant': 11 } if type in type_code_dict: if operation == 'increase': self.shell(f'service call audio 57 i32 {type_code_dict[type]} i32 1 i32 1') if operation == 'decrease': self.shell(f'service call audio 9 i32 {type_code_dict[type]} i32 -1 i32 1') if operation == 'mute': self.shell(f'service call audio 9 i32 {type_code_dict[type]} i32 -100 i32 1') if operation == 'cancel_mute': self.shell(f'service call audio 9 i32 {type_code_dict[type]} i32 100 i32 1') else: print("声音type参数错误!") # 设置音量 def set_Volume(self, type, level): type_code_dict = { 'call': 0, 'ring': 1, 'media': 3, 'alarm': 4, 'assistant': 11 } if type in type_code_dict: self.shell(f'service call audio 10 i32 {type_code_dict[type]} i32 {level} i32 1') ########################################################################### ########################################################################### def init_device_ex(self, cmd): try: proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, close_fds=True) while True: stdout, stderr = proc.communicate() returncode = proc.returncode print("returncode=%s" % returncode) if returncode == 0: print('执行结果:执行成功!') if returncode != 0: errors = stderr.split('\n') for erroline in errors: if 'FAILED' in erroline: print(erroline) # 写入错误行英文日志 if returncode == 127: print('执行结果:此行为空字符串!') if returncode == 17: print('执行结果:找不到表异常') if returncode == 64: print('执行结果:sql语句异常,缺失关键字') if returncode == 41: print('执行结果:sql语句异常,查询的字段不存在') if stdout == '' and proc.poll() != None: break except Exception as e: print("调用hive客户端,执行hive语句出错") print(str(e)) """ 以上代码中returncode为执行返回结果的标志 我总结的有以下几种情况 returncode=0 表示执行成功 returncode=127 表示语句为空串 returncode=17 表示找不到表 returncode=64 表示缺失关键字 returncode=41 表示查询的字段不存在 """ def input_set_passwd(self): for _ in range(6): time.sleep(1) self.adb("shell input keyevent 8") # # obj.reboot() # # obj.wait_device_ready(60) # # obj.wait_device_online(60) # # devices = obj.get_devices() # print(devices) # # devices = obj.get_android_version() # print(devices)
07-26
内容概要:本文详细探讨了杯形谐波减速器的齿廓修形方法及寿命预测分析。文章首先介绍了针对柔轮与波发生器装配时出现的啮合干涉问题,提出了一种柔轮齿廓修形方法。通过有限元法装配仿真确定修形量,并对修形后的柔轮进行装配和运转有限元分析。基于Miner线性疲劳理论,使用Fe-safe软件预测柔轮寿命。结果显示,修形后柔轮装配最大应力从962.2 MPa降至532.7 MPa,负载运转应力为609.9 MPa,解决了啮合干涉问题,柔轮寿命循环次数达到4.28×10⁶次。此外,文中还提供了详细的Python代码实现及ANSYS APDL脚本,用于柔轮变形分析、齿廓修形设计、有限元验证和疲劳寿命预测。 适合人群:机械工程领域的研究人员、工程师,尤其是从事精密传动系统设计和分析的专业人士。 使用场景及目标:①解决杯形谐波减速器中柔轮与波发生器装配时的啮合干涉问题;②通过优化齿廓修形提高柔轮的力学性能和使用寿命;③利用有限元分析和疲劳寿命预测技术评估修形效果,确保设计方案的可靠性和可行性。 阅读建议:本文涉及大量有限元分析和疲劳寿命预测的具体实现细节,建议读者具备一定的机械工程基础知识和有限元分析经验。同时,读者可以通过提供的Python代码和ANSYS APDL脚本进行实际操作和验证,加深对修形方法和技术路线的理解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值