卫星通信短信收发流程完整分析

4 阅读13分钟

一、概述:卫星短信的特点与挑战

1.1 Android 16 卫星短信的两种架构

Android 16 中卫星短信存在两条完全不同的路径

路径机制适用场景AOSP 原生支持
运营商 NTN 漫游 SMS复用标准 3GPP SMS over NAS,通过 SmsDispatchersControllerDatagramDispatcherImsSmsDispatcher → RIL运营商 NB-IoT NTN 网络(如 T-Mobile Starlink)✅ 完整支持
OEM 卫星数据报通过 SatelliteManager.sendDatagram() 发送加密数据报,不经过标准 SMS 协议栈OEM 私有卫星(天通、铱星 SBD 等)✅ 完整支持

关键设计决策:运营商 NTN 漫游模式下,标准 P2P SMS 不经过 SatelliteManager.sendDatagram(),而是走标准 SMS 协议栈,由 SmsDispatchersController 判断当前处于 NTN 漫游后,将 SMS 路由到 DatagramDispatcher 协调发送。

1.2 核心挑战

挑战地面 SMS卫星 SMS影响
传播延迟1-5秒5-30秒 (LEO) / 30-120秒 (GEO)超时判断、用户体验
间歇性覆盖持续覆盖卫星过境才可用存储转发、MT SMS 轮询
消息长度160字符 (GSM)更短(北斗140字节、铱星340字节)分片策略
功耗高(需对星、发射功率大)接收窗口调度
双向通信实时半双工或受限MT SMS 需主动轮询

二、短信发送流程

2.1 运营商 NTN 漫游 SMS 发送流程

这是 Android 16 中最完整的卫星 SMS 实现,代码路径清晰可追踪。

