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层的短彩信逻辑还有好多,比如接收信息的流程,或者接收长短信的流程等,不再一一介绍了。







  • 相关阅读:
    让tabgroup在下方显示
    titanium好的学习网站推荐
    代码积累
    Ti.include和require的异同
    Titanium基本介绍
    Java微信支付开发之扫码支付模式一
    Java微信公众平台开发之用户管理(用户基本信息和关注列表)
    Java微信支付开发之公众号支付(微信内H5调起支付)
    Java微信公众平台开发之自定义菜单
    Java微信支付开发之扫码支付模式二
  • 原文地址:https://www.cnblogs.com/suncoolcat/p/3324966.html
Copyright © 2011-2022 走看看