一、接收流程
1 入口
该BroadcastReceiver配置了对intent: SMS_DELIVER_ACTION的接收:
/packages/apps/Messaging/AndroidManifest.xml
<receiver android:name=".receiver.SmsDeliverReceiver"
android:exported="true"
android:permission="android.permission.BROADCAST_SMS">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_DELIVER" />
</intent-filter>
</receiver>
Telephony对该intent的定义:
/frameworks/base/core/java/android/provider/Telephony.java
public static final String SMS_DELIVER_ACTION =
"android.provider.Telephony.SMS_DELIVER";
2 Framework流程
2.1 流程图
这里以普通接收到的GSM SMS(其他CDMA和IMS SMS可查看代码)为例:

2.2 源码
2.2.1 注册New SMS
在GsmInboundSmsHandler这个Handler中注册EVENT_NEW_SMS事件,设置RIL_UNSOL_RESPONSE_NEW_SMS的监听者为mGsmSmsRegistrant:
/frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
/**
* Create a new GSM inbound SMS handler.
*/
private GsmInboundSmsHandler(Context context, SmsStorageMonitor storageMonitor,
Phone phone) {
super("GsmInboundSmsHandler", context, storageMonitor, phone);
phone.mCi.setOnNewGsmSms(getHandler(), EVENT_NEW_SMS, null);
mDataDownloadHandler = new UsimDataDownloadHandler(phone.mCi, phone.getPhoneId());
mCellBroadcastServiceManager.enable();
if (TEST_MODE) {
if (sTestBroadcastReceiver == null) {
sTestBroadcastReceiver = new GsmCbTestBroadcastReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(TEST_ACTION);
context.registerReceiver(sTestBroadcastReceiver, filter);
}
}
}
RILJ方法:
public void setOnNewGsmSms(Handler h, int what, Object obj) {
mGsmSmsRegistrant = new Registrant (h, what, obj);
}
2.2.2 接收New SMS
RadioIndication通过newSms()上报新短信,最终通过mRil.mGsmSmsRegistrant.notifyRegistrant()发送消息EVENT_NEW_SMS事件给GsmInboundSmsHandler:
/frameworks/opt/telephony/src/java/com/android/internal/telephony/RadioIndication.java
public void newSms(int indicationType, ArrayList<Byte> pdu) {
mRil.processIndication(indicationType);
byte[] pduArray = RIL.arrayListToPrimitiveArray(pdu);
if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS);
SmsMessageBase smsb = com.android.internal.telephony.gsm.SmsMessage.createFromPdu(pduArray);
if (mRil.mGsmSmsRegistrant != null) {
mRil.mGsmSmsRegistrant.notifyRegistrant(
new AsyncResult(null, smsb == null ? null : new SmsMessage(smsb), null));
}
}
GsmInboundSmsHandler最终会发送intent: SMS_DELIVER_ACTION给相关app。
2.2.3 发送Delivery Report
对于普通短消息,会在GsmInboundSmsHandler中发送Delivery Report给网络。
3 RILD流程
3.1 接收New SMS流程
3.1.1 流程图

3.1.2 源码
vendor_ril收到RIL_onUnsolicitedResponse(),会调用radio::newSmsInd()方法:
/hardware/ril/libril/ril_unsol_commands.h
{RIL_UNSOL_RESPONSE_NEW_SMS, radio::newSmsInd, WAKE_PARTIAL},
该方法直接调用RadioIndication的newSms()方法通知Framework:
/hardware/ril/libril/ril_service.cpp
int radio::newSmsInd(int slotId, int indicationType,
int token, RIL_Errno e, void *response, size_t responseLen) {
if (radioService[slotId] != NULL && radioService[slotId]->mRadioIndication != NULL) {
if (response == NULL || responseLen == 0) {
RLOGE("newSmsInd: invalid response");
return 0;
}
uint8_t *bytes = convertHexStringToBytes(response, responseLen);
if (bytes == NULL) {
RLOGE("newSmsInd: convertHexStringToBytes failed");
return 0;
}
hidl_vec<uint8_t> pdu;
pdu.setToExternal(bytes, responseLen/2);
#if VDBG
RLOGD("newSmsInd");
#endif
Return<void> retStatus = radioService[slotId]->mRadioIndication->newSms(
convertIntToRadioIndicationType(indicationType), pdu);
radioService[slotId]->checkReturnStatus(retStatus);
free(bytes);
} else {
RLOGE("newSmsInd: radioService[%d]->mRadioIndication == NULL", slotId);
}
return 0;
}
3.2 发送Delivery Report流程
3.2.1 流程图