┌─────────────────────────────────────────────────────────────────────────────┐
│ 运营商 NTN 漫游 SMS 发送流程                                                 │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ① 应用层发起                                                               │
│  SmsManager.sendTextMessage(destAddr, scAddr, text, sentIntent, delivery)  │
│    │ → IccSmsInterfaceManager.sendText()                                    │
│    │ → SmsDispatchersController.sendText()                                  │
│    ▼                                                                       │
│  ② NTN 漫游判断与路由                                                       │
│  SmsDispatchersController.sendText()                                        │
│    │ → SatelliteController.shouldSendSmsToDatagramDispatcher(phone)         │
│    │   条件:                                                                │
│    │   1. isInCarrierRoamingNbIotNtn(phone) == true                         │
│    │      - 卫星已启用                                                       │
│    │      - 运营商支持卫星 (isSatelliteSupportedViaCarrier)                  │
│    │      - 连接类型为 MANUAL (CARRIER_ROAMING_NTN_CONNECT_MANUAL)           │
│    │      - subId 匹配卫星订阅                                               │
│    │   2. isDemoModeEnabled() == false                                      │
│    │   3. getSupportedServicesOnCarrierRoamingNtn() 包含 SERVICE_TYPE_SMS   │
│    │                                                                       │
│    ├── shouldSendSmsToDatagramDispatcher == true                            │
│    │   → DatagramDispatcher.sendSms(pendingRequest)  ← 卫星路径             │
│    │                                                                       │
│    ├── shouldSendSmsToDatagramDispatcher == false                           │
│    │   && isInCarrierRoamingNbIotNtn == true                                │
│    │   → triggerSentIntentForFailure()  ← 阻止SMS(P2P不支持时)            │
│    │                                                                       │
│    └── 否则 → sendTextInternal()  ← 标准地面路径                             │
│                                                                             │
│  ③ DatagramDispatcher 处理                                                  │
│  DatagramDispatcher.sendSms(pendingSms)                                     │
│    │ → SatelliteController.startPointingUI()  // 启动对星UI                  │
│    │ → mPendingSmsMap.put(messageId, pendingSms)  // 加入待发队列            │
│    │                                                                       │
│    │ 判断卫星连接状态:                                                       │
│    ├── needsWaitingForSatelliteConnected() == true                          │
│    │   → updateSendStatus(WAITING_TO_CONNECT)                               │
│    │   → startDatagramWaitForConnectedStateTimer()                          │
│    │                                                                       │
│    ├── !mSendingInProgress && isPollingInIdleState()                        │
│    │   → mSendingInProgress = true                                          │
│    │   → updateSendStatus(SENDING)                                          │
│    │   → sendMessage(CMD_SEND_SMS, pendingSms)                              │
│    │                                                                       │
│    └── 否则 → 排队等待(mSendingInProgress=true 或正在接收)                  │
│    ▼                                                                       │
│  ④ CMD_SEND_SMS 处理                                                        │
│  DatagramDispatcher.handleMessage(CMD_SEND_SMS)                             │
│    │ → satellitePhone = SatelliteController.getSatellitePhone()             │
│    │ → smsDispatchersController = satellitePhone.getSmsDispatchersController│
│    │ → mSmsTransmissionStartTime = System.currentTimeMillis()               │
│    │ → smsDispatchersController.sendCarrierRoamingNbIotNtnText(request)     │
│    ▼                                                                       │
│  ⑤ SmsDispatchersController 回调到标准 SMS 栈                               │
│  sendCarrierRoamingNbIotNtnText(request)                                    │
│    │ → sendMessage(CMD_SEND_TEXT, request)                                  │
│    │ → sendTextInternal(request)                                            │
│    │   → mImsSmsDispatcher.sendText(...)  // 走IMS SMS通道                  │
│    │     → RIL.sendImsGsmSms() → Modem → 卫星NAS → SMSC                    │
│    ▼                                                                       │
│  ⑥ 发送结果回调                                                             │
│  SmsDispatchersController.onSmsSentResult()                                 │
│    │ → if (shouldSendSmsToDatagramDispatcher && isLastSmsPart)              │
│    │     DatagramDispatcher.onSendSmsDone(subId, messageId, success)        │
│    ▼                                                                       │
│  ⑦ DatagramDispatcher 处理发送结果                                           │
│  handleEventSendSmsDone(subId, messageId, success)                          │
│    │ → pendingSms = mPendingSmsMap.remove(messageId)                       │
│    │ → mSendingInProgress = false                                           │
│    │                                                                       │
│    ├── success == true                                                      │
│    │   → updateSendStatus(SEND_SUCCESS)                                     │
│    │   → if (isMtSmsPolling) startMtSmsPollingThrottle()                   │
│    │   → reportSendSmsCompleted(SATELLITE_RESULT_SUCCESS)                   │
│    │                                                                       │
│    └── success == false                                                     │
│        → updateSendStatus(SEND_FAILED)                                      │
│        → reportSendSmsCompleted(SATELLITE_RESULT_NETWORK_ERROR)             │
│    │                                                                       │
│    → if (pendingMessages > 0) sendPendingMessages()                         │
│    → else updateSendStatus(IDLE)                                            │
│    └─────────────────────────────────────────────────────────────────────────┘

2.2 P2P SMS 禁用判断

运营商可能不允许 P2P SMS,此时 SMS 会被阻止:

// SmsDispatchersController.java L1885-1894
if (SatelliteController.getInstance().shouldSendSmsToDatagramDispatcher(mPhone)) {
    // Send P2P SMS using carrier roaming NB IOT NTN
    DatagramDispatcher.getInstance().sendSms(pendingRequest);
    return;
} else if (SatelliteController.getInstance().isInCarrierRoamingNbIotNtn()) {
    // Block SMS in satellite mode if P2P SMS is not supported.
    triggerSentIntentForFailure(pendingRequest.sentIntents);
    return;
}

P2P SMS 禁用条件SatelliteController.java:9594):

public boolean isP2PSmsDisallowedOnCarrierRoamingNtn(int subId) {
    int carrierRoamingNtnConnectType = getCarrierRoamingNtnConnectType(subId);
    if (carrierRoamingNtnConnectType == CARRIER_ROAMING_NTN_CONNECT_MANUAL) {
        if (!isNtnSmsSupportedByMessagesApp()
                || !isApplicationSupportsP2P(getSatelliteGatewayServicePackageName())) {
            return true;  // 短信应用或网关应用不支持P2P
        }
        if (!isSubscriptionProvisioned(subId)) {
            return true;  // 订阅未开通
        }
    }
    return false;
}

