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<String>

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

public void

sendMultipartTextMessage(

            String destinationAddress, String scAddress, ArrayList<String> parts,

            ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> 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<String, Object> 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之上的短信发送过程如下图所示。


1.3   SmsTracker与pendingItent

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

1.4   小结

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

 

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


原文地址: http://blog.csdn.net/a34140974/article/details/50964080

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: "understand-6.0.1044-windows-64bit" 是一个软件的文件名,该软件是用于代码分析和可视化的工具。它被设计用于开发人员和软件工程师,可以帮助他们更好地理解和分析代码。 这个软件有许多功能,其中包括源代码浏览、代码度量、拓扑分析、依赖性分析、数据流分析等。它可以自动解析多种编程语言,并提供交互式的可视化界面,以帮助用户深入了解代码的结构和行为。 通过使用这个工具,开发人员可以更快地理解代码,找到潜在的问题和缺陷,并改进代码质量。它还可以帮助团队成员之间共享代码和知识,提高协作效率。 作为一个64位的Windows应用程序,它可以在支持该操作系统的电脑上运行。用户只需下载安装文件,并按照指示进行安装即可使用该软件。 总的来说,"understand-6.0.1044-windows-64bit" 是一个强大的代码分析和可视化工具,它可以帮助开发人员更好地理解和改进代码质量,提高软件开发的效率和质量。 ### 回答2: understand-6.0.1044-windows-64bit是一种特定版本的软件,用于帮助用户分析和理解代码。它是一款广泛使用的软件工具,用于软件开发人员、程序员和软件工程师等在他们的工作中对各种编程语言进行静态代码分析。 这个版本的understand软件是适用于Windows 64位操作系统的,可以在64位Windows系统上运行。它具有65位元处理器的优势,能够充分发挥处理器的性能,并支持更大的内存访问。 understand-6.0.1044-windows-64bit具有许多功能,可以帮助用户深入了解源代码的结构和关系。它可以生成各种图形化表示,如类图、调用图、依赖图等,用于可视化代码的结构。它还提供了强大的搜索和导航功能,使用户能够轻松地浏览和跳转到源代码中的特定部分。此外,它还提供了一些高级功能,如代码度量、代码比较、代码重构等,帮助用户优化和改进代码质量。 对于程序员和软件开发人员来说,了解并使用understand-6.0.1044-windows-64bit是非常重要的。它提供了一种可靠且高效的方法来分析和理解代码,从而更好地维护和改进软件项目。无论是在个人项目还是团队协作中,使用understand软件都可以帮助开发人员更好地理解代码,并提供更好的代码质量和可维护性。 总而言之,understand-6.0.1044-windows-64bit是一款功能强大的代码分析工具,适用于Windows 64位操作系统。它为用户提供了深入理解源代码的能力,并帮助开发人员优化和改进代码。无论是在个人项目还是团队协作中,understand软件都是一个有价值的工具。 ### 回答3: "understand-6.0.1044-windows-64bit" 是一个软件版本的标识。该标识表示的是针对 Windows 系统的 64 位操作系统的 Understand 软件的版本号。Understand 是一款软件开发工具,用于代码分析和理解。它能够帮助开发人员对代码进行深入分析,查找潜在问题并提供解决方案。 其中的"6.0.1044"是该软件版本的具体号码,表示该版本是第六代的发布,1044 是该发行版本的具体编码。通过版本号,可以了解软件的功能、改进和修复的问题。 "windows-64bit" 表示该软件适用于微软的 Windows 操作系统中的 64 位处理器。64 位处理器的电脑性能更强大,能够更好地支持处理大型数据和复杂计算任务。 总而言之,“understand-6.0.1044-windows-64bit” 是一个特定版本的 Understand 软件,适用于 Windows 的 64 位操作系统,用于代码分析和理解。它为开发人员提供了更好的代码理解和分析工具,以提高软件开发的效率和质量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值