Android6.0的SMS(短信)源码分析--短信发送

1     SMS发送流程

1.1   SmsManager

Android发送短信的接口可以认为是SmsManager,当然并不是所有的App都可以发送短信的,必须配置相关的权限。App中可以通过SmsManager.getDefault()得到SmsManager的单例。首先来SmsManager主要提供的接口有哪些。

public static SmsManager

getDefault()获取 SmsManager 的默认实例

public void

sendTextMessage(

            String destinationAddress, String scAddress, String text,

            PendingIntent sentIntent, PendingIntent deliveryIntent) {

        sendTextMessageInternal(destinationAddress, scAddress, text,

            sentIntent, deliveryIntent, true /* persistMessageForCarrierApp*/);

    }发送一个基于 SMS 的文本

public void

sendDataMessage(

            String destinationAddress, String scAddress, short destinationPort,

            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent)

发送一个基于 SMS 的数据到指定的应用程序端口

public ArrayList

divideMessage(String text) 当短信超过 SMS 消息的最大长度时,将短信分割为几块。

public void

sendMultipartTextMessage(

            String destinationAddress, String scAddress, ArrayList parts,

            ArrayList sentIntents, ArrayList deliveryIntents) 发送一个基于SMS的多部分文本,调用者应用已经通过调用divideMessage (String text)将消息分割成正确的大小

我们知道短信有长短信和普通短信的区别。长短信一般需要做分隔处理,而普通短信则不需要。这里重点研究的是普通短信。

普通短信的发送接口为SmsManager. sendTextMessage()。其函数原型和参数解释如下:

public void sendTextMessage(String destinationAddress, String scAddress, String text,PendingIntent sentIntent, 

PendingIntent deliveryIntent)

destinationAddress: 收件人地址
scAddress: 短信中心号码,null为默认中心号码
sentIntent: 当消息发出时,成功或者失败的信息报告通过PendingIntent来广播。如果该参数为空,则发信程序会被所有位置程序检查一遍,这样会导致发送时间延长。
deliveryIntent: 当消息发送到收件人时,该PendingIntent会被广播。pdu数据在状态报告的extended data ("pdu")中。

下面是SmsManager. sendTextMessage()的具体实现。

    public void sendTextMessage(

            String destinationAddress, String scAddress, String text,

            PendingIntent sentIntent, PendingIntent deliveryIntent) {

        sendTextMessageInternal(destinationAddress, scAddress, text,

            sentIntent, deliveryIntent, true /* persistMessageForCarrierApp*/);

    }

可以看到仅仅是转调了内部方法sendTextMessageInternal()。

    private void sendTextMessageInternal(String destinationAddress, String scAddress,

            String text, PendingIntent sentIntent, PendingIntent deliveryIntent,

            boolean persistMessageForCarrierApp) {

        if (TextUtils.isEmpty(destinationAddress)) {//对目的地址进行非空判断

            throw new IllegalArgumentException("Invalid destinationAddress");

        }

 

        if (TextUtils.isEmpty(text)) {//对发送的内容进行非空判断

            throw new IllegalArgumentException("Invalid message body");

        }

 

        try {

            ISms iccISms = getISmsServiceOrThrow();//获取isim服务

            iccISms.sendTextForSubscriber(getSubscriptionId(), ActivityThread.currentPackageName(),

                    destinationAddress,

                    scAddress, text, sentIntent, deliveryIntent,

                    persistMessageForCarrierApp);

        } catch (RemoteException ex) {

            // ignore it

        }

    }

sendTextMessageInternal()首先是获取了isms系统服务,然后调用了其sendTextForSubscriber()方法。这里可以看出Andorid的一贯风格:App总是将某个任务交给有能力完成该任务的服务去执行。而这里这个有能力完成短信发送任务的系统服务其实就是UiccSmsController(从其构造方法可以看出来它就是isim service),因此进入UiccSmsController.sendTextForSubscriber()。

    public void sendTextForSubscriber(int subId, String callingPackage, String destAddr,

            String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent,

            boolean persistMessageForNonDefaultSmsApp) {

        IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);

        if (iccSmsIntMgr != null) {

            iccSmsIntMgr.sendText(callingPackage, destAddr, scAddr, text, sentIntent,

                    deliveryIntent, persistMessageForNonDefaultSmsApp);

        } else {

            Rlog.e(LOG_TAG,"sendTextForSubscriber iccSmsIntMgr is null for" +

                          " Subscription: " + subId);

            sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_GENERIC_FAILURE);

        }

    }