2.3 OEM 卫星数据报发送流程

OEM 卫星模式(天通/铱星等)使用完全不同的路径——SatelliteManager.sendDatagram()

┌─────────────────────────────────────────────────────────────────────────────┐
│ OEM 卫星数据报发送流程                                                       │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ① 应用层发起                                                               │
│  SatelliteManager.sendDatagram(datagram, datagramType, needPointingUI)     │
│    │ → ISatelliteService.sendSatelliteDatagram()                            │
│    │ → SatelliteService → SatelliteController                               │
│    ▼                                                                       │
│  ② DatagramController 处理                                                  │
│  DatagramController.sendSatelliteDatagram(subId, datagramType, datagram)    │
│    │ → 检查卫星状态(已连接、已对星)                                         │
│    │ → DatagramDispatcher.sendSatelliteDatagram()                           │
│    ▼                                                                       │
│  ③ DatagramDispatcher 下发到 Modem                                          │
│    │ → SatelliteModemInterface.sendSatelliteDatagram(datagram, isEmergency) │
│    │ → mSatelliteService.sendSatelliteDatagram()  // HAL AIDL              │
│    │ → Modem 固件 → 卫星链路                                                │
│    ▼                                                                       │
│  ④ 结果回调                                                                 │
│  Modem → HAL → SatelliteModemInterface → DatagramDispatcher                │
│    → DatagramController.updateSendStatus() → SatelliteManager callback     │
│    └─────────────────────────────────────────────────────────────────────────┘

关键区别:OEM 数据报路径不经过 SmsDispatchersController,消息由应用自行编码为加密数据报,框架不解析内容。


三、短信接收流程

3.1 运营商 NTN 漫游 SMS 接收流程

NTN 漫游模式下,MT SMS 的接收有一个独特机制——终端需要主动轮询(MT SMS Polling),因为卫星链路无法像地面网络那样实时推送 Paging。

┌─────────────────────────────────────────────────────────────────────────────┐
│ 运营商 NTN 漫游 SMS 接收流程                                                 │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ① MT SMS 轮询触发                                                          │
│  DatagramDispatcher                                                         │
│    │ 触发条件:                                                               │
│    │ - 卫星 Modem 进入 CONNECTED 状态(首次连接时)                           │
│    │ - 卫星对准成功(isAligned == true)                                     │
│    │ - MT SMS 轮询节流超时后                                                 │
│    │                                                                       │
│    │ allowMtSmsPolling() 检查:                                              │
│    │ - !isP2PSmsDisallowedOnCarrierRoamingNtn(subId)                        │
│    │ - Modem状态为 CONNECTED 或 DATAGRAM_TRANSFERRING                       │
│    │ - !mIsMtSmsPollingThrottled (未被节流)                                 │
│    │ - mIsAligned == true (已对星)                                          │
│    │ - isEnabledMtSmsPolling() (配置启用)                                   │
│    │ - shouldPollMtSms() (卫星控制器允许)                                    │
│    ▼                                                                       │
│  ② 构造轮询 SMS                                                             │
│  SmsDispatchersController.sendMtSmsPollingMessage()                         │
│    │ → destAddr = 自己的手机号 (SubscriptionManager.getPhoneNumber)          │
│    │ → mtSmsPollingText = config_mt_sms_polling_text (配置的轮询文本)        │
│    │ → 创建 PendingRequest(isMtSmsPolling=true)                             │
│    │ → DatagramDispatcher.sendSms(pendingRequest)                           │
│    ▼                                                                       │
│  ③ 轮询 SMS 发送(同发送流程)                                                │
│  DatagramDispatcher → CMD_SEND_SMS → SmsDispatchersController              │
│    → sendCarrierRoamingNbIotNtnText → ImsSmsDispatcher → RIL → Modem      │
│    → 卫星链路 → SMSC                                                         │
│    │                                                                       │
│    │ SMSC 收到发往自己的 SMS 后,                                             │
│    │ 将所有待投递的 MT SMS 下发给终端                                         │
│    ▼                                                                       │
│  ④ MT SMS 到达 Modem                                                        │
│  Modem → RIL_UNSOL_RESPONSE_NEW_SMS                                        │
│    → RILJ → mSmsRegistrant.notifyRegistrant()                              │
│    → SmsDispatchersController.handleMessage(EVENT_NEW_SMS)                  │
│    → InboundSmsHandler → 写入 content://sms                                 │
│    → SMS_RECEIVED_ACTION 广播                                               │
│    → 默认短信应用显示新消息                                                   │
│    ▼                                                                       │
│  ⑤ 轮询结果处理                                                             │
│  handleEventSendSmsDone(subId, messageId, success)                          │
│    │ datagramType = DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS                │
│    │ if (success) startMtSmsPollingThrottle()                               │
│    │                                                                       │
│    │ MT SMS 轮询节流:                                                       │
│    │ mIsMtSmsPollingThrottled = true                                        │
│    │ sendMessageDelayed(EVENT_MT_SMS_POLLING_THROTTLE_TIMED_OUT,            │
│    │     config_mt_sms_polling_throttle_millis)                             │
│    └─────────────────────────────────────────────────────────────────────────┘

