Air 系列应用交流/AirRDP(FreeRDP)· 鸿蒙 PC 适配全记录

AirRDP(FreeRDP)· 鸿蒙 PC 适配全记录

【Air 系列 · 鸿蒙 PC 适配全记录】34 / 56

■ 应用:AirRDP(AirRDP)
■ 定位:RDP 远程桌面客户端
■ 上游:FreeRDP | 路线:引擎直连+ArkTS | 版本:5.2.0
■ 市场状态:已上架 AGC 应用页: https://appgallery.huawei.com/app/6917600576246338066
■ 官网(免费下载): https://www.shunidc.cn/

————————————————————
开源鸿蒙 PC 社区: https://harmonypc.csdn.net/

PC 社区项目申请: https://atomgit.com/OpenHarmonyPCDeveloper

适配开源地址:(AtomGit 仓库建立后回填)

环境搭建文章:https://blog.csdn.net/weixin_52908342/article/details/161343743

▍一、为什么要适配 AirRDP

远程桌面是 PC 上无可替代的工作形态:管理没有显示器的服务器、在Mac 上临时使用一台 Windows 机器、跨机房排障,最终都落在 RDP 协议上。Windows 自带 mstsc,Linux 桌面有 Remmina 和 xfreerdp,而鸿蒙 PC 目前没有一条从开源协议栈到原生窗口系统完整接线的 RDP 客户端链路。AirRDP 要补的就是这一段:把 FreeRDP 3.30.0 完整交叉编译到 aarch64-linux-ohos,再为它重建渲染、解码、音频、输入、文件与剪贴板等全部宿主环境。

这不是一个命令行工具套壳。工程现状是 64 个 ArkTS/TS 文件约 2.5 万行,加上 28 个 C/C++ 源文件组成的 NAPI 桥接与渲染层,仓库根目录同时保留 FreeRDP 上游源码树(libfreerdp/、winpr/、client/、channels/)与交叉编译管线(build_freerdp/)。应用版本 5.2.0,包名 com.airrdp.app,支持 phone、tablet、2in1,并声明了 multiAppMode 多实例(最多 5 个并发会话)与 NEED_OPENGL 应用环境变量,从配置层面就是按 PC 多窗口重度使用设计的。

▍二、先划清边界:不把编译通过当成适配完成

FreeRDP 自带的 xfreerdp 是面向 X11 的可执行程序,即使交叉编译成功,也不等于鸿蒙上有了可用的远程桌面:窗口、帧呈现、H.264 解码、声音、键盘焦点、文件通道,每一项都要在鸿蒙侧找到对应宿主。本工程的边界划分如下表。

层次 传统桌面方案 鸿蒙侧当前方案
协议引擎 FreeRDP xfreerdp 可执行程序 + X11 事件循环 FreeRDP 3.30.0 交叉编译为 libfreerdp3.so 等随包库,工作线程由 NAPI 层驱动
窗口承载 X11/Win32 顶层窗口 XComponent + OHNativeWindow,ArkTS 侧 RemoteDesktop.ets 会话页
帧呈现 cairo/XCopy 或 GLX OpenGL ES 渲染器(rendering/OpenGLRenderer.cpp),BGRA 纹理直传
视频解码 OpenH264 软解 OH AVCodec 硬解适配层(AVC444/AVC420),FreeRDP 内置 OpenH264 静态保留
音频输出 PulseAudio/ALSA 后端 OpenSL ES 后端 + SLOHBufferQueueItf(audio/OHAudioDevice.cpp)
键盘输入 操作系统焦点分发 Input Kit 按键拦截(input/KeyInterceptor.cpp)+ ArkTS KEY_MAP 键码映射
文件交换 drive 通道挂载本地目录 rdpdr drive 通道 + ArkTS 目录同步(FileSyncManager.ets)与传输面板
凭据管理 已知主机文件 + 系统钥匙串 certificate/CertificateStore.cpp + ArkTS PasswordVault.ets

一个明确的取舍是:连接参数结构(EntryAbility.ets 的 RdpLaunchParams)围绕 host、端口、凭据、色深、画质、UDP、显示缩放组织,当前版本交付的是完整桌面会话客户端,而不是协议栈全部特性的包装。

▍三、鸿蒙版的整体架构

工程为 Air 双层结构:ArkTS 层承担连接管理、会话界面与外设协同;Native 层承接 FreeRDP 回调、渲染与解码;两侧通过 NAPI 与 TSFN 双向通话。

text
ArkTS 壳(entry/src/main/ets,约 2.5 万行)
├── pages/Index.ets 连接管理与系统发现
├── pages/RemoteDesktop.ets 会话主界面(标题栏/工具条/悬浮输入)
├── managers/ 25 个管理器:剪贴板、文件同步、重连、分辨率、
│ 鼠标模式、按键拦截、全屏、网络监视、密码库
├── input/ 手势、触控、滚轮、指针四条输入链
└── services/RdpService/RdpConnection.ets NAPI 调用封装(单例)
▼ NAPI:libentry.so(entry/src/main/cpp)
├── napi/ Connection / Input / Query / Certificate /
│ Callback / Transfer 六个 NAPI 模块
├── core/ SessionManager 多实例会话表 + RdpSession
├── rendering/ OpenGLRenderer + RemoteCursor + XEngine 超分
├── codec/ H264GfxDecoder + OHVideoDecoderAdapter
├── audio/ OpenSL ES 音频后端
├── input/ KeyInterceptor(Input Kit 按键拦截)
├── drive/ + clipboard/ rdpdr 文件通道 + 剪贴板文件描述符
└── certificate/ 证书信任存储
▼ FreeRDP 3.30.0(build_freerdp 交叉编译产物)
libfreerdp3.so / libwinpr3.so / libfreerdp-client3.so(entry/libs/arm64-v8a)


主要目录如下:

text
AirRDP/
├── AppScope/ # 应用配置(多实例、NEED_OPENGL)
├── build_freerdp/ # 交叉编译管线
│ ├── build_all.sh # OpenSSL→OpenH264→FreeRDP 三阶段构建
│ └── ohos-toolchain.cmake # aarch64-linux-ohos 工具链文件
├── entry/
│ ├── libs/arm64-v8a/ # freerdp3/winpr3/freerdp-client3/turbojpeg
│ └── src/main/
│ ├── ets/ # ArkTS 层(pages/managers/input/store/services)
│ └── cpp/ # NAPI 桥 + 渲染/解码/音频/输入/通道
├── include/ # freerdp3 / winpr3 头文件
├── libfreerdp/ winpr/ client/ channels/ # FreeRDP 上游源码树
└── regression_shots/ smoke_shots/ # 回归与冒烟截图存档


▍四、核心适配过程

### 1. FreeRDP 3.30.0 的三阶段交叉编译管线

build_freerdp/build_all.sh 是协议栈的唯一定时来源:先用 ./Configure linux-aarch64 静态编译 OpenSSL 3.4.1(no-shared、enable-legacy),再以 build_freerdp/ohos-toolchain.cmake 指定的 aarch64-unknown-linux-ohos-clang 工具链编译 FreeRDP,C 编译标志统一带 -D__MUSL__ 以匹配鸿蒙 musl libc。OpenH264 按上游规范(libfreerdp/codec/CMakeLists.txt)以 WITH_OPENH264=ON、WITH_OPENH264_LOADING=OFF 静态链入 libfreerdp.so,工程用 nm 验证过 WelsCreateDecoder 等符号为 T 导出。产物连同 libturbojpeg.so 一起进入 entry/libs/arm64-v8a/。

### 2. NAPI 六模块与多实例会话管理

entry/src/main/cpp/freerdp_napi.cpp 头部注释明确了拆分原则:连接、输入、状态查询、证书、回调、传输各自独立成 napi/NapiConnection.cpp 等六个模块,主文件只保留 FreeRDP 协议回调(cb_pre/post_connect、cb_end_paint、cb_verify_certificate)、RDP 工作线程与 XComponent 绑定。ArkTS 侧由 services/RdpService/RdpConnection.ets 单例封装 connect/disconnect/reconnect。多实例通过 core/SessionManager.cpp 的会话表实现:g_sessions 以自增 instanceId 索引多个 RdpSession,g_pendingWindows 处理 XComponent Surface 晚于连接建立的延迟绑定,对应 app.json5 中最多 5 实例的多开声明。

### 3. GFX 渲染链:硬解、纹理与远端光标

