[经验分享] 【DEBUG调试方法】(MMI,ArkUI,MSDP)不同设备常见拖拽问题分析的基本思路 原创 精华

诚迈_雨哥 显示全部楼层 发表于 2023-12-13 20:49:11

不同设备常见拖拽问题分析的基本思路

一、典型问题汇总表

序号 问题描述 原因分析 解决方案
1 触摸板+鼠标,触摸板拖拽图标,鼠标不按键滑动翻页,拖拽终止 解决其他问题时引入。拖拽后立即抬起会导致页面卡死,原因是还没有等到拖拽发起完成就结束了,拖拽永远等不到结束的事件,所以卡死。解决的方法是(鼠标、手指、触摸板)移动时直接结束拖拽结束。 当且仅当发起拖拽源抬起才能结束拖拽。
2 双焦点拖拽大面积卡顿,最终失败 拖拽事件的发起到拖拽结束流程太长,开启ArkUI模块 DEBUG 日志恶化了这个情况,最终导致 IPC-socket通信异常,ANR超时,所以先卡顿后失败。 输入 ’hilog -b X‘ 一切正常。
3 鼠标拖拽备忘录中的文字到该文档的不同位置,HAP 闪退 解决返回空字符串规范问题时引入。问题必现,查看Crash日志,addr2line 定位到文件的具体行,发现异常时返回的字符串为{}, 查看历史PR 发现原来为 "". 将 原来返回{} 改为 “”,问题解决。
4 手指拖拽备忘录中的文字,鼠标不按键滑动出上边框,再进来才显示 Cursor 查看日志,鼠标不按键滑动出了上边框会发送DragLeave,进来之后发送DragEnter. 离开再进入Cursor显示正常,说明拖拽发起之后UI的拖拽状态不正常。进而发现收集拖拽手势识别器的时候,拖拽之前顺便取消了之前拖拽 拖拽发起前取消拖拽代码注释掉,鼠标不按键滑动会立刻显示 Cursor。
5 一个手指拖拽桌面图标,另一个手指翻页失败 手指拖拽图标,鼠标滑动翻页功能正常,与其比较,发现程序中如果事件源发生改变则清空裁决器中的所有手势。当前两个手指是同一事件源的不同手指ID,不清空导致。 事件源不改变,手指ID改变时清空裁决器中所有手势,手指拖拽图标,另一手指翻页正常。
6 鼠标拖拽时补发窗口变化的进出事件

二、自动设置与导出 hilog 日志

第一步、开启某个模块的 DEBUG 日志

  • 可将以下文本写入 setting.bat,双击运行
  • 例如开启ArkUI DEBUG 日志,除过设置日志域 0xD003900 为D之外(删除 REM),还要设置内部受控的Info日志开关param set persist.ace.debug.enabled 1
  • 例如开启多模 MMI DEBUG 日志,设置 0xD002800 为 D 即可
  • 例如开启拖拽框架 MSDP DEBUG 日志,设置 0xD002220 为 D 即可
  • REM 或者 :: 为注销该行
@echo off

for /f %%a in ('call hdc list targets') do (
    echo set device: %%a

    hdc -t %%a shell "mount -o remount,rw /"
    hdc -t %%a shell "setenforce 0"
    hdc -t %%a file send h /system/bin/
    hdc -t %%a shell "chmod 777 /system/bin/h"
    hdc -t %%a shell "hilog -b D -T DragManager"
    hdc -t %%a shell "hilog -b D -T InputEventHandler"
    hdc -t %%a shell "hilog -b D -T InputChannel"
    hdc -t %%a shell "hilog -b D -T InputEventListener"
    hdc -t %%a shell "hilog -b D -T ClientMsgHandler"
    hdc -t %%a shell "hilog -b D -T InputManagerImpl"
    hdc -t %%a shell "hilog -b I -D 0xD002800"
    hdc -t %%a shell "hilog -b D -D 0xD002220"
    REM hdc -t %%a shell "hilog -b D -D 0xD003900"
    REM hdc -t %%a shell "hilog -b D -D 0xD004200"
    REM hdc -t %%a shell "hilog -b D -D 0xD001300"
    hdc -t %%a shell "hilog -b D -D 0xD003900"
    hdc -t %%a shell "hilog -b D -D 0xD004200"
    REM hdc -t %%a shell "hilog -b D -D 0xD0015c0"
    hdc -t %%a shell "hilog -Q pidoff"
    hdc -t %%a shell "power-shell setmode 602"
)