3.2 MT SMS 轮询的核心设计

为什么需要轮询? 卫星网络中,终端无法像地面网络那样持续监听 Paging 信道(功耗太高、卫星可能不在上空)。因此采用"终端主动询问 SMSC 是否有待收消息"的模式。

轮询 SMS 的构造SmsDispatchersController.java:2264):

public void sendMtSmsPollingMessage() {
    // 目标地址 = 自己的手机号
    String destAddr = subscriptionManager.getPhoneNumber(mPhone.getSubId());
    // 轮询文本 = 配置的固定文本
    String mtSmsPollingText = mContext.getResources()
            .getString(R.string.config_mt_sms_polling_text);
    // 创建轮询请求
    PendingRequest pendingRequest = new PendingRequest(TYPE_TEXT, null,
            callingPackage, ..., destAddr, scAddr, ..., asArrayList(mtSmsPollingText),
            ..., true /*isMtSmsPolling*/, true /*skipShortCodeCheck*/, ...);
    DatagramDispatcher.getInstance().sendSms(pendingRequest);
}

轮询节流机制DatagramDispatcher.java:1392):

private void startMtSmsPollingThrottle() {
    mIsMtSmsPollingThrottled.set(true);
    sendMessageDelayed(obtainMessage(EVENT_MT_SMS_POLLING_THROTTLE_TIMED_OUT),
            getMtSmsPollingThrottleMillis());  // config_mt_sms_polling_throttle_millis
}

3.3 OEM 卫星数据报接收流程