3.2.2 源码
RILJ直接调用radioProxy.acknowledgeLastIncomingGsmSms(rr.mSerial, success, cause),最终该消息会通过RILC发送给Modem:
/frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java : RIL_REQUEST_SMS_ACKNOWLEDGE
当收到网络侧的回复后,RILC通过ril.cpp调用ril_service的radio::acknowledgeLastIncomingGsmSmsResponse():
/hardware/ril/reference-ril/reference-ril.c
/hardware/ril/libril/ril.cpp
ril_service直接调用RadioResponse的acknowledgeLastIncomingGsmSmsResponse()去通知Framework:
/hardware/ril/libril/ril_service.cpp
/hardware/ril/libril/ril_service.cpp:RadioImpl
/frameworks/opt/telephony/src/java/com/android/internal/telephony/RadioResponse.java
4 命令+CMT和AT+CNMA
3GPP 27.005介绍了这两个AT命令:
3 Text Mode
3.4 Message Receiving and Reading Commands
3.4.1 New Message Indications to TE +CNMI
+CMT: [], (PDU mode enabled); or
+CMT: ,[],[,,,,,,,] (text mode enabled; about parameters in italics, refer command Show Text Mode Parameters +CSDH)
4 PDU Mode
4.6 New Message Acknowledgement to ME/TA +CNMA
< +CMT: ,30
0891683110808805F0040BA13142166977F10000223061713590230CC9531B642FCBF3A0F7DA05
> AT+CNMA=1, 2, "0000"
< OK
5 Modem流程
3GPP 24.011,以LTE S1 mode下接收SMS为例:
协议分层
Step1(NW->MS)
Step2(MS->NW)
Step3(MS->NW)
Step4(NW->MS)
SM_AL
SMS-DELIVER
SMS-DELIVER REPORT
SM_TL
SM_RL
RP-DATA
RP-ACK
CM_sublayer
CP-DATA
CP-ACK
CP-DATA
CP-ACK
EMM_sublayer
Downlink NAS transport
Uplink NAS Transport
Uplink NAS Transport
Downlink NAS Transport
ERRC
dlInformationTransfer
ulInformationTransfer
ulInformationTransfer
dlInformationTransfer
PDU消息:
Step1: SMS DELIVER
Downlink NAS transport: 07 62 2e 09 01 2b 01 00 08 91 68 31 10 80 88 05 f0 00 1e 04 0b a1 31 42 16 69 77 f1 00 00 22 30 61 71 35 90 23 0c c9 53 1b 64 2f cb f3 a0 f7 da 05
Step2: CP-ACK
Uplink NAS Transport: 07 63 02 89 04
Step3: SMS DELIVER REPORT
Uplink NAS Transport: 07 63 09 89 01 06 02 00 41 02 00 00
Step4: CP-ACK
Downlink NAS transport: 07 62 02 09 04
二、发送流程
1 入口
查看Messaging app调用的发送SMS的接口,在这里根据短信内容创建deliveryIntents和sentIntents两个List,deliveryIntents用于短信发送状态报告的回调Intent,而sentIntents作为短信发送结果的回调Intent。
/packages/apps/Messaging/src/com/android/messaging/sms/SmsSender.java
if (MmsConfig.get(subId).getSendMultipartSmsAsSeparateMessages()) {
// If multipart sms is not supported, send them as separate messages
for (int i = 0; i < messageCount; i++) {
smsManager.sendTextMessage(dest,
serviceCenter,
messages.get(i),
sentIntents.get(i),
deliveryIntents.get(i));
}
} else {
smsManager.sendMultipartTextMessage(
dest, serviceCenter, messages, sentIntents, deliveryIntents);
}
2 Framework流程
2.1 流程图
以GSM SMS为例,CDMA SMS和IMS SMS类似:

2.2 源码
Messaging App调用Telephony Frameworks的SmsManager API如下方法发送SMS,该方法的参数解释如下:
String destinationAddress : 短消息目标地址
String scAddress : 短消息服务中心地址
String text : 短消息内容
PendingIntent sentIntent : 短消息发送结果回调Intent,当消息发送成功或者失败时,通过该PendingIntent广播。
PendingIntent deliveryIntent : 短消息发送状态报告回调Intent,接收到Delivery Report后通过该PendingIntent广播。
/frameworks/base/telephony/java/android/telephony/SmsManager.java
sendTextMessage() -> sendTextMessageInternal() -> iSms.sendTextForSubscriber()
public void sendTextMessage(
String destinationAddress, String scAddress, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent) {
sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
true /* persistMessage*/, getOpPackageName(), getAttributionTag(),
0L /* messageId */);
}
private void sendTextMessageInternal(String destinationAddress, String scAddress,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
boolean persistMessage, String packageName, String attributionTag, long messageId) {
if (TextUtils.isEmpty(destinationAddress)) {
throw new IllegalArgumentException("Invalid destinationAddress");
}
if (TextUtils.isEmpty(text)) {
throw new IllegalArgumentException("Invalid message body");
}
// We will only show the SMS disambiguation dialog in the case that the message is being
// persisted. This is for two reasons:
// 1) Messages that are not persisted are sent by carrier/OEM apps for a specific
// subscription and require special permissions. These messages are usually not sent by
// the device user and should not have an SMS disambiguation dialog associated with them
// because the device user did not trigger them.
// 2) The SMS disambiguation dialog ONLY checks to make sure that the user has the SEND_SMS
// permission. If we call resolveSubscriptionForOperation from a carrier/OEM app that has
// the correct MODIFY_PHONE_STATE or carrier permissions, but no SEND_SMS, it will throw
// an incorrect SecurityException.
if (persistMessage) {
resolveSubscriptionForOperation(new SubscriptionResolverResult() {
@Override
public void onSuccess(int subId) {
ISms iSms = getISmsServiceOrThrow();
try {
iSms.sendTextForSubscriber(subId, packageName, attributionTag,
destinationAddress, scAddress, text, sentIntent, deliveryIntent,
persistMessage, messageId);
} catch (RemoteException e) {
Log.e(TAG, "sendTextMessageInternal: Couldn't send SMS, exception - "
+ e.getMessage() + " " + formatCrossStackMessageId(messageId));
notifySmsError(sentIntent, RESULT_REMOTE_EXCEPTION);
}
}
@Override
public void onFailure() {
notifySmsError(sentIntent, RESULT_NO_DEFAULT_SMS_APP);
}
});
} else {
// Not persisting the message, used by sendTextMessageWithoutPersisting() and is not
// visible to the user.
ISms iSms = getISmsServiceOrThrow();
try {
iSms.sendTextForSubscriber(getSubscriptionId(), packageName, attributionTag,
destinationAddress, scAddress, text, sentIntent, deliveryIntent,
persistMessage, messageId);
} catch (RemoteException e) {
Log.e(TAG, "sendTextMessageInternal (no persist): Couldn't send SMS, exception - "
+ e.getMessage() + " " + formatCrossStackMessageId(messageId));
notifySmsError(sentIntent, RESULT_REMOTE_EXCEPTION);
}
}
}
/frameworks/opt/telephony/src/java/com/android/internal/telephony/SmsController.java
sendTextForSubscriber() -> sendIccText() -> iccSmsIntMgr.sendText()
@Override
public void sendTextForSubscriber(int subId, String callingPackage,
String callingAttributionTag, String destAddr, String scAddr, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent,
boolean persistMessageForNonDefaultSmsApp, long messageId) {
if (callingPackage == null) {
callingPackage = getCallingPackage();
}
if (!getSmsPermissions(subId).checkCallingCanSendText(persistMessageForNonDefaultSmsApp,
callingPackage, callingAttributionTag, "Sending SMS message")) {
sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_GENERIC_FAILURE);
return;
}
long token = Binder.clearCallingIdentity();
SubscriptionInfo info;
try {
info = getSubscriptionInfo(subId);
} finally {
Binder.restoreCallingIdentity(token);
}
if (isBluetoothSubscription(info)) {
sendBluetoothText(info, destAddr, text, sentIntent, deliveryIntent);
} else {
sendIccText(subId, callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent,
persistMessageForNonDefaultSmsApp, messageId);
}
}
private void sendIccText(int subId, String callingPackage, String destAddr,
String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
boolean persistMessageForNonDefaultSmsApp, long messageId) {
Rlog.d(LOG_TAG, "sendTextForSubscriber iccSmsIntMgr"
+ " Subscription: " + subId + " id: " + messageId);
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null) {
iccSmsIntMgr.sendText(callingPackage, destAddr, scAddr, text, sentIntent,
deliveryIntent, persistMessageForNonDefaultSmsApp, messageId);
} else {
Rlog.e(LOG_TAG, "sendTextForSubscriber iccSmsIntMgr is null for"
+ " Subscription: " + subId + " id: " + messageId);
sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_GENERIC_FAILURE);
}
}
/frameworks/opt/telephony/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
sendText() -> sendTextInternal() -> mDispatchersController.sendText()
/**
* A permissions check before passing to {@link IccSmsInterfaceManager#sendTextInternal}.
* This method checks only if the calling package has the permission to send the sms.
* Note: SEND_SMS permission should be checked by the caller of this method
*/
public void sendText(String callingPackage, String destAddr, String scAddr,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
boolean persistMessageForNonDefaultSmsApp, long messageId) {
sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent,
persistMessageForNonDefaultSmsApp, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
false /* expectMore */, SMS_MESSAGE_PERIOD_NOT_SPECIFIED, false /* isForVvm */,
messageId);
}
private void sendTextInternal(String callingPackage, String destAddr, String scAddr,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
boolean persistMessageForNonDefaultSmsApp, int priority, boolean expectMore,
int validityPeriod, boolean isForVvm, long messageId) {
if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr
+ " text='" + text + "' sentIntent=" + sentIntent + " deliveryIntent="
+ deliveryIntent + " priority=" + priority + " expectMore=" + expectMore
+ " validityPeriod=" + validityPeriod + " isForVVM=" + isForVvm
+ " " + SmsController.formatCrossStackMessageId(messageId));
}
notifyIfOutgoingEmergencySms(destAddr);
destAddr = filterDestAddress(destAddr);
mDispatchersController.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp,
priority, expectMore, validityPeriod, isForVvm, messageId);
}
/frameworks/opt/telephony/src/java/com/android/internal/telephony/SmsDispatchersController.java
sendText() -> mGsmDispatcher.sendText()
public void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,
PendingIntent deliveryIntent, Uri messageUri, String callingPkg, boolean persistMessage,
int priority, boolean expectMore, int validityPeriod, boolean isForVvm,
long messageId) {
if (mImsSmsDispatcher.isAvailable() || mImsSmsDispatcher.isEmergencySmsSupport(destAddr)) {
mImsSmsDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
messageUri, callingPkg, persistMessage, priority, false /*expectMore*/,
validityPeriod, isForVvm, messageId);
} else {
if (isCdmaMo()) {
mCdmaDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
messageUri, callingPkg, persistMessage, priority, expectMore,
validityPeriod, isForVvm, messageId);
} else {
mGsmDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
messageUri, callingPkg, persistMessage, priority, expectMore,
validityPeriod, isForVvm, messageId);
}
}
}
/frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java extends SMSDispatcher
sendText() -> sendSubmitPdu() -> sendRawPdu() -> sendSms()
public void sendText(String destAddr, String scAddr, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri,
String callingPkg, boolean persistMessage, int priority,
boolean expectMore, int validityPeriod, boolean isForVvm,
long messageId) {
Rlog.d(TAG, "sendText id: " + messageId);
SmsMessageBase.SubmitPduBase pdu = getSubmitPdu(
scAddr, destAddr, text, (deliveryIntent != null), null, priority, validityPeriod);
if (pdu != null) {
HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu);
SmsTracker tracker = getSmsTracker(callingPkg, map, sentIntent, deliveryIntent,
getFormat(), messageUri, expectMore, text, true /*isText*/,
persistMessage, priority, validityPeriod, isForVvm, messageId);
if (!sendSmsByCarrierApp(false /* isDataSms */, tracker)) {
sendSubmitPdu(tracker);
}
} else {
Rlog.e(TAG, "SmsDispatcher.sendText(): getSubmitPdu() returned null" + " id: "
+ messageId);
triggerSentIntentForFailure(sentIntent);
}
}
/** Send a single SMS PDU. */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void sendSubmitPdu(SmsTracker tracker) {
sendSubmitPdu(new SmsTracker[] {tracker});
}
/** Send a multi-part SMS PDU. Usually just calls {@link sendRawPdu}. */
private void sendSubmitPdu(SmsTracker[] trackers) {
if (shouldBlockSmsForEcbm()) {
Rlog.d(TAG, "Block SMS in Emergency Callback mode");
handleSmsTrackersFailure(trackers, SmsManager.RESULT_SMS_BLOCKED_DURING_EMERGENCY,
NO_ERROR_CODE);
} else {
sendRawPdu(trackers);
}
}
public void sendRawPdu(SmsTracker[] trackers) {
@SmsManager.Result int error = SmsManager.RESULT_ERROR_NONE;
PackageInfo appInfo = null;
if (mSmsSendDisabled) {
Rlog.e(TAG, "Device does not support sending sms.");
error = SmsManager.RESULT_ERROR_NO_SERVICE;
} else {
for (SmsTracker tracker : trackers) {
if (tracker.getData().get(MAP_KEY_PDU) == null) {
Rlog.e(TAG, "Empty PDU");
error = SmsManager.RESULT_ERROR_NULL_PDU;
break;
}
}
if (error == SmsManager.RESULT_ERROR_NONE) {
UserHandle userHandle = UserHandle.of(trackers[0].mUserId);
PackageManager pm = mContext.createContextAsUser(userHandle, 0).getPackageManager();
try {
// Get package info via packagemanager
appInfo =
pm.getPackageInfo(
trackers[0].getAppPackageName(),
PackageManager.GET_SIGNATURES);
} catch (PackageManager.NameNotFoundException e) {
Rlog.e(TAG, "Can't get calling app package info: refusing to send SMS"
+ " id: " + getMultiTrackermessageId(trackers));
error = SmsManager.RESULT_ERROR_GENERIC_FAILURE;
}
}
}
if (error != SmsManager.RESULT_ERROR_NONE) {
handleSmsTrackersFailure(trackers, error, NO_ERROR_CODE);
return;
}
// checkDestination() returns true if the destination is not a premium short code or the
// sending app is approved to send to short codes. Otherwise, a message is sent to our
// handler with the SmsTracker to request user confirmation before sending.
if (checkDestination(trackers)) {
// check for excessive outgoing SMS usage by this app
if (!mSmsDispatchersController
.getUsageMonitor()
.check(appInfo.packageName, trackers.length)) {
sendMessage(obtainMessage(EVENT_SEND_LIMIT_REACHED_CONFIRMATION, trackers));
return;
}
for (SmsTracker tracker : trackers) {
sendSms(tracker);
}
}
if (mTelephonyManager.isEmergencyNumber(trackers[0].mDestAddress)) {
new AsyncEmergencyContactNotifier(mContext).execute();
}
}
protected void sendSms(SmsTracker tracker) {
int ss = mPhone.getServiceState().getState();
Rlog.d(TAG, "sendSms: "
+ " isIms()=" + isIms()
+ " mRetryCount=" + tracker.mRetryCount
+ " mImsRetry=" + tracker.mImsRetry
+ " mMessageRef=" + tracker.mMessageRef
+ " mUsesImsServiceForIms=" + tracker.mUsesImsServiceForIms
+ " SS=" + ss
+ " " + SmsController.formatCrossStackMessageId(tracker.mMessageId));
// if sms over IMS is not supported on data and voice is not available...
if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
//In 5G case only Data Rat is reported.
if(mPhone.getServiceState().getRilDataRadioTechnology()
!= ServiceState.RIL_RADIO_TECHNOLOGY_NR) {
tracker.onFailed(mContext, getNotInServiceError(ss), NO_ERROR_CODE);
return;
}
}
Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
HashMap<String, Object> map = tracker.getData();
byte pdu[] = (byte[]) map.get("pdu");
byte smsc[] = (byte[]) map.get("smsc");
if (tracker.mRetryCount > 0) {
// per TS 23.040 Section 9.2.3.6: If TP-MTI SMS-SUBMIT (0x01) type
// TP-RD (bit 2) is 1 for retry
// and TP-MR is set to previously failed sms TP-MR
if (((0x01 & pdu[0]) == 0x01)) {
pdu[0] |= 0x04; // TP-RD
pdu[1] = (byte) tracker.mMessageRef; // TP-MR
}
}
// sms over gsm is used:
// if sms over IMS is not supported AND
// this is not a retry case after sms over IMS failed
// indicated by mImsRetry > 0 OR
// this tracker uses ImsSmsDispatcher to handle SMS over IMS. This dispatcher has received
// this message because the ImsSmsDispatcher has indicated that the message needs to
// fall back to sending over CS.
if (0 == tracker.mImsRetry && !isIms() || tracker.mUsesImsServiceForIms) {
if (tracker.mRetryCount == 0 && tracker.mExpectMore) {
mCi.sendSMSExpectMore(IccUtils.bytesToHexString(smsc),
IccUtils.bytesToHexString(pdu), reply);
} else {
mCi.sendSMS(IccUtils.bytesToHexString(smsc),
IccUtils.bytesToHexString(pdu), reply);
}
} else {
mCi.sendImsGsmSms(IccUtils.bytesToHexString(smsc),
IccUtils.bytesToHexString(pdu), tracker.mImsRetry,
tracker.mMessageRef, reply);
// increment it here, so in case of SMS_FAIL_RETRY over IMS
// next retry will be sent using IMS request again.
tracker.mImsRetry++;
}
}
3 RILD流程