pause

第二步、停止之前的日志记录任务,然后清空日志目录,最后开启最大100M文件的保存

  • 可将以下文本写入 clean_hilog.bat,双击运行
@echo off

for /f %%a in ('call hdc list targets') do (
    echo device: %%a
    hdc -t %%a shell "hilog -w stop"
    hdc -t %%a shell "hilog -r"
    hdc -t %%a shell "cd /data/log/hilog && rm -rf *"
    hdc -t %%a shell "hilog -w start -l 100M"
)

pause

第三步、导出 hilog 到当前目录或者 D:\ 目录下

  • 可将以下文本写入 save_hilog.bat,双击运行
  • R=xxx, xxx 通过控制台 cmd 窗口输入 'hdc list targets' 获取(前提是 hdc 能用)
  • REM 或者 :: 为注销该行
@echo off

set time_now=%time:~0,2%-%time:~3,2%-%time:~6,2%

set local="hilog\%time_now%_local"

set yyyy=%date:~,4%
set mm=%date:~5,2%
set day=%date:~8,2% 
set YYYYmmdd=%yyyy%-%mm%-%day%
set YYYYmmdd=%YYYYmmdd: =%

set hh=%time:~0,2%
set mi=%time:~3,2%
set ss=%time:~6,2%
set hhmiss=-%hh%h%mi%m%ss%s

set timestamp=%YYYYmmdd%%hhmiss%
REM set R=7001005458323933328a25ce2a693900

if not exist "hilog" (
    md hilog
)

for /f %%a in ('call hdc list targets') do (
    echo device: %%a
    hdc -t %%a shell "hilog -w stop"
    REM if "%%a" equ "%R%" ( 
        hdc -t %%a shell "cd /data/log/hilog/ && mv *.gz local.gz"
        hdc -t %%a file recv /data/log/hilog/local.gz %CD%\hilog\

    REM 解压log,使用WinRAR解压需要设置环境变量
    WinRAR e -or "hilog\local.gz" "hilog\"
    echo WinRAR Decompress successfully!
    timeout /t 1 /nobreak >nul

    REM 以当前时间命名log文件名
    if exist hilog\local (
        CD hilog
        REN local %timestamp%
        echo Successfully modify file name!
    )

    REM )
    REM if "%%a" equ "%L%" (
    REM   hdc -t %%a shell "cd /data/log/hilog/ && mv *.gz remote.gz"
    REM    hdc -t %%a file recv /data/log/hilog/remote.gz %CD%\hilog\
    REM )
    REM hdc -t %%a file recv /data/log/hilog/ hilog\

    REM if "%%a" equ "%R%" 
    REM move hilog\hilog %local%
    REM if "%%a" equ "%L%" move hilog\hilog %remote%
)

hdc file recv /data/log/hilog/ D:\
pause

三、 addr2line 用法举例

  1. crash 日志在开发板这个目录下 /data/log/faultlog/faultlogger
  2. 导出crash日志 hdc file recv /data/log/faultlog/faultlogger D:\
  3. 堆栈信息

    01 pc 00009289 /vendor/lib/libhdi_audio_primary_server.z.so(HdiServiceGetAllAdapter+182)

  4. 查找unstripped 未压缩的so wyy@wyy-virtual-machine:~/code_openharmony$ find -name libhdi_audio_primary_server.z.so ./out/rk3568/packages/phone/vendor/lib/libhdi_audio_primary_server.z.so ./out/rk3568/hdf/drivers_peripheral_audio/libhdi_audio_primary_server.z.so ./out/rk3568/lib.unstripped/hdf/drivers_peripheral_audio/libhdi_audio_primary_server.z.so
  5. 在ubuntu上运行 addr2line wyy@wyy-virtual-machine:~/code_openharmony$ addr2line -e ./out/rk3568/lib.unstripped/hdf/drivers_peripheral_audio/libhdi_audio_primary_server.z.so 0x00009289
  6. 结果 /home/wyy/code_openharmony/out/rk3568/../../drivers/peripheral/audio/hal/hdi_binder/server/src/hdf_audio_server_common.c:1104

四、几个输入事件的关键节点(时机)

1. 收集来自 Linux 的libinput 事件