默认渲染模式为 GFX Progressive(constants/RdpConstants.ets 的 DEFAULT_RENDER_MODE),默认连接类型 6、色深 24。H.264 帧不走 FreeRDP 软解路径,而是 codec/H264GfxDecoder.cpp 调用 codec/OHVideoDecoderAdapter.cpp 创建系统 AVCodec 解码器(OH_VideoDecoder_CreateByMime(OH_AVCODEC_MIMETYPE_VIDEO_AVC)),支持 AVC444 与 AVC420 两种模式,再经 OpenGLRenderer.cpp 以 BGRA 纹理上传绘制;rendering/RemoteCursor.cpp 单独处理远端光标形状。cb_end_paint 直接触发同步渲染,把 FreeRDP 的绘制节拍原样接到 NativeWindow。

### 4. 输入链:从触控手势到物理键盘拦截

触控侧由 ets/input/ 的 RdpGestureHandler、RdpNativeTouchSender、RdpWheelHandler 与 MouseModeManager(触控/指针双模式,含软键盘弹出时的坐标偏移修正)组成。PC 场景的关键在物理键盘:远端桌面必须收到 Win/Meta 等系统快捷键,而它们默认被本机系统消费。cpp/input/KeyInterceptor.cpp 直接链接 libohinput.so,用 OH_Input_AddKeyEventInterceptor 全局拦截按键,经 TSFN 回调 ArkTS KeyInterceptorManager,由 KEY_MAP 完成键码映射后经 InputSender.sendKeyEvent 送入 FreeRDP,对应权限 ohos.permission.INTERCEPT_INPUT_EVENT。

### 5. 外围通道:文件、剪贴板、音频与证书

文件重定向复用 FreeRDP 的 rdpdr 文件系统虚拟通道(cpp/drive/drive_main.c、drive_file.c),ArkTS 侧 FileSyncManager.ets 提供目录级同步与变更监视,RdpTransferPanel.ets 承载传输 UI。剪贴板除文本同步(ClipboardSyncManager.ets,带去重窗口)外,cpp/clipboard/ClipboardFileUtils.cpp 解析并打包 FileGroupDescriptorW,使文件可以在本机剪贴板与远端之间直接复制。音频后端是 audio/OHAudioDevice.cpp:基于 FreeRDP 官方 OpenSL ES 后端,把 Android 的 SL_IID_ANDROIDSIMPLEBUFFERQUEUE 接口逐项替换为鸿蒙的 SL_IID_OH_BUFFERQUEUE。证书信任由 certificate/CertificateStore.cpp 持久化,配合 RdpCertVerifyDialog.ets 完成首次连接确认;NLA 认证有独立的 RdpNlaAuthDialog.ets。

▍五、适配中遇到的几个难点

### 难点一:音频后端在非 UI 上下文失效

最初采用 OHAudio 方案,但 OHAudio 通过 Binder IPC 连接 AudioServer,在 RDP 工作线程这类非 UI 上下文中因 AccessToken 校验失败无法出声。工程改用 OpenSL ES:slCreateEngine 创建的是本地引擎对象,不需要 IPC 与 AccessToken,任意线程可正常调用,只需把 Android 专用接口逐项替换为鸿蒙等价物。这个解法保留在 OHAudioDevice.cpp 的头部注释中,成为后续同类问题的参考。

### 难点二:物理键盘快捷键必须在系统层拦截

远程桌面用户在远端按 Win 键的预期是打开远端开始菜单。若只在应用内监听按键,系统快捷键根本到不了回调。工程为此申请 INTERCEPT_INPUT_EVENT 权限并在 Native 层直接链接 Input Kit 的 C API,把拦截到的系统级按键经 TSFN 送到 ArkTS 映射后再注入 FreeRDP,形成"物理键盘、Input Kit 拦截、键码映射、RDP 事件"的完整数据流。

### 难点三:OpenH264 静态与动态混用导致符号重复

解码链路上若同时存在系统硬解适配层与独立 libopenh264.so,会出现静态/动态两套符号。工程的处理是把 OpenH264 严格按上游规范静态链入 libfreerdp.so 并关闭运行时加载(WITH_OPENH264_LOADING=OFF),CMake 侧显式不再链接独立库,并用 nm 确认符号导出形态,从构建层面消除混用。

### 难点四:远端低分辨率与 PC 高分屏的落差