首先是通过subId获得对应的IccSmsInterfaceManager,然后调用了其sendText方法。里的subId是调用者SmsManager以参数的形式传进来的。通过getSubscriptionId()获取,其主要的逻辑就是如果设置成了默认方式,那就返回默认卡的subId。如果设置成了默认方式但并没有设置默认卡,则发intent提示用户设置。如果没有设置为默认方式,而是通过SmsManager的构造参数传递进来的,则直接返回这个参数的值就行了。还有一种可能就是非默认,且SmsManager又是通过getDegault的方式得到的,那这个subId就可能会根据时间变化了,并且可能返回负数。我们不管这里如何得到卡的subId,直接进入ICCSmsInterface.sendText()。

    public void sendText(String callingPackage, String destAddr, String scAddr,

            String text, PendingIntent sentIntent, PendingIntent deliveryIntent,

            boolean persistMessageForNonDefaultSmsApp) {

       //检查是否声明了发短信的权限

        mPhone.getContext().enforceCallingPermission(

                Manifest.permission.SEND_SMS,

                "Sending SMS message");

        sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent,

            persistMessageForNonDefaultSmsApp);//转调了内部方法

    }

转调了内部方法sendTextInternal()。

private void sendTextInternal(String callingPackage, String destAddr, String scAddr,

            String text, PendingIntent sentIntent, PendingIntent deliveryIntent,

            boolean persistMessageForNonDefaultSmsApp) {

        if (Rlog.isLoggable("SMS", Log.VERBOSE)) {

            log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr +

                " text='"+ text + "' sentIntent=" +

                sentIntent + " deliveryIntent=" + deliveryIntent);

        }

       //检查该操作是否被用户允许

        if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),

                callingPackage) != AppOpsManager.MODE_ALLOWED) {

            return;

        }

        if (!persistMessageForNonDefaultSmsApp) {

            // Only allow carrier app to skip auto message persistence.

            enforceCarrierPrivilege();

        }

        destAddr = filterDestAddress(destAddr);//对目的地址进行检测

        mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,

                null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp);

    }

其实就是对短信的权限和目的地址的有效性进行了筛查。然后进行短信的Dispatch。

1.2   SmsDispatcher

SmsDispatcher总共派生出三个子类:CdmaSMSDispatcher、GsmSMSDispatcher、ImsSmsDispatcher,在IccSmsInterfaceManager创建时只创建ImsSMSDispatcher,而在ImsSmsDispatcher创建过程中会对创建其他两种制式的SmsDispatcher,IccSmsInterfaceManager把请求发送给ImsSMSDispatcher后,由ImsSMSDispatcher根据当前网络状态选择使用CdmaSmsDispatcher还是GsmSmsDispatcher。这里主要以Cdma为例。因此调用的是CdmaSmsDispathcer.sendText()。

    protected void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,

            PendingIntent deliveryIntent, Uri messageUri, String callingPkg,

            boolean persistMessage) {

        //将短信内容包装成pdu

        SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(

                scAddr, destAddr, text, (deliveryIntent != null), null);

        if (pdu != null) {

            //接着将短信包装成tracker

            HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu);

            SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),messageUri, false /*isExpectMore*/, text, true /*isText*/, persistMessage);

            //carrier是运营商的意思,因此这里和运营商有关

            String carrierPackage = getCarrierAppPackageName();

            if (carrierPackage != null) {//通过运行商的app发送短信

                Rlog.d(TAG, "Found carrier package.");

                TextSmsSender smsSender = new TextSmsSender(tracker);

                smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender));

            } else {

                Rlog.v(TAG, "No carrier package.");

                sendSubmitPdu(tracker);//一般走这里

            }

        } else {

            Rlog.e(TAG, "CdmaSMSDispatcher.sendText(): getSubmitPdu() returned null");

            if (sentIntent != null) {

                try {

                    sentIntent.send(SmsManager.RESULT_ERROR_GENERIC_FAILURE);

                } catch (CanceledException ex) {

                    Rlog.e(TAG, "Intent has been canceled!");

                }

            }

        }

    }

进入sendSubmitPdu()

    protected void sendSubmitPdu(SmsTracker tracker) {

        //紧急回拨模式检测

        if (SystemProperties.getBoolean(TelephonyProperties.PROPERTY_INECM_MODE, false)) {

            if (VDBG) {

                Rlog.d(TAG, "Block SMS in Emergency Callback mode");

            }

            tracker.onFailed(mContext, SmsManager.RESULT_ERROR_NO_SERVICE, 0/*errorCode*/);

            return;

        }

        sendRawPdu(tracker);//这里

    }