4 命令AT+CMGS
3GPP 27.005介绍了该AT命令:
4 PDU Mode
4.3 Send Message +CMGS
如下是一条具体的例子:
> AT+CMGS=23, "0001000B813142166977F100000B417919947FD741EFF50F"
< +CMGS: 15
< OK
AT+CMGS命令携带长度和PDU;返回的+CMGS命令携带MR。
5 Modem流程
3GPP 24.011,以LTE S1 mode下发送SMS为例:
协议分层
Step1(MS->NW)
Step2(NW->MS)
Step3(NW->MS)
Step4(MS->NW)
SM_AL
SMS-SUBMIT
SMS-SUBMIT REPORT
SM_TL
SM_RL
RP-DATA
RP-ACK
CM_sublayer
CP-DATA
CP-ACK
CP-DATA
CP-ACK
EMM_sublayer
Uplink NAS Transport
Downlink NAS transport
Downlink NAS transport
Uplink NAS Transport
ERRC
ulInformationTransfer
dlInformationTransfer
dlInformationTransfer
ulInformationTransfer
PDU消息:
Step1:
SMS SUBMIT: 01 0f 0b 81 31 42 16 69 77 f1 00 00 0b 41 79 19 94 7f d7 41 ef f5 0f
Uplink NAS Transport: 07 63 27 09 01 24 00 0f 00 08 91 68 31 10 80 88 05 f0 17 01 0f 0b 81 31 42 16 69 77 f1 00 00 0b 41 79 19 94 7f d7 41 ef f5 0f
Step2:
Downlink NAS transport: 07 62 02 89 04
Step3:
Downlink NAS transport: 07 62 10 89 01 0d 03 0f 41 09 01 00 22 30 61 01 93 45 23
SMS-SUBMIT REPORT: 01 00 22 30 61 01 93 45 23
Step4:
Uplink NAS Transport: 07 63 02 09 04
参考: