OpenHarmony开发者论坛

标题: 【DEBUG调试方法】(MMI,ArkUI,MSDP)不同设备常见拖拽问题分析的基本思路 [打印本页]

作者: wangyeyu01    时间: 2023-12-13 20:49
标题: 【DEBUG调试方法】(MMI,ArkUI,MSDP)不同设备常见拖拽问题分析的基本思路
[md]# 不同设备常见拖拽问题分析的基本思路

## 一、典型问题汇总表

| 序号 | 问题描述                                                            | 原因分析                                                                                                                                                                                             | 解决方案                                                                         |
| ---- | ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| 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 事件

```cpp
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)事件派发前的事件打包

```cpp
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<ointerEvent> event, NetPacket &pkt)
{
// ......
}

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

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

```cpp
void AceViewOhos:rocessTouchEvent(const std::shared_ptr<MMI:ointerEvent>& pointerEvent)
{
// ......
}

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

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

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

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

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

### 1. 鼠标移动事件注入

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

```shell
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 子系统,因而分析问题较为繁琐。此文仅针对简单问题进行了基本思路阐述,实际问题错综复杂需要多种方法结合进行分析。同时需要多方面、充分样例测试避免引入新问题。
[/md]




欢迎光临 OpenHarmony开发者论坛 (https://forums.openharmony.cn/) Powered by Discuz! X3.5