我来分析一下这个仓库中短信和彩信的收发流程。让我查看相关的代码:
现在让我整理一下短彩信的收发流程:
Android Telephony 短信和彩信收发流程详解 📱💬
基于 frameworks-opt-telephony 代码分析,短信(SMS)和彩信(MMS)有不同但相关的处理流程。
一、短信发送流程(SMS Outbound) 📤
流程架构图
应用层 (App)
↓ SmsManager.sendTextMessage()
SmsController (系统服务接口)
↓
SmsDispatchersController (多卡管理)
↓
SMSDispatcher (抽象类)
├─ GsmSMSDispatcher (GSM/UMTS/LTE)
└─ CdmaSMSDispatcher (CDMA)
↓
运营商短信过滤器 (可选)
↓
Phone.mCi (CommandsInterface)
↓
RIL (Radio Interface Layer)
↓ HIDL/AIDL
HAL → Modem → SMSC (短信中心)
详细步骤
1. 应用发起短信请求
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(
destinationAddress, // 目标号码
scAddress, // 短信中心号码(可为null)
text, // 短信内容
sentIntent, // 发送结果通知
deliveryIntent // 送达报告通知
);
2. SmsController 接收请求
- 位置:
SmsController.java - 实现
ISmsImplBaseAIDL 接口 - 职责:
- 权限检查(
SEND_SMS权限) - 验证订阅 ID
- 路由到对应的 Phone 实例
- 权限检查(
/**
* Implements the ISmsImplBase interface used in the SmsManager API.
*/
public class SmsController extends ISmsImplBase {
static final String LOG_TAG = "SmsController";
private final Context mContext;
private final PackageManager mPackageManager;
private final int mVendorApiLevel;
@NonNull private final FeatureFlags mFlags;
@VisibleForTesting
public SmsController(Context context, @NonNull FeatureFlags flags) {
mContext = context;
mFlags = flags;
mPackageManager = context.getPackageManager();
ServiceRegisterer smsServiceRegisterer = TelephonyFrameworkInitializer
.getTelephonyServiceManager()
.getSmsServiceRegisterer();
if (smsServiceRegisterer.get() == null) {
smsServiceRegisterer.register(this);
}
mVendorApiLevel = SystemProperties.getInt(
"ro.vendor.api_level", Build.VERSION.DEVICE_INITIAL_SDK_INT);
}
private Phone getPhone(int subId) {
Phone phone = PhoneFactory.getPhone(SubscriptionManager.getPhoneId(subId));
if (phone == null) {
phone = PhoneFactory.getDefaultPhone();
}
return phone;
}
3. SmsDispatchersController 分发
- 根据当前网络类型选择 Dispatcher:
- GSM 网络 →
GsmSMSDispatcher - CDMA 网络 →
CdmaSMSDispatcher - IMS 网络 →
ImsSmsDispatcher
- GSM 网络 →
4. SMSDispatcher 处理核心逻辑
public abstract class SMSDispatcher extends Handler {
static final String TAG = "SMSDispatcher"; // accessed from inner class
static final boolean DBG = false;
private static final String SEND_NEXT_MSG_EXTRA = "SendNextMsg";
private static final String MESSAGE_ID_EXTRA = "MessageId";
protected static final String MAP_KEY_PDU = "pdu";
protected static final String MAP_KEY_SMSC = "smsc";
protected static final String MAP_KEY_DEST_ADDR = "destAddr";
protected static final String MAP_KEY_SC_ADDR = "scAddr";
protected static final String MAP_KEY_DEST_PORT = "destPort";
protected static final String MAP_KEY_DATA = "data";
protected static final String MAP_KEY_TEXT = "text";
private static final int PREMIUM_RULE_USE_SIM = 1;
private static final int PREMIUM_RULE_USE_NETWORK = 2;
private static final int PREMIUM_RULE_USE_BOTH = 3;
private final AtomicInteger mPremiumSmsRule = new AtomicInteger(PREMIUM_RULE_USE_SIM);
private final SettingsObserver mSettingsObserver;
/** SMS send complete. */
protected static final int EVENT_SEND_SMS_COMPLETE = 2;
/** Retry sending a previously failed SMS message */
protected static final int EVENT_SEND_RETRY = 3;
/** Confirmation required for sending a large number of messages. */
private static final int EVENT_SEND_LIMIT_REACHED_CONFIRMATION = 4;
/** Send the user confirmed SMS */
static final int EVENT_SEND_CONFIRMED_SMS = 5; // accessed from inner class
/** Don't send SMS (user did not confirm). */
static final int EVENT_STOP_SENDING = 6; // accessed from inner class
/** Don't send SMS for this app (User had already denied eariler.) */
static final int EVENT_SENDING_NOT_ALLOWED = 7;
/** Confirmation required for third-party apps sending to an SMS short code. */
private static final int EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE = 8;
/** Confirmation required for third-party apps sending to an SMS short code. */
private static final int EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE = 9;
关键检查:
- 费用检查:检测是否发送到付费短号码
- 使用限制:
SmsUsageMonitor防止短信轰炸 - 运营商过滤:
CarrierServicesSmsFilter运营商定制处理 - 权限确认:敏感操作需用户确认
5. 运营商短信服务包装(可选)
mCarrierMessagingServiceWrapper.sendTextSms(
text,
getSubId(),
mTracker.mDestAddress,
(mTracker.mDeliveryIntent != null)
? CarrierMessagingService.SEND_FLAG_REQUEST_DELIVERY_STATUS
: 0,
运营商可以通过 CarrierMessagingService 拦截和处理短信。
6. 编码和分片
- 文本编码:
- GSM 7-bit 编码(最多160字符)
- UCS-2/UTF-16 编码(最多70字符)
- 长短信自动分片(通过
SmsMessage.fragmentText()) - 添加 UDH(User Data Header)用于拼接
7. 通过 RIL 发送到 Modem
- GSM:
mCi.sendSMS()→RIL_REQUEST_SEND_SMS - CDMA:
mCi.sendCdmaSms()→RIL_REQUEST_CDMA_SEND_SMS - IMS:
mCi.sendImsGsmSms()/sendImsCdmaSms()
8. Modem 处理
- 构造 TPDU (Transfer Protocol Data Unit)
- 通过无线信道发送到 SMSC(短信中心)
- SMSC 转发到目标号码
9. 发送状态回调
EVENT_SEND_SMS_COMPLETE事件- 通知应用
sentIntent:RESULT_OK: 发送成功RESULT_ERROR_GENERIC_FAILURE: 通用失败RESULT_ERROR_RADIO_OFF: 无线关闭RESULT_ERROR_NO_SERVICE: 无服务
10. 送达报告(可选)
- 如果请求了
deliveryIntent - Modem 收到网络的 Status Report
- 通知应用短信已送达
二、短信接收流程(SMS Inbound) 📥
流程架构图
SMSC → Modem
↓ RIL_UNSOL_RESPONSE_NEW_SMS
RIL → Phone.mCi
↓
InboundSmsHandler (状态机)
├─ GsmInboundSmsHandler (GSM)
└─ CdmaInboundSmsHandler (CDMA)
↓
存储到 raw 表 (SMS Provider)
↓
发送 ACK 到 Modem
↓
判断完整性 (单条/多条拼接)
↓
广播给应用 (Ordered Broadcast)
↓
默认短信应用处理
↓
从 raw 表删除
详细步骤
1. Modem 接收短信
- 基站推送短信到设备
- Modem 解码 TPDU
- 通过 RIL 上报:
RIL_UNSOL_RESPONSE_NEW_SMS
2. InboundSmsHandler 状态机处理
/**
* This class broadcasts incoming SMS messages to interested apps after storing them in the
* SmsProvider "raw" table and ACKing them to the SMSC. After each message has been broadcast, its
* parts are removed from the raw table. If the device crashes after ACKing but before the broadcast
* completes, the pending messages will be rebroadcast on the next boot.
*
* <p>The state machine starts in {@link IdleState} state. When we receive a new SMS from the radio,
* the wakelock is acquired, then transition to {@link DeliveringState} state, where the message is
* saved to the raw table, then acknowledged to the modem which in turn acknowledges it to the SMSC.
*
* <p>After saving the SMS, if the message is complete (either single-part or the final segment of a
* multi-part SMS), we broadcast the completed PDUs as an ordered broadcast, then transition to
* {@link WaitingState} state to wait for the broadcast to complete. When the local
* {@link BroadcastReceiver} is called with the result, it sends {@link #EVENT_BROADCAST_COMPLETE}
* to the state machine, causing us to either broadcast the next pending message (if one has arrived
* while waiting for the broadcast to complete), or to transition back to the halted state after all
* messages are processed. Then the wakelock is released and we wait for the next SMS.
*/
状态机状态:
- IdleState(空闲):等待新短信
- DeliveringState(投递中):保存、ACK、广播
- WaitingState(等待):等待应用处理完成
3. 保存到数据库
- 写入
content://sms/raw表 - 多段短信保存所有分段
- 包含:PDU、发送者、时间戳、引用编号等
4. 发送 ACK 给网络
- 成功:
mCi.acknowledgeLastIncomingGsmSms(true, 0, null) - 失败(如存储满):发送 NACK
关键点:先保存再 ACK,防止崩溃丢失短信
5. 多段短信拼接
- 检查 UDH 中的引用编号、总段数、序号
- 从数据库查询相同引用的其他分段
- 所有分段到齐后组装完整消息
6. 广播给应用
- Intent:
SMS_DELIVER_ACTION/SMS_RECEIVED_ACTION - 有序广播:按优先级依次通知应用
- 运营商应用(最高优先级)
- 默认短信应用
- 其他监听应用
Intent intent = new Intent(Intents.SMS_DELIVER_ACTION);
intent.putExtra("pdus", pdus);
intent.putExtra("format", format);
context.sendOrderedBroadcast(intent, permission, ...);
7. 应用处理
- 默认短信应用将短信写入
content://sms/inbox - 可以通过
setResultCode(RESULT_CANCELED)阻止后续应用
8. 清理
- 广播完成后从 raw 表删除
- 状态机返回 IdleState
- 释放 WakeLock
9. 特殊类型短信
- Class 0 (Flash SMS):直接显示,不保存
- Status Report:送达报告,更新已发短信状态
- WAP Push:彩信通知,转给
WapPushOverSms处理 - SIM 短信:存储在 SIM 卡的短信
三、彩信发送流程(MMS Outbound) 📧
流程概览
彩信不通过 Telephony 框架直接发送,而是通过数据网络(HTTP):
彩信应用
↓
构造 MMS PDU (多媒体内容)
↓
请求 MMS 数据连接
TelephonyNetworkFactory
↓ NET_CAPABILITY_MMS
DataNetworkController
↓
建立 MMS APN 数据连接
↓
通过 HTTP POST 上传到 MMSC
↓
MMSC 发送 WAP Push 通知给接收方
详细步骤
1. 彩信应用准备
- 编码彩信内容(文本、图片、音频、视频)
- 构造 MMS PDU(使用
com.google.android.mms库) - 从 APN 配置读取 MMSC URL、代理地址
2. 请求 MMS 专用网络
NetworkRequest request = new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS)
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.setNetworkSpecifier(new TelephonyNetworkSpecifier(subId))
.build();
connectivityManager.requestNetwork(request, callback);
3. 建立 MMS APN 连接
TelephonyNetworkFactory识别 MMS 请求DataNetworkController选择 MMS 类型的 DataProfile- 建立专用的 PDN 连接(即使主数据关闭也可用)
4. HTTP 上传
- 通过 MMS APN 的网络接口
- POST 请求到 MMSC(如
http://mmsc.provider.com/mms) - 可能需要通过代理服务器
5. MMSC 处理
- 存储彩信内容
- 向接收方发送 WAP Push 通知(SMS DATA)
四、彩信接收流程(MMS Inbound) 📬
流程架构图
MMSC 发送 WAP Push 通知 (SMS DATA)
↓
Modem → RIL → InboundSmsHandler
↓
识别为 WAP Push (端口 2948/2949)
↓
WapPushOverSms.dispatchWapPdu()
↓
解析 MMS 通知 (NotificationInd)
↓
广播给彩信应用
↓
彩信应用请求 MMS 网络
↓
HTTP GET 下载完整彩信
↓
显示给用户
详细步骤
1. 接收 WAP Push 通知
- 作为特殊的 SMS DATA 消息接收
- 目标端口:2948(未加密)或 2949(加密)
InboundSmsHandler识别 WAP Push
2. WAP Push 解析
// Continue if PDU parsing fails: the default messaging app may successfully parse the
// same PDU.
GenericPdu parsedPdu = null;
try {
parsedPdu = new PduParser(intentData, shouldParseContentDisposition(subId)).parse();
} catch (Exception e) {
Rlog.e(TAG, "Unable to parse PDU: " + e.toString());
}
if (parsedPdu != null && parsedPdu.getMessageType() == MESSAGE_TYPE_NOTIFICATION_IND) {
final NotificationInd nInd = (NotificationInd) parsedPdu;
// save the WAP push message size so that if a download request is made for it
// while on a satellite connection we can check if the size is under the threshold
WapPushCache.putWapMessageSize(
nInd.getContentLocation(),
nInd.getTransactionId(),
nInd.getMessageSize()
);
if (nInd.getFrom() != null
&& BlockChecker.isBlocked(mContext, nInd.getFrom().getString(), null)) {
result.statusCode = Intents.RESULT_SMS_HANDLED;
return result;
}
}
/**
* Seek for application ID field in WSP header.
* If application ID is found, WapPushManager substitute the message
* processing. Since WapPushManager is optional module, if WapPushManager
* is not found, legacy message processing will be continued.
*/
if (pduDecoder.seekXWapApplicationId(index, index + headerLength - 1)) {
index = (int) pduDecoder.getValue32();
pduDecoder.decodeXWapApplicationId(index);
String wapAppId = pduDecoder.getValueString();
if (wapAppId == null) {
wapAppId = Integer.toString((int) pduDecoder.getValue32());
}
result.wapAppId = wapAppId;
String contentType = ((mimeType == null) ?
Long.toString(binaryContentType) : mimeType);
result.contentType = contentType;
if (DBG) Rlog.v(TAG, "appid found: " + wapAppId + ":" + contentType);
}
result.subId = subId;
result.phoneId = phoneId;
result.parsedPdu = parsedPdu;
result.mimeType = mimeType;
result.transactionId = transactionId;
result.pduType = pduType;
result.header = header;
result.intentData = intentData;
result.contentTypeParameters = pduDecoder.getContentParameters();
result.statusCode = Activity.RESULT_OK;
MMS 通知包含:
- Content-Location: 彩信下载 URL
- Message-Size: 彩信大小
- Expiry: 过期时间
- From: 发件人
- Subject: 主题
3. 广播给彩信应用
Intent intent = new Intent(Intents.WAP_PUSH_DELIVER_ACTION);
intent.setType("application/vnd.wap.mms-message");
intent.putExtra("data", pdu);
intent.putExtra("transactionId", transactionId);
context.sendOrderedBroadcast(intent, permission, ...);
4. 彩信应用下载
- 请求 MMS 网络连接
- 通过 HTTP GET 从 Content-Location 下载
- 下载完整的 MMS PDU
5. 解析和显示
- 解析 M-Retrieve.conf PDU
- 提取多媒体附件(图片、音频、视频)
- 保存到
content://mms数据库 - 显示给用户
6. 发送确认
- HTTP POST M-NotifyResp.ind 到 MMSC
- 通知 MMSC 彩信已接收
关键差异对比
| 特性 | SMS 短信 | MMS 彩信 |
|---|---|---|
| 传输方式 | 信令通道(Control Plane) | 数据通道(User Plane / HTTP) |
| 大小限制 | 160字符(GSM 7-bit)或70字符(UCS-2) | 几百KB到几MB(运营商限制) |
| 内容类型 | 纯文本 | 文本、图片、音频、视频 |
| 费用 | 按条计费 | 按数据量计费(或包月) |
| 送达方式 | 直接推送 | 通知→下载两步 |
| 网络要求 | 2G及以上 | 需要数据连接 |
| 接口 | RIL 命令 | TCP/IP + HTTP |
| 离线接收 | Modem 缓存 | MMSC 缓存 |
相关数据库表
SMS 数据库
content://sms/raw: 原始接收队列content://sms/inbox: 收件箱content://sms/sent: 已发送content://sms/outbox: 发送队列content://sms/failed: 发送失败
MMS 数据库
content://mms: 彩信消息content://mms/part: 彩信附件
关键类总结
发送相关
SmsController: 系统服务入口SMSDispatcher: 短信发送核心抽象类GsmSMSDispatcher/CdmaSMSDispatcher: 协议实现SmsUsageMonitor: 使用限制监控
接收相关
InboundSmsHandler: 接收处理状态机GsmInboundSmsHandler/CdmaInboundSmsHandler: 协议实现InboundSmsTracker: 追踪接收消息
彩信相关
WapPushOverSms: WAP Push 处理(彩信通知)WspTypeDecoder: WSP 协议解码DataNetworkController: MMS 数据连接管理
这就是 Android Telephony 中完整的短彩信收发流程!整个系统设计考虑了可靠性(先存储后ACK)、性能(状态机异步处理)和兼容性(支持多种协议)。