Android S短信收发

258 阅读8分钟

一、接收流程

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

参考:

www.cnblogs.com/wuhen1024/p…