void EventNormalizeHandler::HandleEvent(libinput_event* event)
{
    CALL_DEBUG_ENTER;
    CHKPV(event);
    DfxHisysevent::GetDispStartTime();
    auto type = libinput_event_get_type(event);
    // ......
    switch (type) {
        case LIBINPUT_EVENT_DEVICE_ADDED: {
            OnEventDeviceAdded(event);
            break;
        }
        case LIBINPUT_EVENT_DEVICE_REMOVED: {
            OnEventDeviceRemoved(event);
            break;
        }
        case LIBINPUT_EVENT_KEYBOARD_KEY: {
            HandleKeyboardEvent(event);
            DfxHisysevent::CalcKeyDispTimes();
            break;
        }
        case LIBINPUT_EVENT_POINTER_MOTION:
        case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
        case LIBINPUT_EVENT_POINTER_BUTTON:
        case LIBINPUT_EVENT_POINTER_BUTTON_TOUCHPAD:
        case LIBINPUT_EVENT_POINTER_AXIS:
        case LIBINPUT_EVENT_POINTER_TAP:
        case LIBINPUT_EVENT_POINTER_MOTION_TOUCHPAD: {
            HandleMouseEvent(event);
            DfxHisysevent::CalcPointerDispTimes();
            break;
        }
        // ......
        case LIBINPUT_EVENT_SWITCH_TOGGLE: {
            HandleSwitchInputEvent(event);
            break;
        }
        default: {
            MMI_HILOGW("This device does not support :%d", type);
            break;
        }
    }
    DfxHisysevent::ReportDispTimes();
}

2. 多模输入Multimodalinput (MMI)事件派发前的事件打包

int32_t InputEventDataTransformation::KeyEventToNetPacket(
    const std::shared_ptr<KeyEvent> key, NetPacket &pkt)
{
// ......
}
int32_t InputEventDataTransformation::SwitchEventToNetPacket(
    const std::shared_ptr<SwitchEvent> swEvent, NetPacket &pkt)
{
// ......
}

int32_t InputEventDataTransformation::Marshalling(std::shared_ptr<PointerEvent> event, NetPacket &pkt)
{
// ......
}

#ifdef OHOS_BUILD_ENABLE_SECURITY_COMPONENT
int32_t InputEventDataTransformation::MarshallingEnhanceData(std::shared_ptr<PointerEvent> event, NetPacket &pkt)
{
// ......
}
#endif // OHOS_BUILD_ENABLE_SECURITY_COMPONENT

3. ArkUI 接收来自多模输入(MMI)的输入事件,并转换成 ArkUI 对应的事件

void AceViewOhos::ProcessTouchEvent(const std::shared_ptr<MMI::PointerEvent>& pointerEvent)
{
// ......
}

#ifdef ENABLE_DRAG_FRAMEWORK
void AceViewOhos::ProcessDragEvent(const std::shared_ptr<MMI::PointerEvent>& pointerEvent)
{
// ......
}

#endif // ENABLE_DRAG_FRAMEWORK
void AceViewOhos::ProcessMouseEvent(const std::shared_ptr<MMI::PointerEvent>& pointerEvent)
{
// ......
}

void AceViewOhos::ProcessAxisEvent(const std::shared_ptr<MMI::PointerEvent>& pointerEvent)
{
// ......
}

bool AceViewOhos::ProcessKeyEvent(const std::shared_ptr<MMI::KeyEvent>& keyEvent)
{
// ......
}

五、高级方式,采用虚拟设备、虚拟注入事件进行问题分析

1. 鼠标移动事件注入

在一个cmd窗口鼠标移动注入

D:>hdc shell mount -o remount,rw /

D:>hdc file send D:\wyy\four_in_one\hdc_tool\injectmove\vdevadm system/bin
FileTransfer finish, Size:214332, File count = 1, time:53ms rate:4044.00kB/s

D:>hdc shell
# cd system/bin
# chmod 755 vdevadm
# vdevadm mount -t M
Mount virtual mouse
Start to mount virtual mouse.
Mount virtual mouse successfully.
# vdevadm act -t M -m 1 1
[mouse] move: (1,1)
# vdevadm act -t M -m -514 -820
[mouse] move: (-514,-820)
# vdevadm act -t M -m -500 -800
[mouse] move: (-500,-800)
# vdevadm act -t M -M 200 200
[mouse] move-to (200,200)
# uinput -M -m 200 200
move to 200 200

