一、系统架构概览
Android 彩信处理涉及多个层级的组件:
应用层 (MmsManager/SmsManager)
↓
系统服务层 (MmsServiceBroker)
↓
运营商消息服务 (CarrierMessagingService)
↓
数据网络层 (DataNetwork/PhoneSwitcher)
↓
运营商 MMSC 服务器
二、彩信发送流程
2.1 应用端发起发送请求
应用通过 MmsManager 或 SmsManager 调用发送接口:
MmsManager.sendMultimediaMessage(subId, contentUri, locationUrl, configOverrides, sentIntent, messageId)
关键参数说明:
subId: 副卡的 Subscription ID (例如:副卡可能是 subId=2)contentUri: MMS PDU (Protocol Data Unit) 的内容 URI,从 ContentProvider 读取locationUrl: 可选,指定 MMSC (Multimedia Messaging Service Center) 服务器地址sentIntent: 发送结果回调 PendingIntentmessageId: 用于日志和诊断的唯一消息ID
2.2 MmsServiceBroker 权限检查与转发
MmsServiceBroker.BinderService.sendMessage()
├─ 权限检查 (SEND_SMS)
├─ 订阅关联性检查 (checkSubscriptionAssociatedWithUser)
├─ AppOps 权限检查 (OP_SEND_SMS)
├─ URI 权限调整和赋予 (adjustUriForUserAndGrantPermission)
└─ 转发到真实的 MmsService
代码路径:
public void sendMessage(int subId, String callingPkg, Uri contentUri,
String locationUrl, Bundle configOverrides, PendingIntent sentIntent,
long messageId, String attributionTag)
throws RemoteException {
// 权限检查
mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Send MMS message");
// 订阅检查
if (!TelephonyPermissions.checkSubscriptionAssociatedWithUser(mContext, subId,
Binder.getCallingUserHandle())
&& isActiveSubId(subId)) {
return;
}
// AppOps 检查
if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
callingPkg, attributionTag, null) != AppOpsManager.MODE_ALLOWED) {
return;
}
// 转发到真实服务
getServiceGuarded().sendMessage(subId, callingPkg, contentUri, locationUrl,
configOverrides, sentIntent, messageId, attributionTag);
}
2.3 MmsService 处理发送
MmsService (在 com.android.mms.service 进程中) 接收请求:
- 读取 MMS PDU 数据:从 ContentProvider 读取彩信内容
- 查询副卡配置:获取副卡对应的 MMSC URL、代理等配置
- 请求 MMS 网络:向 ConnectivityService 请求 MMS 数据连接
2.4 MMS 数据网络建立(多卡设备关键步骤)
在多卡设备上,系统需要处理数据连接的切换:
PhoneSwitcher.onRequestNetwork(NetworkRequest with NET_CAPABILITY_MMS)
├─ 检查多卡状态 (mActiveModemCount > 1)
├─ 将 MMS NetworkRequest 添加到列表
├─ 触发网络评估 (onEvaluate)
└─ 记录指标 (collectRequestNetworkMetrics)
关键代码:
private void onRequestNetwork(NetworkRequest networkRequest) {
TelephonyNetworkRequest telephonyNetworkRequest = new TelephonyNetworkRequest(
networkRequest, PhoneFactory.getDefaultPhone(), mFlags);
mNetworkRequestList.add(telephonyNetworkRequest);
onEvaluate(REQUESTS_CHANGED, "netRequest");
}
private void collectRequestNetworkMetrics(NetworkRequest networkRequest) {
// 在多卡设备上,MMS 请求会临时禁用默认数据SIM卡上的网络
if (mActiveModemCount > 1 && networkRequest.hasCapability(
NetworkCapabilities.NET_CAPABILITY_MMS)) {
OnDemandDataSwitch onDemandDataSwitch = new OnDemandDataSwitch();
onDemandDataSwitch.apn = TelephonyEvent.ApnType.APN_TYPE_MMS;
onDemandDataSwitch.state = TelephonyEvent.EventState.EVENT_STATE_START;
TelephonyMetrics.getInstance().writeOnDemandDataSwitch(onDemandDataSwitch);
}
}
2.5 DataNetwork 建立连接
DataNetworkController 根据 NetworkRequest 寻找合适的 DataProfile (APN配置):
DataNetworkController.createDataNetwork()
├─ 查询副卡的 DataProfile 列表
├─ 选择支持 MMS 的 APN (TYPE_MMS)
├─ 确定传输类型 (WWAN 或 IWLAN)
└─ 建立 DataNetwork
MMS 网络能力配置:
// 如果设备启用了 "IWLAN 上强制 MMS" 功能
if (mFlags.forceIwlanMms() && builder.build()
.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)
&& mDataConfigManager.isForceIwlanMmsFeatureEnabled()) {
// 获取 IWLAN 上的 MMS 数据配置
DataProfile dataProfile = mDataNetworkController.getDataProfileManager()
.getDataProfileForNetworkRequest(
new TelephonyNetworkRequest(
new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS)
.build(),
mPhone, mFlags),
TelephonyManager.NETWORK_TYPE_IWLAN, false, false, false);
if (dataProfile != null) {
log("Found MMS on IWLAN with APN: " + dataProfile.getApn());
}
}
2.6 通过运营商消息服务发送
CarrierMessagingService 拦截发送请求(如果运营商实现了该服务):
CarrierMessagingService.onSendMms(pduUri, subId, location, callback)
├─ 可选:修改 MMS 内容或配置
├─ 调用运营商特定的发送逻辑
└─ 返回发送结果给 MmsService
2.7 MMS PDU 通过 HTTP 发送到 MMSC
MmsService 通过建立的数据连接:
- 将 MMS PDU 编码为二进制格式
- 通过 HTTP POST 发送到 MMSC 服务器
- 接收 MMSC 的发送确认 (SendConf PDU)
2.8 发送结果回调
发送成功/失败 → PendingIntent.send() → 应用 Intent 接收者
三、彩信接收流程
3.1 WAP Push 通知接收
当副卡接收到新彩信时,运营商网络通过 WAP Push 通知设备:
RIL 层 → RadioIndication.onNewWapPdu()
├─ 解析 WAP Push PDU
├─ 提取 MMS 下载 Location URL
└─ 发送广播 Intent.DATA_SMSRECEIVED_ACTION
3.2 系统确定接收卡槽
系统识别哪个副卡收到通知
├─ 从 Intent 提取 slotIndex
├─ 映射到对应的 subId
└─ 后续操作基于此 subId
3.3 MMS 下载请求
应用或系统服务调用下载接口:
MmsManager.downloadMultimediaMessage(subId, locationUrl, contentUri, ...)
代码路径:
public void downloadMultimediaMessage(int subId, @NonNull String locationUrl,
@NonNull Uri contentUri, @Nullable Bundle configOverrides,
@Nullable PendingIntent downloadedIntent, long messageId) {
IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
iMms.downloadMessage(subId, ActivityThread.currentPackageName(),
locationUrl, contentUri, configOverrides, downloadedIntent,
messageId, mContext.getAttributionTag());
}
3.4 MmsServiceBroker 处理下载
MmsServiceBroker.downloadMessage()
├─ 权限检查 (RECEIVE_MMS)
├─ AppOps 检查 (OP_RECEIVE_MMS)
├─ URI 权限赋予 (读写权限)
└─ 转发到 MmsService
代码路径:
public void downloadMessage(int subId, String callingPkg, String locationUrl,
Uri contentUri, Bundle configOverrides, PendingIntent downloadedIntent,
long messageId, String attributionTag) throws RemoteException {
mContext.enforceCallingPermission(Manifest.permission.RECEIVE_MMS,
"Download MMS message");
if (getAppOpsManager().noteOp(AppOpsManager.OP_RECEIVE_MMS,
Binder.getCallingUid(), callingPkg, attributionTag, null)
!= AppOpsManager.MODE_ALLOWED) {
return;
}
contentUri = adjustUriForUserAndGrantPermission(contentUri,
CarrierMessagingService.SERVICE_INTERFACE,
Intent.FLAG_GRANT_READ_URI_PERMISSION |
Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
subId);
getServiceGuarded().downloadMessage(subId, callingPkg, locationUrl,
contentUri, configOverrides, downloadedIntent, messageId, attributionTag);
}
3.5 MmsService 建立 MMS 数据连接
同发送过程,建立副卡的 MMS 数据连接。
3.6 运营商消息服务拦截(可选)
CarrierMessagingService.onDownloadMms(contentUri, subId, location, callback)
├─ 可选:修改下载配置
├─ 调用运营商特定逻辑
└─ 返回下载结果
3.7 通过 HTTP 从 MMSC 下载
MmsService
├─ 通过建立的数据连接
├─ 向 Location URL 发送 HTTP GET 请求
├─ 接收 MMS PDU 数据
└─ 写入 ContentProvider (TelephonyProvider)
3.8 存储彩信并回调
MmsProvider 存储 MMS PDU
├─ 检查 subId 是否在请求体中
├─ 若无则使用默认彩信 subId
├─ 存储到数据库 (TABLE_PDU)
└─ 返回 URI
代码路径:
if (values.containsKey(Telephony.Sms.SUBSCRIPTION_ID)) {
subId = values.getAsInteger(Telephony.Sms.SUBSCRIPTION_ID);
} else {
// 未指定则用默认彩信 subId
subId = SmsManager.getDefaultSmsSubscriptionId();
if (SubscriptionManager.isValidSubscriptionId(subId)) {
values.put(Telephony.Sms.SUBSCRIPTION_ID, subId);
}
}
3.9 下载完成回调
下载成功/失败 → PendingIntent.send() → 应用下载结果接收者
四、多卡设备特殊处理
4.1 MMS 网络请求与数据切换
在多卡设备上,当发送/接收 MMS 时:
多卡设备 (DSDS)
├─ 副卡发送 MMS
├─ PhoneSwitcher 检测到 MMS NetworkRequest
├─ 如果当前是默认数据卡在使用,临时禁用其连接
├─ 为副卡建立 MMS 数据连接
├─ MMS 操作完成后释放 MMS 连接
└─ 恢复默认数据卡的连接
4.2 运营商配置的 MMS 政策
系统支持多种 MMS 相关的运营商政策:
KEY_MMS_CLOSE_CONNECTION_BOOL
└─ 在 MMS HTTP 请求中添加 "Connection: close" 头
KEY_MMS_NETWORK_RELEASE_TIMEOUT_MILLIS_INT
└─ MMS 数据连接释放前的等待时间(便于连续发送)
KEY_MOBILEDATA_POLICY_MMS_ALWAYS_ALLOWED (MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED)
└─ 即使用户关闭移动数据,也允许 MMS 建立连接
4.3 用户默认 MMS 卡选择
系统存储用户的默认 MMS 发送卡:
Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION
└─ 存储默认彩信发送卡的 subId
五、关键数据结构
5.1 NetworkRequest 能力标记
NetworkCapabilities.NET_CAPABILITY_MMS
├─ 标识该网络支持彩信服务
├─ 在 DataNetwork 建立时添加
└─ PhoneSwitcher 和 ConnectivityService 据此判断
5.2 APN 类型映射
ApnSetting.TYPE_MMS
↔ NetworkCapabilities.NET_CAPABILITY_MMS
↔ DataUtils.networkCapabilityToApnType()
// 双向转换
networkCapabilityToApnType(NET_CAPABILITY_MMS) → TYPE_MMS
apnTypeToNetworkCapability(TYPE_MMS) → NET_CAPABILITY_MMS
5.3 DataProfile 和 MMS 配置
DataProfile (包含 ApnSetting)
├─ apnName: "mms" 或运营商特定名称
├─ mmscUrl: MMSC 服务器地址
├─ mmsProxyAddress: MMS 代理 (可选)
├─ mmsProxyPort: MMS 代理端口 (可选)
└─ apnTypes: 包含 TYPE_MMS
六、流程时序图
【发送流程】
应用 ──sendMms──> MmsManager
↓
MmsServiceBroker (权限检查)
↓
MmsService (com.android.mms.service)
↓
CarrierMessagingService (可选拦截)
↓
请求 MMS 数据连接
↓ (多卡设备上)
PhoneSwitcher (评估网络)
↓
DataNetworkController
↓
DataNetwork (建立连接)
↓
RIL.setupDataCall()
↓
Modem 建立 PDP Context
↓
HTTP POST to MMSC
↓
MMSC 返回 SendConf PDU
↓
释放 MMS 数据连接
↓
PendingIntent 回调应用
【接收流程】
MMSC WAP Push ──> RIL (RadioIndication)
↓
提取 Location URL & slotIndex
↓
Intent 通知系统
↓
MmsManager.downloadMultimediaMessage()
↓
MmsServiceBroker (权限检查)
↓
MmsService (com.android.mms.service)
↓
CarrierMessagingService (可选拦截)
↓
请求 MMS 数据连接
↓
HTTP GET from Location URL
↓
接收 MMS PDU 数据
↓
TelephonyProvider 存储
↓
PendingIntent 回调应用
七、关键要点总结
-
SubId 追踪:整个流程中
subId是关键,标识请求来自哪张卡 -
权限多重检查:SEND_SMS/RECEIVE_MMS 权限 + AppOps + 订阅关联性检查
-
网络资源管理:多卡设备上 MMS 网络请求可能导致数据连接切换
-
运营商定制:CarrierMessagingService 提供钩点进行运营商特定处理
-
URI 权限:ContentUri 需要显式赋予权限给 CarrierMessagingService
-
异步处理:MMS 发送/接收是异步的,通过 PendingIntent 回调结果
-
多层代理:MmsServiceBroker 是中间代理,增加稳定性和权限隔离
这就是 Android 副卡彩信收发的完整流程!