┌─────────────────────────────────────────────────────────────────────────────┐
│ OEM 卫星数据报接收流程                                                       │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ① Modem 接收卫星数据报                                                     │
│  Modem → SatelliteModemInterface.onSatelliteDatagramReceived()              │
│    → DatagramReceiver.pollPendingSatelliteDatagrams()                       │
│    → SatelliteModemInterface.pollPendingSatelliteDatagrams()                │
│    → mSatelliteService.pollPendingSatelliteDatagrams()  // HAL AIDL        │
│    → Modem 返回 SatelliteDatagram + pendingCount                            │
│    ▼                                                                       │
│  ② DatagramReceiver 处理                                                    │
│  EVENT_POLL_PENDING_SATELLITE_DATAGRAMS_DONE                                │
│    │ → 解析 SatelliteDatagram + pendingCount                                │
│    │                                                                       │
│    ├── pendingCount <= 0 && datagram == null                                │
│    │   → updateReceiveStatus(RECEIVE_NONE)  // 无待收消息                    │
│    │                                                                       │
│    └── datagram != null                                                     │
│        → updateReceiveStatus(RECEIVE_SUCCESS)                               │
│        → datagramId = getDatagramId()  // 持久化ID                          │
│        → insertDatagram(datagramId, datagram)  // 写入DB                    │content://satellite_datagrams                                      │
│        → 通知所有注册的 ISatelliteDatagramCallback:                          │
│          listener.onSatelliteDatagramReceived(datagramId, datagram,         │
│              pendingCount, ackCallback)                                     │
│        → 启动 ACK 超时定时器:                                                │
│          sendMessageDelayed(EVENT_RETRY_DELIVERING_RECEIVED_DATAGRAM,       │
│              config_timeout_to_receive_delivered_ack_millis)                │
│    ▼                                                                       │
│  ③ 应用层接收                                                               │
│  ISatelliteDatagramCallback.onSatelliteDatagramReceived()                   │
│    → 卫星消息应用解析数据报                                                   │
│    → 调用 ackCallback.accept() 确认接收                                     │
│    ▼                                                                       │
│  ④ ACK 处理与重试                                                           │
│  EVENT_RECEIVED_ACK                                                         │
│    │ → pendingAckCount -= 1                                                 │
│    │ → removeMessages(EVENT_RETRY_DELIVERING_RECEIVED_DATAGRAM)             │
│    │ → if (pendingAckCount <= 0) deleteDatagram(datagramId)                │
│                                                                             │
│  EVENT_RETRY_DELIVERING_RECEIVED_DATAGRAM                                   │
│    → 重新调用 onSatelliteDatagramReceived()  // 5分钟未ACK则重试             │
│                                                                             │
│  ⑤ 继续轮询                                                                 │
│  if (pendingCount > 0)                                                      │
│    → pollPendingSatelliteDatagramsInternal()  // 继续拉取下一条              │
│    └─────────────────────────────────────────────────────────────────────────┘

3.4 SMS 接收通知(运营商 NTN)

当运营商 NTN 漫游模式下收到标准 SMS 时,DatagramController 会更新接收状态:

// DatagramController.java L375-386
void onSmsReceived(int subId) {
    // 快速通知三个状态:RECEIVING → RECEIVE_SUCCESS → IDLE
    updateReceiveStatus(subId, SatelliteManager.DATAGRAM_TYPE_SMS,
            SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING, ...);
    updateReceiveStatus(subId, SatelliteManager.DATAGRAM_TYPE_SMS,
            SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS, ...);
    updateReceiveStatus(subId, SatelliteManager.DATAGRAM_TYPE_SMS,
            SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, ...);
}

四、卫星短信特有机制

4.1 长延迟与重试

发送队列与串行化

DatagramDispatcher 使用 mPendingSmsMap(LinkedHashMap)维护待发 SMS 队列,严格串行发送