D:>hdc shell
#
# uinput -M -m 300 300
move to 300 300
# uinput -M -m 300 300
move to 300 300
# uinput -M -m 300 300
move to 300 300

在另一个cmd窗口位置监听
D:>hdc shell
#  vdevadm monitor -t M
Monitor for position of current pointer
current pointer position - x: 347   y: 640

2. 虚拟鼠标设备

vdevadm mount -t M    加载虚拟鼠标
vdevadm unmount -t M    卸载虚拟鼠标
vdevadm monitor -t M    实时获取当前鼠标光标位置
vdevadm act -t M -r <file>    从指定文件(原始事件的注入)中读取鼠标操作命令,并执行
vdevadm act -t M -f <file>    从指定文件(鼠标动作的注入)中读取鼠标操作命令,并执行
vdevadm act -t M -d L/M/R    按下鼠标左键/中键/右键
vdevadm act -t M -m dx dy    移动鼠标
vdevadm act -t M -u L/M/R    抬起鼠标左键/中键/右键
vdevadm act -t M -s dy    滚动鼠标滚轮
vdevadm act -t M -M x y    鼠标移动到<x,y>
vdevadm act -t M -w <ms>    等待<ms>毫秒
vdevadm act -t M -D x y    拖拽到

3. 虚拟触摸屏设备

vdevadm mount -t T    加载虚拟触摸屏
vdevadm unmount -t T     卸载虚拟触摸屏
vdevadm clone -t T    当前已存在的物理触屏设备,创建虚拟触屏设备
vdevadm act -t T -r <file>    从指定文件(原始事件的注入)中读取触摸屏操作命令,并执行
vdevadm act -t T -f <file>    从指定文件(触摸屏动作的注入)中读取触摸屏操作命令,并执行
vdevadm act -t T -d <0-9> x y    按下手指(slot,按下的位置)
vdevadm act -t T -m <0-9> dx dy    移动手指
vdevadm act -t T -D <0-9> sx sy tx ty    拖拽到tx ty
vdevadm act -t T -u <0-9>    抬起手指
vdevadm act -t T -M <0-9> x y    手指移动到(x,y)
vdevadm act -t T -w <ms>    等待<ms>毫秒

4. 虚拟键盘设备

vdevadm mount -t K    加载虚拟键盘
vdevadm unmount -t K    卸载虚拟键盘
vdevadm act -t K -d <key>    按下按键<key>
vdevadm act -t K -u <key>    抬起按键<key>
vdevadm act -t K -w <ms>    等待<ms>毫秒
vdevadm act -t K -r <file>    从指定文件(原始事件的注入)中读取键盘操作命令,并执行
vdevadm act -t K -f <file>    从指定文件(键盘动作的注入)中读取键盘操作命令,并执行

六、ArkUI关于手势的处理

1. 手势收集阶段

ArkUI 将来自多模输入子系统(MMI)的输入事件转换成自己对应的手势之后。在适配层进行事件转换,交给上下文管线PiplineContext,管线调用事件管理器EventManager,事件管理器再调用FrameNode遍历当前应用中的每个组件节点,接着每个节点收集由应用设置下来的各种动作手势

2. 手势派发阶段

收集到手势之后,手势裁决器遍历每个手势识别器,结合并行、互斥、序列以及优先级裁决出有效的手势,最后派发给对应的 UI-Pattern。

3. 事件结束

当事件源抬起,键盘ESC或者由用户取消,将结束事件派发个 UI-Pattern 之后,清空手势裁决器中的所有手势。

七、结论

输入事件相关问题直接影响用户的体验,它涉及的模块有多模输入子系统(MMI)、拖拽框架子系统(MSDP)、窗口管理及窗口显示子系统(WINDOW)以及ArkUI 子系统,因而分析问题较为繁琐。此文仅针对简单问题进行了基本思路阐述,实际问题错综复杂需要多种方法结合进行分析。同时需要多方面、充分样例测试避免引入新问题。

©著作权归作者所有,转载或内容合作请联系作者

您尚未登录,无法参与评论,登录后可以:
参与开源共建问题交流
认同或收藏高质量问答
获取积分成为开源共建先驱

Copyright   ©2023  OpenHarmony开发者论坛  京ICP备2020036654号-3 |技术支持 Discuz!

返回顶部