欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SMS 发送流程

程序员文章站 2022-04-26 13:31:01
...

参考文章链接:
http://blog.csdn.net/a34140974/article/details/50964080

SMS 发送流程

介绍

SMS(Short Message Service) ,短信服务是一种存储和转发服务。也就是说,短消息并不是直接从发送人发送到接收人,而始终通过短信服务中心进行转发。 如果接收人处于未连接状态(可能电话已关闭),则消息将在接收人再次连接时发送。

类型

class0 类型短信:accept and displayed,but not stored.
接收这种类型的短信,用户看完自动消失,不会存储到数据库中,也不会显示Notification。

class1 类型短信:storing in ME and displaying
收到短信会保存到手机上。

class2 类型短信:storing in SIM and displaying
收到短信会保存到SIM卡上。

class3 类型短信:directory to the terminal equipment
一般我们收到的都是默认的Class1类型短信

入口

短信会话列表界面

ConvertsationList.java显示的短信会话列表界面,点击屏幕的“新建短信”按钮,即可进入短信编辑界面,而此按钮的响应方法是ConversionList类中的createNewMessage,此方法便是启动ComposeMessageActitity类的Activity。

短信编辑界面

ComposeMessageActivity短信编辑界面,输入短信的接收方电话号码以及短信内容,单击右下角的“发送”按钮即可发出短信。短信发送按钮的响应事件是当前类的onClick方法 ,如果是发送短信,则调用当前类的confirmSendMessageIfNeeded方法。

① 获取编辑内容是短信还是彩信标志isMms,并根据此标志判断输入的短信接收方地址是否正确,如果不正确弹出相应的信息提示。

② 调用当前类的sendMessage方法中的处理逻辑,最关键的就是调用mWorkingMessage.send方法继续发起发送短信请求。

③ 在上面的两个步骤中都使用了mWorkingMessage对象,它是WorkingMessage类型,当前编辑的短信内容被抽象成了WorkingMessage类。它实现了当前编辑的短信信息涉及的所有行为和操作,其中包含:发送当前信息、为当前信息增加主题或者附件、设置当前信息的类型(短信或彩信)、保存草稿、丢弃当前信息、设置当前信息所属的会话等

Service

SmsManager

sendMessage方法的处理逻辑比较多,主要调用Telephony Frameworks层提供的相关方法 ,短信发送前的处理可总结出以下几点:

  1. 调用SmsManager对象的divideMessage方法,完成短信的拆分,此方法的返回值是分割后每条短信的内容。
  2. 调用Sms类中的静态方法moveMessageToFolder,将即将发送的短信移动到短信发件箱中。
  3. 根据短信内容创建deliveryIntents和sentIntents两个List,deliveryIntents用于短信发送状态报告的回调Intent,而sentIntents作为短信发送结果的回调Intent。
  4. 调用SmsManager对象的sendMultipartTextMessage方法,完成单条或多条短信的发送

普通短信的发送接口为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”)中。

具体实现

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方法

  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);

    }

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

Dispatcher

SmsDispather

SmsDispatcher总共派生出三个子类:CdmaSMSDispatcherGsmSMSDispatcherImsSmsDispatcher,在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以等待结果
//需要注意的是这里的mSendItent和mDeliveryIntent都是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;//默认没有errorcod
                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之上的短信发送过程如下图所示。

SMS 发送流程

总结

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

SMS 发送流程
相关标签: android sms