RDP 会话常以远端分辨率运行,直接拉伸到 PC 高分屏会明显发糊。工程接入华为 Graphics Accelerate Kit(XEngineKit)做空域超分:rendering/XEngineUpscaler.cpp 将低分辨率帧纹理经 Maleoon GPU 双边滤波重建后输出,probe() 只查询一次扩展可用性以避免每帧 GetString 开销,scissor 与锐化参数仅在源尺寸变化时重设,超分 Pass 结束立即切回默认帧缓冲,把开销控制住。

### 难点五:多实例会话与 Surface 生命周期错位

最多 5 个并发会话意味着 XComponent 的 Surface 创建、销毁与 RDP 连接建立没有固定先后。SessionManager 用待绑定窗口表(g_pendingWindows)把"连接先到、Surface 后到"与相反顺序都纳入同一状态机,会话销毁时同步清理 EGL 上下文与 VSync 对象,避免多实例切换后残留失效引用。

▍六、编译、安装与启动

构建分两段。第一段交叉编译协议栈(macOS 环境,NDK 路径按实际安装位置调整):

bash
cd build_freerdp
./build_all.sh # OpenSSL 3.4.1 → OpenH264 → FreeRDP 3.30.0
# 产物复制到 entry/libs/arm64-v8a/


第二段构建 HAP:

bash
export JAVA_HOME=/Applications/DevEco-Studio.app/Contents/jbr/Contents/Home
export DEVECO_SDK_HOME=/Applications/DevEco-Studio.app/Contents/sdk

hvigorw --mode module -p module=entry assembleHap --no-daemon


签名产物位于 entry/build/default/outputs/default/entry-default-signed.hap。连接设备后安装并启动:

bash
hdc install -r entry/build/default/outputs/default/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.airrdp.app


▍七、当前功能边界

当前版本已覆盖的能力:

- RDP 完整桌面会话,面向 Windows、Linux(含 xrdp 确认流程 RdpXrdpConfirmDialog.ets)与 macOS 主机;
- GFX Progressive 渲染与 H.264 AVC444/AVC420 解码,GPU 加速可配置,UDP 传输可选;
- 最多 5 个并发会话的多实例管理;
- OpenGL ES 帧呈现、远端光标与 XEngine 空域超分;
- 触控/指针双模式输入、手势与滚轮,物理键盘经 Input Kit 拦截与键码映射;
- 剪贴板文本与文件双向同步,rdpdr 文件重定向与目录级文件同步;
- OpenSL ES 音频重定向;
- NLA 认证对话框、证书信任管理、自动重连、密码库 PasswordVault.ets、连接云同步与缩略图;
- 全屏沉浸、隐私窗口、后台长连接保活与网络状态监视。

尚未进入当前版本的能力:RdpLaunchParams 中没有网关(RD Gateway)与 RemoteApp 字段,远程应用模式与网关接入不在当前交付范围;module.json5 声明了摄像头与麦克风权限(限使用期),但设备重定向通道尚未形成与文件通道同级的完整实现;会话录制、多显示器逐屏映射等特性亦未见对应代码。工程没有宣称这些能力存在,边界以连接参数结构与通道代码的实际覆盖为准。

因此,当前版本更准确的定位是"鸿蒙 PC 多实例 RDP 桌面客户端":协议栈、渲染、输入、外设通道四条主线都已闭环,剩余能力按优先级逐版本补齐。

▍八、总结

AirRDP 的适配证明了一条对协议密集型应用可行的路线:协议栈原样交叉编译、宿主环境全部重建。FreeRDP 3.30.0 的代码没有被改造成分裂的分支,所有鸿蒙差异都收敛在 NAPI 桥接层与外设后端里;OpenSL ES 音频、Input Kit 按键拦截、AVCodec 硬解、XEngine 超分,每一项都是用鸿蒙系统能力替换桌面平台对应物,FreeRDP 只感知到"又一个可用的后端"。

后续工作重心集中在两处:把已声明权限对应的设备重定向通道补成与文件通道同等完整的实现,以及评估网关与 RemoteApp 场景的接入顺序。工程根目录的 regression_shots/ 与 smoke_shots/ 目录记录了回归验证的存档习惯,后续每个通道的接入也应延续"代码、验证、存档"同一闭环。
楼主 8 天前
想参与讨论?登录 或 30 秒注册。