// DatagramDispatcher.java
private final LinkedHashMap<Long, PendingRequest> mPendingSmsMap = new LinkedHashMap<>();
private AtomicBoolean mSendingInProgress = new AtomicBoolean(false);
  • 同一时刻只有一条 SMS 在发送(mSendingInProgress 标志)
  • 发送完成后才处理下一条(sendPendingMessages()
  • 若正在接收数据报,也等待接收完成(isPollingInIdleState()

等待卫星连接

若 SMS 到达时卫星未连接,进入等待状态:

// DatagramDispatcher.java L1167-1172
if (mDatagramController.needsWaitingForSatelliteConnected(datagramType)) {
    mDatagramController.updateSendStatus(subId, datagramType,
            SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT, ...);
    startDatagramWaitForConnectedStateTimer(datagramType);
}

ACK 超时重试(OEM 数据报模式)

// DatagramReceiver.java L249-252
private int getTimeoutToReceiveAck() {
    return sInstance.mContext.getResources().getInteger(
            R.integer.config_timeout_to_receive_delivered_ack_millis);  // 默认5分钟
}

5分钟内未收到应用 ACK,则重新投递数据报。

4.2 受限消息长度

卫星系统最大消息长度分片策略
3GPP NTN SMS标准 GSM SMS 160字符标准 SMS 分片(concat ref)
北斗 RDSS 短报文140字节(民用)/ 1000字节(授权)应用层分段
铱星 SBD340字节应用层分段
天通 SMS~140字节标准 SMS 分片
OEM 数据报SatelliteCapabilities 声明框架不限制,应用负责

运营商 NTN SMS 的长短信处理

SmsDispatchersControllersendMultipartText() 同样支持 NTN 漫游路由:

// SmsDispatchersController.java L2056-2065
if (SatelliteController.getInstance().shouldSendSmsToDatagramDispatcher(mPhone)) {
    // Send multipart P2P SMS using carrier roaming NB IOT NTN
    DatagramDispatcher.getInstance().sendSms(pendingRequest);
    return;
} else if (SatelliteController.getInstance().isInCarrierRoamingNbIotNtn()) {
    // Block SMS in satellite mode if P2P SMS is not supported.
    triggerSentIntentForFailure(pendingRequest.sentIntents);
    return;
}

发送完成回调仅触发一次(最后一片时):

// SmsDispatchersController.java L1272-1276
if (SatelliteController.getInstance().shouldSendSmsToDatagramDispatcher(mPhone)
        && isLastSmsPart) {
    DatagramDispatcher.getInstance().onSendSmsDone(
            mPhone.getSubId(), messageId, success);
}

4.3 紧急短信优先级

OEM 数据报模式:紧急数据报(SOS_MESSAGE)获得更高优先级:

// DatagramDispatcher.java L104-105
private AtomicBoolean mIsEmergencyCommunicationEstablished = new AtomicBoolean(false);

紧急数据报标记 isEmergency=true,Modem 可能给予更高的卫星接入优先级。

运营商 NTN SMS:紧急号码检测:

// SmsDispatchersController.java L2070-2075
private boolean isEmergencyNumber(String number) {
    if (!mPhone.hasCalling()) return false;
    TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
    if (tm == null) return false;
    return tm.isEmergencyNumber(number);
}

紧急 SMS 会触发 EmergencyStateTracker.onEmergencySmsReceived()

4.4 波束切换时的 SMS 处理

发送中切换

若卫星 Modem 状态变化(波束切换、卫星切换),DatagramDispatcher 收到 EVENT_SATELLITE_MODEM_STATE_CHANGED

// DatagramDispatcher.java L429-438
case EVENT_SATELLITE_MODEM_STATE_CHANGED: {
    SomeArgs args = (SomeArgs) msg.obj;
    int state = (int) args.arg1;
    handleEventSatelliteModemStateChanged(state);
    break;
}
  • 若切换到 NOT_CONNECTED:等待重新连接,mPendingSmsMap 保留未发送的 SMS
  • 若切换回 CONNECTED:触发 sendPendingMessages() 继续发送
  • 若卫星被禁用:cleanUpResources() 清理所有待发 SMS 并返回错误

接收中切换

DatagramReceiver 同样监听 Modem 状态变化,若重新连接后自动触发 pollPendingSatelliteDatagrams()

4.5 SMS 投递策略(shouldDropSms)

当终端处于 NTN 漫游但不支持 SMS 时,SMS 会被丢弃:

// SatelliteController.java L9792-9803
public boolean shouldDropSms(@Nullable Phone phone) {
    if (!isInCarrierRoamingNbIotNtn(phone)) {
        return false;
    }
    int[] services = getSupportedServicesOnCarrierRoamingNtn(phone.getSubId());
    return !ArrayUtils.contains(services, NetworkRegistrationInfo.SERVICE_TYPE_SMS);
}

五、与地面 SMS 的对比表格

对比维度地面 SMS运营商 NTN SMSOEM 卫星数据报
协议栈3GPP TS 23.040/24.0113GPP SMS over NAS over NTN厂商私有(加密数据报)
发送路径SmsManager → SmsDispatcher → RIL → ModemSmsManager → SmsDispatchersController → DatagramDispatcher → ImsSmsDispatcher → RILSatelliteManager.sendDatagram → DatagramController → SatelliteModemInterface → HAL
接收路径Modem → RIL_UNSOL → InboundSmsHandler → DB → 广播同左 + DatagramController.onSmsReceived() 通知Modem → HAL → DatagramReceiver.poll → DB → ISatelliteDatagramCallback
MT 触发方式网络主动 Paging 推送终端主动轮询(MT SMS Polling)终端主动轮询(pollPendingDatagrams)
典型发送时延1-5秒5-30秒 (LEO)5-60秒
典型接收时延即时轮询间隔 + 传播延迟轮询间隔 + 传播延迟
消息长度160字符/条160字符/条(标准SMS分片)取决于卫星能力
送达报告支持(deliveryIntent)支持(但延迟大)不支持(应用层实现)
存储转发SMSC 存储SMSC 存储 + 终端轮询拉取卫星网关存储 + 终端轮询
ACK 机制CP-ACK + RP-ACK同左应用层 ACK(5分钟超时重试)
发送队列无(并发发送)严格串行(mSendingInProgress)严格串行
对星要求不需要需要(isAligned)需要(isAligned)
功耗
Android APISmsManagerSmsManager(透明)SatelliteManager.sendDatagram
数据存储content://smscontent://smscontent://satellite_datagrams
3GPP 标准TS 23.040/24.011TS 23.040 + R17 NTN无标准

六、Android 关键代码与配置位置

6.1 核心类索引

路径卫星 SMS 职责
SmsDispatchersControllerSmsDispatchersController.javaSMS 路由决策:NTN 漫游→DatagramDispatcher,否则→标准路径
DatagramDispatcherDatagramDispatcher.java卫星 SMS 发送协调:队列管理、串行发送、MT SMS 轮询
DatagramReceiverDatagramReceiver.java卫星数据报接收:轮询、ACK 管理、重试投递
DatagramControllerDatagramController.java数据报收发状态管理、SMS 接收通知
SatelliteControllerSatelliteController.javaNTN 漫游判断、SMS 服务策略、P2P 禁用判断
SatelliteModemInterfaceSatelliteModemInterface.javaHAL 接口:sendDatagram/pollDatagrams
ImsSmsDispatchertelephony/imsphone/ImsSmsDispatcher.javaNTN SMS 最终通过 IMS SMS 通道发送

6.2 关键方法索引

方法行号说明
shouldSendSmsToDatagramDispatcher()SatelliteControllerL9773判断 SMS 是否应走卫星路径
isInCarrierRoamingNbIotNtn()SatelliteControllerL4816判断是否在 NTN 漫游模式
isP2PSmsDisallowedOnCarrierRoamingNtn()SatelliteControllerL9594P2P SMS 是否被禁用
shouldDropSms()SatelliteControllerL9792SMS 是否应被丢弃
sendSms()DatagramDispatcherL1151卫星 SMS 发送入口
handleEventSendSmsDone()DatagramDispatcherL1282SMS 发送结果处理
sendMtSmsPollingMessage()SmsDispatchersControllerL2264MT SMS 轮询消息发送
allowMtSmsPolling()DatagramDispatcherL1404MT SMS 轮询允许判断
sendCarrierRoamingNbIotNtnText()SmsDispatchersControllerL2240NTN SMS 发送到标准 SMS 栈
onSmsReceived()DatagramControllerL375SMS 接收状态通知
pollPendingSatelliteDatagrams()SatelliteModemInterfaceL907轮询待收数据报
onSatelliteDatagramReceived()DatagramReceiverL320数据报投递到应用

6.3 关键配置

配置类型说明
config_enabled_mt_sms_pollingbool是否启用 MT SMS 轮询
config_mt_sms_polling_throttle_millisintegerMT SMS 轮询节流时间
config_mt_sms_polling_textstringMT SMS 轮询消息文本
config_timeout_to_receive_delivered_ack_millisinteger数据报 ACK 超时(默认5分钟)
KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INTCarrierConfigNTN 连接类型(MANUAL/AUTO)
KEY_SATELLITE_NTN_SMS_SUPPORTED_BOOLCarrierConfig是否支持 NTN SMS

6.4 数据报类型常量

常量说明
DATAGRAM_TYPE_SOS_MESSAGE0SOS 紧急消息
DATAGRAM_TYPE_LOCATION_SHARING1位置共享
DATAGRAM_TYPE_KEEP_ALIVE2保活消息
DATAGRAM_TYPE_SMS3P2P SMS(运营商 NTN)
DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS4MT SMS 轮询

七、调试与测试建议

7.1 logcat 标签

# 卫星 SMS 发送
adb logcat -s DatagramDispatcher
adb logcat -s SmsDispatchersController

# 卫星 SMS 接收
adb logcat -s DatagramReceiver
adb logcat -s DatagramController

# 卫星状态判断
adb logcat -s SatelliteController

# HAL 交互
adb logcat -s SatelliteModemInterface

# 标准 SMS 栈
adb logcat -s ImsSmsDispatcher
adb logcat -s GsmInboundSmsHandler

7.2 dumpsys 命令

# 卫星整体状态
adb shell dumpsys satellite

# SMS 相关状态
adb shell dumpsys isub
adb shell dumpsys phone

# 数据报数据库
adb shell content query --uri content://satellite_datagrams

7.3 关键日志过滤

# SMS 路由判断
adb logcat | grep "shouldSendSmsToDatagramDispatcher"
adb logcat | grep "isInCarrierRoamingNbIotNtn"

# SMS 发送流程
adb logcat | grep "CMD_SEND_SMS"
adb logcat | grep "handleEventSendSmsDone"
adb logcat | grep "sendCarrierRoamingNbIotNtnText"

# MT SMS 轮询
adb logcat | grep "CMD_SEND_MT_SMS_POLLING_MESSAGE"
adb logcat | grep "allowMtSmsPolling"
adb logcat | grep "startMtSmsPollingThrottle"

# 数据报接收
adb logcat | grep "EVENT_SATELLITE_DATAGRAM_RECEIVED"
adb logcat | grep "EVENT_RETRY_DELIVERING_RECEIVED_DATAGRAM"
adb logcat | grep "pollPendingSatelliteDatagrams"

7.4 CTS 测试

// SmsDispatchersControllerTest.java
@Test
public void testSendSmsToDatagramDispatcher() {
    when(mSatelliteController.shouldSendSmsToDatagramDispatcher(any(Phone.class)))
            .thenReturn(true);
    mSmsDispatchersController.sendText("1111", "2222", "text", ...);
    processAllMessages();
    verify(mMockDatagramDispatcher).sendSms(any());
}

@Test
public void testSendCarrierRoamingNbIotNtnText() {
    PendingRequest pendingRequest = createPendingRequest();
    switchImsSmsFormat(PhoneConstants.PHONE_TYPE_GSM);
    mSmsDispatchersController.sendCarrierRoamingNbIotNtnText(pendingRequest);
    processAllMessages();
    verify(mSimulatedCommandsVerifier, times(1)).sendImsGsmSms(...);
}

总结:Android 16 的卫星 SMS 实现体现了精巧的双路径设计

  1. 运营商 NTN 漫游 SMS:完全复用标准 SmsManager API,应用无需任何修改。SmsDispatchersController 在检测到 NTN 漫游状态后,将 SMS 路由到 DatagramDispatcher 协调发送,最终仍通过 ImsSmsDispatcher → RIL → Modem 的标准 SMS 协议栈完成。MT SMS 则通过创新的主动轮询机制(终端给自己发 SMS 触发 SMSC 下发待收消息)解决卫星网络无法实时 Paging 的问题。

  2. OEM 卫星数据报:使用独立的 SatelliteManager.sendDatagram() API,消息由应用自行编码加密,框架不解析内容,通过 SatelliteModemInterface → HAL → Modem 发送。接收采用 pollPendingSatelliteDatagrams() 轮询拉取,配合应用层 ACK 和5分钟超时重试机制保证可靠投递。

报告已完成。核心发现是 Android 16 卫星 SMS 的双路径架构

  • 运营商 NTN 漫游 SMS:对应用完全透明,SmsDispatchersController 自动检测 NTN 漫游状态后将 SMS 路由到 DatagramDispatcher 协调,最终仍走标准 ImsSmsDispatcher → RIL 路径。最独特的设计是 MT SMS 主动轮询机制——终端给自己发一条 SMS,触发 SMSC 下发所有待收消息,解决了卫星网络无法实时 Paging 的根本问题。

  • OEM 卫星数据报:使用独立的 SatelliteManager.sendDatagram() API,消息由应用自行编码加密,通过 SatelliteModemInterface → HAL 发送,接收采用轮询拉取 + 5分钟 ACK 超时重试。