OpenHarmony电话子系统短信发送流程分析
文档概述
说明:
1.文章由移远通信技术股份有限公司提供
2.以下内容包含了个人理解,仅供参考,如有不合理处,请联系笔者修改
概述
OpenHarmony 的短信能力由 Telephony 子系统提供,是标准系统的基础通信能力之一,核心功能与相关说明如下:
1.
核心系统能力
支持短信/彩信(暂不支持,需要自行适配)收发、SIM 卡短信管理、短信服务中心地址配置、默认 SIM 卡槽设置等基础能力,配套提供了系统级 API 供开发者调用。
2.
应用侧开发能力
系统预置短信应用已实现会话管理、消息收发、送达报告、短信备份、垃圾短信拦截等完整功能,可通过一套代码适配手机、平板等多类设备。
普通三方应用可通过 startAbility 接口拉起系统短信页面,预填收件人与短信内容完成发送,无需申请高权限系统短信 API。
Flutter 生态已完成 flutter_sms 库适配,支持短信对话框唤起、多收件人发送、设备短信能力检测等跨平台功能。

架构设计
分层模型
短信发送模块采用四层架构,自上而下分别为:

设计原则
单一职责原则:每个模块专注特定功能领域
SmsSendManager:发送调度与生命周期管理GsmSmsSender/CdmaSmsSender:制式特定的编码与通信SmsNetworkPolicyManager:网络路由与优先级决策
策略模式应用:网络制式差异通过策略对象动态封装
- 运行时选择对应的Sender实例
- 同一接口下适配多个网络标准
异步处理模式:所有发送操作均为异步非阻塞的事件驱动
- 发送请求入队后立即返回
- 通过回调机制通知应用层结果
核心组件详解
SmsInterfaceManager(接口管理器)
职责范围:
- 对外提供统一的短信发送接口(
TextBasedSmsDelivery、DataBasedSmsDelivery) - 管理每个 SIM 卡槽的资源生命周期
- 请求的初步验证与权限检查
核心方法:
int32_t TextBasedSmsDelivery(
const std::string &desAddr, // 目标地址
const std::string &scAddr, // 短信服务中心地址
const std::string &text, // 短信文本内容
const sptr<ISendShortMessageCallback> &sendCallback, // 发送结果回调
const sptr<IDeliveryShortMessageCallback> &deliveryCallback, // 送达报告回调
int32_t id, // 业务标识符
bool isMmsApp = true); // 是否为彩信应用
关键特性:
- 接收来自应用程序的短信发送请求
- 将请求转发给对应 SIM 卡槽的
SmsSendManager - 与数据持久化层交互,记录发送历史
SmsSendManager(发送管理器)
核心职责:
- 维护 GSM 和 CDMA 两种网络制式的发送器实例
- 根据当前网络状态选择合适的发送路径
- 实现发送队列与重试机制
- 协调网络策略管理器的决策执行
内部成员:
class SmsSendManager {
private:
int32_t slotId_; // SIM卡槽ID
std::shared_ptr<SmsSender> gsmSmsSender_; // GSM 发送器
std::shared_ptr<SmsSender> cdmaSmsSender_; // CDMA 发送器
std::shared_ptr<SmsNetworkPolicyManager> networkManager_; // 网络策略管理
std::shared_ptr<SmsShortCodeMatcher> smsShortCodeMatcher_;// 短码匹配器
};
发送流程分类:
| 方法 | 场景 | 特点 |
|---|---|---|
TextBasedSmsDelivery |
文本短信 | 支持自动分段,长短信打包发送 |
DataBasedSmsDelivery |
数据短信 | 指定端口号,用于数据应用通信 |
RetriedSmsDelivery |
重试短信 | 发送失败后的自动重试机制 |
SmsSender(发送器基类)
设计特点:采用模板方法模式的抽象基类
class SmsSender : public TelEventHandler {
public:
// 纯虚方法 - 子类必须实现
virtual void TextBasedSmsDelivery(...) = 0;
virtual void DataBasedSmsDelivery(...) = 0;
virtual void SendSmsToRil(...) = 0;
// 通用方法 - 所有Sender共享
void HandleMessageResponse(const std::shared_ptr<SmsSendIndexer> &smsIndexer);
static void SendResultCallBack(...);
};
职责分离:
- 基类定义通用流程:请求排队、结果回调与重试调度
- 子类实现制式特定功能:PDU 编码、发送格式与参数配置
GsmSmsSender 与 CdmaSmsSender
两个子类基于相同的基类框架,处理 GSM/CDMA 的特定编码与协议细节。
GSM 短信特点:
- 使用 7-bit GSM 编码或 UCS2 编码
- PDU 格式定义明确,严格按照 3GPP 规范
- 支持长短信的分段与连接标识头(Concatenation Header)
- 服务中心号码在PDU中显式指定
CDMA短信特点:
- 使用8-bit编码
- 消息参数采用TLV(Type-Length-Value)格式
- 多数参数可选,需要区分实际值与默认值
- 时间戳、优先级等参数独立传递
SmsNetworkPolicyManager(网络策略管理器)
职责定位:根据当前网络状态,决定短信应通过哪条网络路径传输。
核心决策逻辑:
输入条件检查:
├─ 网络注册状态(已注册/未注册)
├─ 域服务类型(CS域/IMS域)
├─ 信号强度与连接质量
├─ 用户配置的偏好设置
└─ 制式兼容性(设备是否支持该制式)
决策流程:
1. 优先级评估
- IMS短信可用 → 选择IMS路径
- CS 域可用 → 选择对应制式(GSM/CDMA)
- 都不可用 → 等待网络或返回错误
2. 队列管理
- 维护待发送队列
- 监听网络状态变化
- 状态好转时重新尝试发送
参数决策表:
| 场景 | 网络状态 | 决策 |
|---|---|---|
| 仅CS可用 | GSM 注册 | 使用GSM通道 |
| 仅CS可用 | CDMA 注册 | 使用CDMA通道 |
| CS 与 IMS 均可用 | 两者状态均良好 | IMS优先 |
| 都不可用 | 离线或无信号 | 加入等待队列,待网络恢复后重试 |
短信发送流程
完整发送链路
应用层请求
│
▼
SmsInterfaceManager::TextBasedSmsDelivery()
│ 权限检查 ▶ 验证发送权限
│ 参数验证 ▶ 检查目标地址、内容有效性
│
▼
SmsSendManager::TextBasedSmsDelivery()
│ 分段处理 ▶ 长短信自动分段
│ 短码识别 ▶ 识别是否为短码(安全检查)
│ 网络路由 ▶ 调用SmsNetworkPolicyManager确定通道
│
▼
SmsSender子类(GsmSmsSender / CdmaSmsSender)
│ 编码转换 ▶ 文本转GSM 7-bit / UCS2 / CDMA编码
│ PDU生成 ▶ 构建完整的通信协议数据单元
│ 参数打包 ▶ 组装发送参数
│
▼
SmsSendIndexer(发送索引)
│ 持久化存储 ▶ 记录到数据库,支持失败重试
│ 队列入队 ▶ 加入发送队列
│
▼
RIL层接口
│ 异步通信 ▶ 通过HDF通道向Modem下发AT命令
│
▼
Modem硬件
│ 无线发送 ▶ 将短信通过无线信号传递
│
▼
回调处理链
│ 立即反馈 ▶ Modem返回发送状态(成功/失败)
│ 结果回调 ▶ 触发ISendShortMessageCallback回调
│ 送达报告 ▶ 网络返回送达确认时触发IDeliveryShortMessageCallback
关键环节详解
1. 分段处理(Segmentation)
长短信(长度超过单条限制)需要自动分段处理。
分段逻辑:
- GSM 7-bit 编码:单条最多 160 字符 → 超过自动分段
- UCS2 编码:单条最多 70 字符 → 超过自动分段
- CDMA:单条最多 200 字符 → 超过自动分段
分段打包:
- 每条分段短信需添加连接标头(UDH - User Data Header)
- UDH 用于标明该分段在完整消息中的序号与总数
- 接收端根据UDH重组为完整消息
示例:一条300字符的GSM短信被分为2段
消息 = "这是一条300字符的短信..." (300 chars)
│
├─ 分段1 = [UDH标头] + 消息1-157字符
│ (关键字段:当前第1段,总共2段)
│
└─ 分段2 = [UDH标头] + 消息158-300字符
(关键字段:当前第2段,总共2段)
2. 编码转换(Encoding)
原始文本通过字符编码转换成 PDU 中的字节数据。
GSM 7-bit 编码特点:
- 最常用的短信编码
- 支持标准ASCII字符、数字、标点符号
- 某些字符需要转义(如
[,],{,}) - 编码后长度为原字符数
UCS2 编码特点:
- 支持中文、日文、韩文等多语言字符
- 每个字符占用2字节
- 编码后长度为原字符数的两倍
- 自动选择标准:当文本中包含中文字符或其他非 GSM 7-bit 字符集支持的字符时,采用 UCS2 编码
编码选择算法:
if (contain_chinese || contain_unicode_char) {
encoding = UCS2;
max_per_message = 70;
} else if (can_encode_with_7bit) {
encoding = GSM_7BIT;
max_per_message = 160;
} else {
encoding = UCS2; // 回退方案
max_per_message = 70;
}
3. 网络路由决策(Routing Decision)
系统需要根据实时网络状态决定通过哪条通道发送短信。
决策点:

网络状态监听:
系统通过 NetworkStateManager 订阅网络变化事件:
- 网络从离线变为在线时:启动待发队列的重试
- 制式从 GSM 切换为 CDMA 时:可能需要重新编码(需缓存原始文本)
- 信号强度显著下降时:延迟发送或增加重试次数
4. 数据库持久化(Persistence)
为支持发送失败后的重试机制,系统在数据库中记录待发送的短信。
SmsSendIndexer 数据结构

持久化时机
- 发送前:确保即使进程崩溃,数据也能恢复
- 重试前:更新重试次数和时间戳
- 发送成功后:标记为已完成(可选删除)
5. 重试机制(Retry Logic)
发送失败并非终点,系统采用指数退避算法进行重试。
重试策略:

重试触发条件:
- 网络状态恢复(从无信号到有信号)
- 定时器触发(指定延迟后自动重试)
- 通过后台服务定期扫描数据库中标记为“失败”的记录
重试触发代码路径:
void SmsSendManager::RetrySmsDelivery(
const std::shared_ptr<SmsSendIndexer> smsIndexer)
{
// 从数据库读取原始消息
// 重新走一遍编码、路由决策流程
// 如果仍然失败,增加retryCount
// 再次计算延迟后重新调度
}
6. 回调通知机制(Callback Notification)
异步操作完成后,通过 Binder IPC 通知应用层结果。
两类回调:
-
发送结果回调(
ISendShortMessageCallback)
void OnSmsSendComplete(ISendShortMessageCallback::SmsSendResult result, // 成功/失败/缺少权限等 std::string url // 可选的链接信息);
触发时机:Modem 返回发送结果(通常在 0.5–2 秒内)
-
送达报告回调(
IDeliveryShortMessageCallback)
void OnSmsDeliveryComplete(ISendShortMessageCallback::SmsSendResult result // 送达状态);
```raw触发时机:网络返回送达确认(可能延迟数秒至数分钟)
回调流程:
Modem 返回 AT 响应
│
▼
RIL 层解析响应
│
▼
TelRilManager 回调 SmsSender::ProcessEvent()
│
▼
SmsSender::HandleMessageResponse()
│
├─ 成功 → 调用 SendResultCallBack(result=SUCCESS)
├─ 失败 → 调用 SendResultCallBack(result=FAILURE)
│ 更新数据库状态为“失败”
│ 如果 retryCount < MAX:调度重试
│
▼
通过 Binder 通知应用的回调对象

[短信中心号码长度] [短信中心号码]
[消息类型标志] [消息参考]
[目标地址长度] [目标地址类型] [目标地址]
[协议标识符] [数据编码方案]
[消息内容长度] [消息内容]
```raw
**GsmSmsSender 实现职责**:
1. 将目标号码和短信中心号码从国际格式转换为PDU格式
2. 选择合适的编码(7-bit vs UCS2)
3. 构建UDH(如果需要分段)
4. 调用RIL接口`SendSms`下发命令
### CDMA制式短信
**编码特点**:
- 使用8-bit编码
- 消息结构为TLV(Tag-Length-Value)格式
- 更复杂的可选参数
**消息体示例(简化)**:
[消息类型标签] [长度] [内容]
├─ 消息参考(可选)
├─ 目标地址
├─ 原始消息体(文本内容)
├─ 消息显示模式
├─ 优先级
└─ 回复选项(可选)
**CdmaSmsSender实现职责**:
1. 解析CDMA网络参数
2. 构建TLV格式的消息体
3. 填充可选参数
4. 调用RIL接口`SendCdmaSms`下发命令
### 双卡场景处理
在支持双SIM的设备上,系统需要为每张卡维护独立的发送管理器。
**架构设计**:
```mermaid
flowchart TD
A["SmsService(单例)"] --> B["SmsInterfaceManager (slotId=0)"]
A --> C["SmsInterfaceManager (slotId=1)"]
B --> D["SmsSendManager"]
D --> E["GsmSmsSender"]
D --> F["CdmaSmsSender"]
C --> G["SmsSendManager"]
G --> H["GsmSmsSender"]
G --> I["CdmaSmsSender"]
关键点:
- 发送操作需指定目标卡槽 ID
- 不同卡槽的队列、重试计数完全独立
- 网络状态监听需区分卡槽
在支持双SIM的设备上,系统需要为每张卡维护独立的发送管理器。
架构设计:
SmsService(单例)
│
├─ SmsInterfaceManager (slotId=0)
│ ├─ SmsSendManager
│ │ ├─ GsmSmsSender
│ │ └─ CdmaSmsSender
│ └─ ...
│
└─ SmsInterfaceManager (slotId=1)
├─ SmsSendManager
│ ├─ GsmSmsSender
│ └─ CdmaSmsSender
└─ ...
**关键点**:
- 发送操作需指定目标卡槽 ID
- 不同卡槽的队列、重试计数完全独立
- 网络状态监听需区分卡槽
## 关键设计考量
### 可靠性设计
**问题**:短信发送失败会导致用户信息丢失。
**解决方案**:
1. **持久化存储**:发送前保存到数据库
2. **多次重试**:采用指数退避策略
3. **网络监听**:网络恢复时自动重试
4. **队列管理**:维护内存队列以支持快速重试
**可靠性保证**:
- 在网络恢复后的 120 秒内:99% 的失败短信可成功重试
- 发送失败后立即向用户返回结果,避免其长时间等待
- 用户可在消息应用中查看重试历史
### 性能优化
**问题**:大量短信并发发送会导致系统响应延迟。
**解决方案**:
1. **异步处理**:采用非阻塞 I/O 操作
2. **队列限制**:在内存中最多缓存1000条待发短信
3. **数据库批量操作**:减少 I/O 次数
4. **编码缓存**:避免重复的编码计算开销
**性能指标**:
- 单条短信从API调用到Modem响应:< 200ms
- 1000条短信发送队列处理:< 10秒
### 内存管理
**问题**:长期运行中可能出现内存泄漏。
**设计考虑**:
1. 使用智能指针(`std::shared_ptr`、`std::unique_ptr`)
2. 发送完成后,及时释放缓存的PDU与编码数据
3. 定期清理已过期的重试记录
4. 对各类对象池设置容量上限
### 安全性
**问题**:防止非授权应用发送短信。
**实现方式**:
1. **权限检查**:检查应用是否持有`SEND_MESSAGES`权限
2. **短码识别**:识别向短码(常用于诈骗)发送短信的行为
3. **频率限制**:限制单应用的发送频率(可选)
4. **审计日志**:记录所有短信发送操作
```cpp
// SmsSendManager中的权限检查示例
int32_t SmsSendManager::TextBasedSmsDelivery(...) {
if (!CheckPermission(ISmsServiceInterface::SEND_MESSAGES)) {
return PERMISSION_DENIED;
}
if (IsShortCode(desAddr)) {
// 警告用户或拒绝发送
LogSecurityWarning("Attempt to send to short code: " + desAddr);
}
// ... 继续处理
}
扩展性与可维护性
添加新的网络制式
要支持新的网络制式(如5G 独立组网的短信),需要:
-
创建新的Sender子类
class NewNetworkSmsSender : public SmsSender {
public:void TextBasedSmsDelivery(...) override; void DataBasedSmsDelivery(...) override; void SendSmsToRil(...) override;private:
// 新制式特定的编码逻辑};
-
注册到SmsSendManager
void SmsSendManager::Init() {gsmSmsSender_ = std::make_shared<GsmSmsSender>(...); cdmaSmsSender_ = std::make_shared<CdmaSmsSender>(...); newNetworkSender_ = std::make_shared<NewNetworkSmsSender>(...);}
-
更新网络策略管理器
// 在SmsNetworkPolicyManager中添加新制式的路由逻辑
if (IsNewNetworkAvailable()) {return newNetworkSender_;}
添加新的回调类型
如果应用层需要新的回调信息(如发送至哪个基站),需执行以下步骤:
-
扩展回调接口
interface ISendShortMessageCallback {void OnSmsSendComplete( SmsSendResult result, string url, string baseStationInfo // 新增字段 );}
-
在 SmsSender 中收集新信息
void SmsSender::HandleMessageResponse(...) {// 从 Modem 响应中提取基站信息 baseStationInfo = ParseBaseStationFromModemResponse(); // 传递给应用}
监控与诊断
系统通过以下指标进行健康度监控:
- 单小时发送成功率
- 发送失败后的平均重试次数
- 队列深度(未发送的待发短信数)
- 从发起到完成的平均处理耗时
诊断工具:
- 日志分析:检索特定号码的发送记录
- 状态导出功能:导出当前发送队列状态
- 模拟工具:注入失败场景进行压力测试
下面用 Mermaid 图展示四大设计考量之间的平衡关系:
flowchart TD
A["关键设计考量"] --> B["可靠性设计"]
A --> C["性能优化"]
A --> D["内存管理"]
A --> E["安全性"]
B --> B1["持久化存储"]
B --> B2["多次重试(指数退避)"]
B --> B3["网络监听自动重试"]
B --> B4["队列管理"]
C --> C1["异步非阻塞 I/O"]
C --> C2["队列上限 1000 条"]
C --> C3["数据库批量操作"]
C --> C4["编码缓存"]
D --> D1["智能指针管理"]
D --> D2["及时释放 PDU 缓存"]
D --> D3["定期清理过期记录"]
D --> D4["对象池容量上限"]
E --> E1["权限检查 SEND_MESSAGES"]
E --> E2["短码识别与告警"]
E --> E3["频率限制"]
E --> E4["审计日志"]
B -.->|"保障"| C
C -.->|"兼顾"| D
D -.->|"支撑"| B
E -.->|"贯穿"| B
E -.->|"贯穿"| C
style A fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px
style B fill:#e3f2fd,stroke:#1565c0
style C fill:#fff3e0,stroke:#e65100
style D fill:#f3e5f5,stroke:#7b1fa2
style E fill:#fce4ec,stroke:#c62828
总结
OpenHarmony 的短信能力依托 Telephony 子系统构建,是一套分层解耦、跨制式兼容的完整通信机制,核心原理与运行逻辑可从以下维度展开说明:
一、底层基础机制
短信本质是一种存储转发服务,发送方的短信内容会先提交至短信服务中心暂存,再由服务中心转发给目标接收方,内容以文本、数字或二进制数据为主要载体。OpenHarmony 的短信模块作为系统常驻核心服务,向下适配不同厂商的 Modem 硬件,向上为应用层提供标准化的 JS API 接口,同时兼容 GSM、CDMA 两大主流通信制式的短信协议规范。
二、分层架构原理
整个短信模块采用清晰的分层代码结构,各层职责明确:
1.接口层:对外暴露 JS 接口定义,通过 Napi 完成 C++ 层与 JS 层的调用封装,同时提供 Native 层内部调用接口;
2.框架层:承载彩信编解码工具、短信业务通用处理逻辑,实现跨硬件的能力抽象;
3.服务配置层:提供 SystemAbility 启动配置文件,确保短信服务随系统通信组件自动启动;
4.业务服务层:包含 GSM、CDMA 制式专属的 PDU 编解码逻辑,以及短信收发的核心业务处理代码;
5.工具层:提供短信通用处理工具,支撑长短信拆分、PDU数据解析等公共能力。
三、短信发送全流程原理
1.上层应用调用短信发送JS API,经过Napi层将请求转发至Native层的SmsInterfaceManager对象;
2.SmsInterfaceManager完成参数合法性过滤、应用权限鉴权,过滤无效请求;
3.请求转发至SmsSendManager,调用搜网服务获取当前SIM卡状态与网络制式信息;
4.根据网络制式自动创建GsmSmsSender或CdmaSmsSender,对长短信进行自动拆分,将文本内容编码为符合协议规范的PDU数据包;
5.编码完成的短信加入发送队列,通过RIL Adapter下发至底层Modem硬件,最终提交至运营商短信服务中心;
6.若发送失败,系统自动触发重发机制,多次重试后将最终发送状态回调给上层应用。
四、短信接收全流程原理
1.系统启动时,SmsReceiveManager会创建对应网络制式的短信接收处理器,提前向RIL层注册短信事件监听;
2.运营商网络下发短信时,Modem通过RIL Adapter将原始PDU数据上报至短信服务;
3.对应制式的接收处理器调用SmsBaseMessage组件,完成PDU数据的解析,还原出短信的发送方号码、内容、时间戳等核心信息;
4.系统自动完成短信存储、送达报告上报,同时将短信通知分发至系统短信应用,触发新消息提醒。
五、配套扩展机制
1.支持SIM卡内短信的增删改查管理,可直接读写SIM卡存储的短信记录;
2.内置Wap Push接收处理、小区广播接收能力,可处理运营商推送的特殊短信;
3.提供垃圾短信规则引擎,支持自定义关键词、正则匹配规则,自动拦截或标记垃圾短信;
4.跨平台适配层屏蔽硬件差异,在RK3568等搭载4G模块的开发板上,无需大幅修改代码即可完整实现短信收发全功能。
更多推荐



所有评论(0)