进入sendRawPdu()

protected void sendRawPdu(SmsTracker tracker) {

        HashMap map = tracker.mData;  //tracker中解析出map

        byte pdu[] = (byte[]) map.get("pdu");//map中解析出pdu

 

        if (mSmsSendDisabled) {//短信发送被禁止了

            Rlog.e(TAG, "Device does not support sending sms.");

            tracker.onFailed(mContext, RESULT_ERROR_NO_SERVICE, 0/*errorCode*/);

            return;

        }

 

        if (pdu == null) {//pdu

            Rlog.e(TAG, "Empty PDU");

            tracker.onFailed(mContext, RESULT_ERROR_NULL_PDU, 0/*errorCode*/);

            return;

        }

 

        // Get calling app package name via UID from Binder call

        PackageManager pm = mContext.getPackageManager();

        String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid());

 

        if (packageNames == null || packageNames.length == 0) {

            // Refuse to send SMS if we can't get the calling package name.

            Rlog.e(TAG, "Can't get calling app package name: refusing to send SMS");

            tracker.onFailed(mContext, RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/);

            return;

        }

 

        // Get package info via packagemanager

        PackageInfo appInfo;

        try {

            // XXX this is lossy- apps can share a UID

            appInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES);

        } catch (PackageManager.NameNotFoundException e) {

            Rlog.e(TAG, "Can't get calling app package info: refusing to send SMS");

            tracker.onFailed(mContext, RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/);

            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(tracker)) {

            // check for excessive(过多的) outgoing SMS usage by this app

            if (!mUsageMonitor.check(appInfo.packageName, SINGLE_PART_SMS)) {

                sendMessage(obtainMessage(EVENT_SEND_LIMIT_REACHED_CONFIRMATION, tracker));

                return;

            }

            sendSms(tracker);//这里

        }

    }

进入sendSms()。

    protected void sendSms(SmsTracker tracker) {

        HashMap map = tracker.mData;

 

        // byte[] smsc = (byte[]) map.get("smsc");  // unused for CDMA

        byte[] pdu = (byte[]) map.get("pdu");//再次从tracker中解出pdu

 

        Rlog.d(TAG, "sendSms: "

                + " isIms()=" + isIms()

                + " mRetryCount=" + tracker.mRetryCount

                + " mImsRetry=" + tracker.mImsRetry

                + " mMessageRef=" + tracker.mMessageRef

                + " SS=" + mPhone.getServiceState().getState());

 

        sendSmsByPstn(tracker);//这里

    }

进入sendSmsByPstn().

    protected void sendSmsByPstn(SmsTracker tracker) {

        int ss = mPhone.getServiceState().getState();//获取phone状态

        // if sms over IMS is not supported on data and voice is not available...

        if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {

            tracker.onFailed(mContext, getNotInServiceError(ss), 0/*errorCode*/);

            return;

        }

        //获取一个发送完成的Message,一遍发送完成后回到HandleMessage

        Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);

        byte[] pdu = (byte[]) tracker.mData.get("pdu");//又解出pdu,好频繁啊

 

        int currentDataNetwork = mPhone.getServiceState().getDataNetworkType();

        boolean imsSmsDisabled = (currentDataNetwork == TelephonyManager.NETWORK_TYPE_EHRPD

                    || (currentDataNetwork == TelephonyManager.NETWORK_TYPE_LTE

                    && !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()))

                    && mPhone.getServiceState().getVoiceNetworkType()

                    == TelephonyManager.NETWORK_TYPE_1xRTT

                    && ((CDMAPhone) mPhone).mCT.mState != PhoneConstants.State.IDLE;

 

        // sms over cdma 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

        //注意携带了参数reply是一个EVENT_SEND_SMS_COMPLETE, tracker的消息

        if (0 == tracker.mImsRetry && !isIms() || imsSmsDisabled) {

            mCi.sendCdmaSms(pdu, reply);

        } else {

            mCi.sendImsCdmaSms(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++;//如果是重试,重试次数加1

        }

    }

mCi我们在phone应用的分析中重点分析过,其实就是RILJ。因此进入到了RIL层。关于RIL在phone应用的分析中已经很详细了,肯定是先构造一个RILRequest,然后将pdu数据写入,接着send(rr),接着在processSolicited中处理发送结果,并调用rr.mResult.sendToTarget()将结果上传到到上层(这里是smsDispatcher)。这里对于RILJ以下的处理过程就不赘述了。

