zoukankan      html  css  js  c++  java
  • Android短彩信源码解析-短信发送流程(二)

    转载请注明出处:http://blog.csdn.net/droyon/article/details/11699935

    2,短彩信发送framework逻辑

    短信在SmsSingleRecipientSender.java中包装了SentIntents,以及DeliveryIntents,信息的内容在message中,信息的目的发送地址在mDest中,然后调用下面的代码进行信息的发送

    smsManager.sendMultipartTextMessage(mDest, mServiceCenter, messages, sentIntents, deliveryIntents);

    smsMessager对应的类为:SmsManager.java

    2.1  进入SmsManager.java

    public void sendMultipartTextMessage(
                String destinationAddress, String scAddress, ArrayList<String> parts,
                ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
            if (TextUtils.isEmpty(destinationAddress)) {
                throw new IllegalArgumentException("Invalid destinationAddress");
            }
            if (parts == null || parts.size() < 1) {
                throw new IllegalArgumentException("Invalid message body");
            }
    
            if (parts.size() > 1) {
                try {
                    ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
                    if (iccISms != null) {
                        iccISms.sendMultipartText(destinationAddress, scAddress, parts,
                                sentIntents, deliveryIntents);
                    }
                } catch (RemoteException ex) {
                    // ignore it
                }
            } else {
                PendingIntent sentIntent = null;
                PendingIntent deliveryIntent = null;
                if (sentIntents != null && sentIntents.size() > 0) {
                    sentIntent = sentIntents.get(0);
                }
                if (deliveryIntents != null && deliveryIntents.size() > 0) {
                    deliveryIntent = deliveryIntents.get(0);
                }
                sendTextMessage(destinationAddress, scAddress, parts.get(0),
                        sentIntent, deliveryIntent);
            }
        }


    在这个类中,主要是根据parts的数量,进行跨进程调用ISms服务。如果是多条信息,执行:

    iccISms.sendMultipartText(destinationAddress, scAddress, parts,
                                sentIntents, deliveryIntents);

    这里调用的ISms服务的sendMultipartText方法。

    如果是单条信息,执行sendTextMessage

    public void sendTextMessage(
                String destinationAddress, String scAddress, String text,
                PendingIntent sentIntent, PendingIntent deliveryIntent) {
            if (TextUtils.isEmpty(destinationAddress)) {
                throw new IllegalArgumentException("Invalid destinationAddress");
            }
    
            if (TextUtils.isEmpty(text)) {
                throw new IllegalArgumentException("Invalid message body");
            }
    
            try {
                ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
                if (iccISms != null) {
                    iccISms.sendText(destinationAddress, scAddress, text, sentIntent, deliveryIntent);
                }
            } catch (RemoteException ex) {
                // ignore it
            }
        }

    同样的,要调用ISms服务。只不过方法变成了Isms服务的sendText方法

    关于iSms服务的注册,它是在初始化phone进程时注册的

    public IccSmsInterfaceManagerProxy(IccSmsInterfaceManager
                iccSmsInterfaceManager) {
            this.mIccSmsInterfaceManager = iccSmsInterfaceManager;
            if(ServiceManager.getService("isms") == null) {
                ServiceManager.addService("isms", this);
            }
        }


    然后我们进到IccSmsInterfaceManagerProxy.java中

    2.2  IccSmsManagerProxy.java

        public void sendText(String destAddr, String scAddr,
                String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
            mIccSmsInterfaceManager.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent);
        }
    
        public void sendMultipartText(String destAddr, String scAddr,
                List<String> parts, List<PendingIntent> sentIntents,
                List<PendingIntent> deliveryIntents) throws android.os.RemoteException {
            mIccSmsInterfaceManager.sendMultipartText(destAddr, scAddr,
                    parts, sentIntents, deliveryIntents);
        }


    代理类中进行实现

    2.3 iccSmsManager.java

        public void sendMultipartText(String destAddr, String scAddr, List<String> parts,
                List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) {
            mPhone.getContext().enforceCallingPermission(
                    "android.permission.SEND_SMS",
                    "Sending SMS message");
            if (Log.isLoggable("SMS", Log.VERBOSE)) {
                int i = 0;
                for (String part : parts) {
                    log("sendMultipartText: destAddr=" + destAddr + ", srAddr=" + scAddr +
                            ", part[" + (i++) + "]=" + part);
                }
            }
            mDispatcher.sendMultipartText(destAddr, scAddr, (ArrayList<String>) parts,
                    (ArrayList<PendingIntent>) sentIntents, (ArrayList<PendingIntent>) deliveryIntents);
        }
        public void sendText(String destAddr, String scAddr,
                String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
            mPhone.getContext().enforceCallingOrSelfPermission(
                    "android.permission.SEND_SMS",
                    "Sending SMS message");
            if (Log.isLoggable("SMS", Log.VERBOSE)) {
                log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr +
                    " text='"+ text + "' sentIntent=" +
                    sentIntent + " deliveryIntent=" + deliveryIntent);
            }
            mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent);
        }


    最终他们都要调到mDispatcher的sendText或者sendMultipartText方法,mDispatcher的原型类:SMSDispatcher,它有两个子类,GsmSmsDispatcher.java以及CdmaSmsDispatcher.java

    2.4,GsmSmsDispatcher.java

    protected void sendMultipartText(String destAddr, String scAddr,
                ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
                ArrayList<PendingIntent> deliveryIntents) {
    
            int refNumber = getNextConcatenatedRef() & 0x00FF;
            int msgCount = parts.size();
            int encoding = android.telephony.SmsMessage.ENCODING_UNKNOWN;
    
            mRemainingMessages = msgCount;
    
            TextEncodingDetails[] encodingForParts = new TextEncodingDetails[msgCount];
            for (int i = 0; i < msgCount; i++) {
                TextEncodingDetails details = calculateLength(parts.get(i), false);
                if (encoding != details.codeUnitSize
                        && (encoding == android.telephony.SmsMessage.ENCODING_UNKNOWN
                                || encoding == android.telephony.SmsMessage.ENCODING_7BIT)) {
                    encoding = details.codeUnitSize;
                }
                encodingForParts[i] = details;
            }
    
            for (int i = 0; i < msgCount; i++) {
                SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
                concatRef.refNumber = refNumber;
                concatRef.seqNumber = i + 1;  // 1-based sequence
                concatRef.msgCount = msgCount;
                // TODO: We currently set this to true since our messaging app will never
                // send more than 255 parts (it converts the message to MMS well before that).
                // However, we should support 3rd party messaging apps that might need 16-bit
                // references
                // Note:  It's not sufficient to just flip this bit to true; it will have
                // ripple effects (several calculations assume 8-bit ref).
                concatRef.isEightBits = true;
                SmsHeader smsHeader = new SmsHeader();
                smsHeader.concatRef = concatRef;
    
                // Set the national language tables for 3GPP 7-bit encoding, if enabled.
                if (encoding == android.telephony.SmsMessage.ENCODING_7BIT) {
                    smsHeader.languageTable = encodingForParts[i].languageTable;
                    smsHeader.languageShiftTable = encodingForParts[i].languageShiftTable;
                }
    
                PendingIntent sentIntent = null;
                if (sentIntents != null && sentIntents.size() > i) {
                    sentIntent = sentIntents.get(i);
                }
    
                PendingIntent deliveryIntent = null;
                if (deliveryIntents != null && deliveryIntents.size() > i) {
                    deliveryIntent = deliveryIntents.get(i);
                }
    
                sendNewSubmitPdu(destAddr, scAddr, parts.get(i), smsHeader, encoding,
                        sentIntent, deliveryIntent, (i == (msgCount - 1)));
            }
    
        }

    遍历所有的短信part,然后执行sendNewSubmitPdu方法,这个方法在SmsDispatcher.java中是abstract的,真正的实现在其子类中:

    protected void sendNewSubmitPdu(String destinationAddress, String scAddress,
                String message, SmsHeader smsHeader, int encoding,
                PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart) {
            SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(scAddress, destinationAddress,
                    message, deliveryIntent != null, SmsHeader.toByteArray(smsHeader),
                    encoding, smsHeader.languageTable, smsHeader.languageShiftTable);
            if (pdu != null) {
                sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent);
            } else {
                Log.e(TAG, "GsmSMSDispatcher.sendNewSubmitPdu(): getSubmitPdu() returned null");
            }
        }


    实现方式为执行sendRawPdu。

    以上是sendMultipartsText,让我们看看sendText方法在SmsDispatcher.java中的实现:

    protected abstract void sendText(String destAddr, String scAddr,
                String text, PendingIntent sentIntent, PendingIntent deliveryIntent);


    SmsDispatcher.java的设计方法为让其子类来决定sendText方法的实现方式,现贴出sendText的源码实现:

    protected void sendText(String destAddr, String scAddr, String text,
                PendingIntent sentIntent, PendingIntent deliveryIntent) {
            SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
                    scAddr, destAddr, text, (deliveryIntent != null));
            if (pdu != null) {
                sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent);
            } else {
                Log.e(TAG, "GsmSMSDispatcher.sendText(): getSubmitPdu() returned null");
            }
        }


    我们看到其实现方式为执行sendRawPdu,到这里也就是说,无论是sendText还是sendMultipartsText方法,他们归根结底都是执行sendRawPdu方法,不同的是sendMultipartsText是遍历所有的短信parts,然后在调用sendRawPdu。

    在这个方法的前一步会将信息的内容,要发送的目的地址,当前的系统时间等打包成SubmitPdu,关于短信pdu这部分,会稍后介绍,我们现在主要解析一下短信framework发送的主要逻辑。

    下面我们看看sendRawPdu的方法实现:

    protected void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent,
                PendingIntent deliveryIntent) {
            if (mSmsSendDisabled) {
                if (sentIntent != null) {
                    try {
                        sentIntent.send(RESULT_ERROR_NO_SERVICE);
                    } catch (CanceledException ex) {}
                }
                Log.d(TAG, "Device does not support sending sms.");
                return;
            }
    
            if (pdu == null) {
                if (sentIntent != null) {
                    try {
                        sentIntent.send(RESULT_ERROR_NULL_PDU);
                    } catch (CanceledException ex) {}
                }
                return;
            }
    
            HashMap<String, Object> map = new HashMap<String, Object>();
            map.put("smsc", smsc);
            map.put("pdu", pdu);
    
            SmsTracker tracker = new SmsTracker(map, sentIntent,
                    deliveryIntent);
            int ss = mPhone.getServiceState().getState();
    
            if (ss != ServiceState.STATE_IN_SERVICE) {
                handleNotInService(ss, tracker);
            } else {
                String appName = getAppNameByIntent(sentIntent);
                if (mUsageMonitor.check(appName, SINGLE_PART_SMS)) {
                    sendSms(tracker);
                } else {
                    sendMessage(obtainMessage(EVENT_POST_ALERT, tracker));
                }
            }
        }

    在这个方法里,首先会检查一些状态,例如pdu是否为null,smsSendDisabled等,然后包装一个SmsTracher对象,在发送前还要检查一下Phone进程的状态,是否处于“离线”状态,关于这个状态,moderm会根据当前所处的信号强度,做出改变。

    如果当前处于服务中,那么就可以进行我们短信的发送了,发送调用sendMessage或者sendSms进行短信的发送,关于sendMessage这个方法,参数为Message,message生成代码:

    public final Message obtainMessage(int what, Object obj)
        {
            return Message.obtain(this, what, obj);
        }


    也就是说这个message是和Handler相关的message。

    PS:我不知道大家有没有注意,其实SmsDispatcher.java这个类就是个Handler。

    在发送之前,会调用:

    mUsageMonitor.check(appName, SINGLE_PART_SMS)

    进行检查,所作的事情其实就是检查在一段时间内,等待发送的短信数目不超过MAX值,关于等待时间以及MAX数目:

    SmsUsageMonitor.java

    /** Default checking period for SMS sent without user permission. */
        private static final int DEFAULT_SMS_CHECK_PERIOD = 3600000;
    
        /** Default number of SMS sent in checking period without user permission. */
        private static final int DEFAULT_SMS_MAX_COUNT = 100;


    回到短信发送中,在此处,检查通过,故短信发送执行sendSms(tracker)

    protected void sendSms(SmsTracker tracker) {
            HashMap<String, Object> map = tracker.mData;
    
            byte smsc[] = (byte[]) map.get("smsc");
            byte pdu[] = (byte[]) map.get("pdu");
    
            Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
            mCm.sendSMS(IccUtils.bytesToHexString(smsc), IccUtils.bytesToHexString(pdu), reply);
        }


    其中mCm对象是CommandInterface的引用

    protected final CommandsInterface mCm;


    很多人对它不是很熟悉,但一定很熟悉它的其中一个子类,那就是RIL.java。

    也就是说,短信发送流程,在此处会调用RIL.java,执行sendSMS方法,参数为smsc的pdu字符串,短信内容以及时间等的pdu,以及一个Message,WHAT值为EVENT_SEND_SMS_COMPLETE,tracher对象作为其Object。我们进入到RIL.java中

    2.5,RIL.java

    public void
        sendSMS (String smscPDU, String pdu, Message result) {
            RILRequest rr
                    = RILRequest.obtain(RIL_REQUEST_SEND_SMS, result);
    
            rr.mp.writeInt(2);
            rr.mp.writeString(smscPDU);
            rr.mp.writeString(pdu);
    
            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
    
            send(rr);
        }


    关于RIL.java,它会和reference_ril.c进行配合,进行at指令的发送以及接收反馈等信息。reference_ril.c文件会被编译到reference-ril.so中。

    在此处,RIL.java会将相应的pdu等信息打包,然后通过socket发送到reference_ril.c中,相关处理如下:

    2.6,reference_ril.c

    static void requestSendSMS(void *data, size_t datalen, RIL_Token t)
    {
        int err;
        const char *smsc;
        const char *pdu;
        int tpLayerLength;
        char *cmd1, *cmd2;
        RIL_SMS_Response response;
        ATResponse *p_response = NULL;
    
        smsc = ((const char **)data)[0];
        pdu = ((const char **)data)[1];
    
        tpLayerLength = strlen(pdu)/2;
    
        // "NULL for default SMSC"
        if (smsc == NULL) {
            smsc= "00";
        }
    
        asprintf(&cmd1, "AT+CMGS=%d", tpLayerLength);
        asprintf(&cmd2, "%s%s", smsc, pdu);
    
        err = at_send_command_sms(cmd1, cmd2, "+CMGS:", &p_response);
    
        if (err != 0 || p_response->success == 0) goto error;
    
        memset(&response, 0, sizeof(response));
    
        /* FIXME fill in messageRef and ackPDU */
    
        RIL_onRequestComplete(t, RIL_E_SUCCESS, &response, sizeof(response));
        at_response_free(p_response);
    
        return;
    error:
        RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
        at_response_free(p_response);
    }


    调用AT命令,cmgs将信息发往moderm,进行相关流程处理。


    最后,如果短信成功发送出去,还记得我们在发送时,从GsmSmsDispatcher.java传递到RIL.java中的Message吗,在发送之后,RIL.java会回调此Message,这个Message如下:

    Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);


    前面说了,What = EVENT_SEND_SMS_COMPLETE,Object = tricker。我们之前也说了SmsDispacher.java以及其子类是Handler,那么这个Message在那里处理的那?答案是在SmsDispacher.java中,处理逻辑代码如下:

    case EVENT_SEND_SMS_COMPLETE:
                // An outbound SMS has been successfully transferred, or failed.
                handleSendComplete((AsyncResult) msg.obj);
                break;
    protected void handleSendComplete(AsyncResult ar) {
            SmsTracker tracker = (SmsTracker) ar.userObj;
            PendingIntent sentIntent = tracker.mSentIntent;
    
            if (ar.exception == null) {
                if (false) {
                    Log.d(TAG, "SMS send complete. Broadcasting "
                            + "intent: " + sentIntent);
                }
    
                if (tracker.mDeliveryIntent != null) {//wanghailu,hailushijie@163.com ,hlwang
                    // Expecting a status report.  Add it to the list.
                    int messageRef = ((SmsResponse)ar.result).messageRef;
                    tracker.mMessageRef = messageRef;
                    deliveryPendingList.add(tracker);
                }
    
                if (sentIntent != null) {
                    try {
                        if (mRemainingMessages > -1) {
                            mRemainingMessages--;
                        }
    
                        if (mRemainingMessages == 0) {
                            Intent sendNext = new Intent();
                            sendNext.putExtra(SEND_NEXT_MSG_EXTRA, true);
                            sentIntent.send(mContext, Activity.RESULT_OK, sendNext);
                        } else {
                            sentIntent.send(Activity.RESULT_OK);
                        }
                    } catch (CanceledException ex) {}
                }
            } else {
                if (false) {
                    Log.d(TAG, "SMS send failed");
                }
    
                int ss = mPhone.getServiceState().getState();
    
                if (ss != ServiceState.STATE_IN_SERVICE) {
                    handleNotInService(ss, tracker);
                } else if ((((CommandException)(ar.exception)).getCommandError()
                        == CommandException.Error.SMS_FAIL_RETRY) &&
                       tracker.mRetryCount < MAX_SEND_RETRIES) {
                    // Retry after a delay if needed.
                    // TODO: According to TS 23.040, 9.2.3.6, we should resend
                    //       with the same TP-MR as the failed message, and
                    //       TP-RD set to 1.  However, we don't have a means of
                    //       knowing the MR for the failed message (EF_SMSstatus
                    //       may or may not have the MR corresponding to this
                    //       message, depending on the failure).  Also, in some
                    //       implementations this retry is handled by the baseband.
                    tracker.mRetryCount++;
                    Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker);
                    sendMessageDelayed(retryMsg, SEND_RETRY_DELAY);
                } else if (tracker.mSentIntent != null) {
                    int error = RESULT_ERROR_GENERIC_FAILURE;
    
                    if (((CommandException)(ar.exception)).getCommandError()
                            == CommandException.Error.FDN_CHECK_FAILURE) {
                        error = RESULT_ERROR_FDN_CHECK_FAILURE;
                    }
                    // Done retrying; return an error to the app.
                    try {
                        Intent fillIn = new Intent();
                        if (ar.result != null) {
                            fillIn.putExtra("errorCode", ((SmsResponse)ar.result).errorCode);
                        }
                        if (mRemainingMessages > -1) {
                            mRemainingMessages--;
                        }
    
                        if (mRemainingMessages == 0) {
                            fillIn.putExtra(SEND_NEXT_MSG_EXTRA, true);
                        }
    
                        tracker.mSentIntent.send(mContext, error, fillIn);
                    } catch (CanceledException ex) {}
                }
            }
        }



    至此,短信发送流程大致如此,短信接收流程中framework部分的处理大致是发送流程的反方向。

    首先检查AsyncResult对象中是否存在异常,如果成功发送的信息,那么不存在异常,如果发送失败,那么是存在Exception的,会进行异常的相应的逻辑处理,大体流程相似,故本处介绍无异常时的流程逻辑。

    无论发送成功还是失败,大体都是执行tricker对象中的mSentIntents,这是一个PendingIntent,执行send会发送此广播,那么我们的上层应用Mms中的SmsReceiverService.java会收到这个广播,并进行相应的逻辑处理,逻辑代码大体如下:

    private void handleSmsSent(Intent intent, int error) {
            Uri uri = intent.getData();
            mSending = false;
            boolean sendNextMsg = intent.getBooleanExtra(EXTRA_MESSAGE_SENT_SEND_NEXT, false);
    
            if (LogTag.DEBUG_SEND) {
                Log.v(TAG, "handleSmsSent uri: " + uri + " sendNextMsg: " + sendNextMsg +
                        " mResultCode: " + mResultCode +
                        " = " + translateResultCode(mResultCode) + " error: " + error);
            }
    
            if (mResultCode == Activity.RESULT_OK) {
                if (LogTag.DEBUG_SEND || Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
                    Log.v(TAG, "handleSmsSent move message to sent folder uri: " + uri);
                }
                if (!Sms.moveMessageToFolder(this, uri, Sms.MESSAGE_TYPE_SENT, error)) {
                    Log.e(TAG, "handleSmsSent: failed to move message " + uri + " to sent folder");
                }
                if (sendNextMsg) {
                    sendFirstQueuedMessage();
                }
    
                // Update the notification for failed messages since they may be deleted.
                MessagingNotification.updateSendFailedNotification(this);
            } else if ((mResultCode == SmsManager.RESULT_ERROR_RADIO_OFF) ||
                    (mResultCode == SmsManager.RESULT_ERROR_NO_SERVICE)) {
                if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
                    Log.v(TAG, "handleSmsSent: no service, queuing message w/ uri: " + uri);
                }
                // We got an error with no service or no radio. Register for state changes so
                // when the status of the connection/radio changes, we can try to send the
                // queued up messages.
                registerForServiceStateChanges();
                // We couldn't send the message, put in the queue to retry later.
                Sms.moveMessageToFolder(this, uri, Sms.MESSAGE_TYPE_QUEUED, error);
                mToastHandler.post(new Runnable() {
                    public void run() {
                        Toast.makeText(SmsReceiverService.this, getString(R.string.message_queued),
                                Toast.LENGTH_SHORT).show();
                    }
                });
            } else if (mResultCode == SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE) {
                mToastHandler.post(new Runnable() {
                    public void run() {
                        Toast.makeText(SmsReceiverService.this, getString(R.string.fdn_check_failure),
                                Toast.LENGTH_SHORT).show();
                    }
                });
            } else {
                messageFailedToSend(uri, error);
                if (sendNextMsg) {
                    sendFirstQueuedMessage();
                }
            }
        }


    主要是根据ResultCode进行一些逻辑处理,比如如果发送成功,那么会首先更新短信由待发送变为已发送状态,并且更新Notification等。

    如果需要发送报告,那么相关的逻辑大体相似,不再详细介绍。


    Ps:发送成功,回调会从moderm那里带回来一个MessageRef,这是一个int值,很重要噢。


    短信发送framework层逻辑大体介绍到这里,framework层的短彩信逻辑还有好多,比如接收信息的流程,或者接收长短信的流程等,不再一一介绍了。







  • 相关阅读:
    Chrome开发者工具中Elements(元素)断点的用途
    最简单的SAP云平台开发教程
    Java实现 LeetCode 495 提莫攻击
    Java实现 LeetCode 494 目标和
    Java实现 LeetCode 494 目标和
    Java实现 LeetCode 494 目标和
    Java实现 LeetCode 493 翻转对
    Java实现 LeetCode 493 翻转对
    Java实现 LeetCode 493 翻转对
    Java实现 LeetCode 492 构造矩形
  • 原文地址:https://www.cnblogs.com/suncoolcat/p/3324966.html
Copyright © 2011-2022 走看看