当RILJ发送完毕,reply消息被发送,因此sms发送成功的消息被smsDispather接收并HandleMessage。在handleMessage()中直接调用了handleSendComplete()方法。

    protected void handleSendComplete(AsyncResult ar) {

        SmsTracker tracker = (SmsTracker) ar.userObj;//ar中解出tracker

        PendingIntent sentIntent = tracker.mSentIntent;//tracker中解出sendIntent

 

        if (ar.result != null) {//解出回应消息

            tracker.mMessageRef = ((SmsResponse)ar.result).mMessageRef;

        } else {

            Rlog.d(TAG, "SmsResponse was null");

        }

        if (ar.exception == null) {//如果没有异常,表示发送成功

            if (DBG) Rlog.d(TAG, "SMS send complete. Broadcasting intent: " + sentIntent);

//如果需要等待对方接受的结果状态,将tracker添加到pendinglist以等待结果

//需要注意的是这里的mSendItentmDeliveryIntent都是pendingIntent,就是留待以后触发的意思,需要触发是调用PendingIntent.send()-—网络总结

            if (tracker.mDeliveryIntent != null) {

                // Expecting a status report.  Add it to the list.

                deliveryPendingList.add(tracker);//留待以后触发

            }

            tracker.onSent(mContext);//发送消息广播,内部调用了PendingIntent.send()

        } else {//如果有异常,表示发送短信失败

            if (DBG) Rlog.d(TAG, "SMS send failed");

            //首先获取短信的状态

            int ss = mPhone.getServiceState().getState();

            //短信发送失败,可以重试,但服务不再Service状态,直接将重试次数设到超过最大

            if ( tracker.mImsRetry > 0 && ss != ServiceState.STATE_IN_SERVICE) {

                // This is retry after failure over IMS but voice is not available.

                // Set retry to max allowed, so no retry is sent and

                //   cause RESULT_ERROR_GENERIC_FAILURE to be returned to app.

                tracker.mRetryCount = MAX_SEND_RETRIES;//设置最大重试次数,即不重试

 

                Rlog.d(TAG, "handleSendComplete: Skipping retry: "

                +" isIms()="+isIms()

                +" mRetryCount="+tracker.mRetryCount

                +" mImsRetry="+tracker.mImsRetry

                +" mMessageRef="+tracker.mMessageRef

                +" SS= "+mPhone.getServiceState().getState());

            }

            // if sms over IMS is not supported on data and voice is not available...

            if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {

                tracker.onFailed(mContext, getNotInServiceError(ss), 0/*errorCode*/);

            } else if ((((CommandException)(ar.exception)).getCommandError()

                    == CommandException.Error.SMS_FAIL_RETRY) &&

                   tracker.mRetryCount < MAX_SEND_RETRIES) {

                //发送失败,重试,从这里看出重试次数有次数限制

                tracker.mRetryCount++;

                Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker);

                sendMessageDelayed(retryMsg, SEND_RETRY_DELAY);

            } else {

                int errorCode = 0;//默认没有errorcode

                if (ar.result != null) {//根据ar设置errorcode

                    errorCode = ((SmsResponse)ar.result).mErrorCode;

                }

                int error = RESULT_ERROR_GENERIC_FAILURE;//默认错误为这个

                if (((CommandException)(ar.exception)).getCommandError()

                        == CommandException.Error.FDN_CHECK_FAILURE) {

                   //如果底层特殊上报了error,则根据底层设置error

                    error = RESULT_ERROR_FDN_CHECK_FAILURE;

                }

       //将错误及错误code发送到上层,内部同样调用了PendingIntent.send()触发执行Intent

                tracker.onFailed(mContext, error, errorCode);

            }

        }

    }

可以看到对于短信的发送失败和成功状态的处理,最后都是通过SmsTracker来处理的。总结RILJ之上的短信发送过程如下图所示。

Android6.0的SMS(短信)源码分析--短信发送_第1张图片

1.3   SmsTracker与pendingItent

这里主要涉及到了PendingIntent,因此还有待研究!

1.4   小结

下图是普通短信的处理流程。可以看到的是,在上层,短信是通过源目地址以及String等体现出来的,接着往底层走是tracker,再接着到RILJ演变成了pdu数据,再到RILRequest下发到RILD。

 Android6.0的SMS(短信)源码分析--短信发送_第2张图片

在图中也画出了长短信的处理流程,可以看到长短信的处理与普通短信的处理基本类似,仅仅是多了分段处理(在App中)。

你可能感兴趣的:(Android)