Android短信----發送流程---框架層(Frameworks)

Android短信接收流程---框架層(Frameworks)


<span style="font-size:14px;">/framework/base/telephony/java/com/android/internal/telephony/ISms.aidl  
/framework/base/telephony/com/android/internal/telephony/IccSmsInterfaceManager  
/telephony/java/com/android/internal/telephony/SMSDispatcher.java  
/telephony/java/com/android/internal/telephony/ImsSMSDispatcher.java  
/telephony/java/com/android/internal/telephony/GsmSMSDispatcher.java  
/telephony/java/android/telephony/SmsMessage.java  
/base/telephony/java/com/android/internal/telephony/RIL.java  
/framework/base/telephone/java/android/telephone/SmsManager  </span>


1:SmsManager.java


一般最簡單的調用sdk發送短信只需要在應用層調用下面兩步~。~
SmsManager smsManager = SmsManager.getDefault();

smsManager.sendTextMessage("10086", null, "11", null, null);//查話費O O

看sendTextMessage方法的源碼

    /**
     * Send a text based SMS.
     *
     * @param destinationAddress the address to send the message to
     * @param scAddress is the service center address or null to use
     *  the current default SMSC
     * @param text the body of the message to send
     * @param sentIntent if not NULL this <code>PendingIntent</code> is
     *  broadcast when the message is successfully sent, or failed.
     *  The result code will be <code>Activity.RESULT_OK</code> for success,
     *  or one of these errors:<br>
     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
     *  <code>RESULT_ERROR_NULL_PDU</code><br>
     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
     *  the extra "errorCode" containing a radio technology specific value,
     *  generally only useful for troubleshooting.<br>
     *  The per-application based SMS control checks sentIntent. If sentIntent
     *  is NULL the caller will be checked against all unknown applications,
     *  which cause smaller number of SMS to be sent in checking period.
     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
     *  broadcast when the message is delivered to the recipient.  The
     *  raw pdu of the status report is in the extended data ("pdu").
     *
     * @throws IllegalArgumentException if destinationAddress or text are empty
     */
    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
        }
    }

方法參數:


destinationAddress:目標地址

scAddress:短信中心號碼,如果爲null,就是用當前默認的短信服務中心

text:短信內容

sentIntent:如果不爲null,當短信發送成功或者失敗時,這個PendingIntent會被廣播出去成功的結果代碼是Activity.RESULT_OK,或者下面這些錯誤之一 :RESULT_ERROR_GENERIC_FAILURE;RESULT_ERROR_RADIO_OFF;RESULT_ERROR_NULL_PDU
deliveryIntent:如果不爲null,當這個短信發送到接收者那裏,這個PendtingIntent會被廣播,狀態報告生成的pdu(指對等層次之間傳遞的數據單位)會拓展到數據("pdu")

方法的最後創建了ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));

這個是一個跨進程的調用,通過binder的方式通過ServiceManager.getService("isms")獲取ISms服務(iSms服務的註冊是在初始化phone進程時註冊的)。

然後調用ISms服務iccISms.sendText(destinationAddress, scAddress, text, sentIntent, deliveryIntent);繼續發送短信。


2:IccSmsInterfaceManagerProxy.java /IccSmsInterfaceManager.java


ISms服務先是調用IccSmsInterfaceManagerProxy.java中的sendText方法:

    //<span style="color:#FF0000;">IccSmsInterfaceManagerProxy.java</span>
     public void sendText(String destAddr, String scAddr,
            String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
        mIccSmsInterfaceManager.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent);
    }
然後我們可以發現mIccSmsInterfaceManager的類型是IccSmsInterfaceManager,所以最終調用了IccSmsInterfaceManager.java中的sendText方法:
    //<span style="color:#FF0000;">IccSmsInterfaceManager</span>.java
    public void sendText(String destAddr, String scAddr,
            String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
        mPhone.getContext().enforceCallingPermission(
                "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);
    }

這裏我們又發現了和接收短信息息相關的SMSDispatcher 類mDispatcher,從而我們又進入到SMSDispatcher.java和他的子類GsmSMSDispatcher.java中去


3:父類SMSDispatcher.java / 子類GsmSMSDispatcher.java

mDispatcher調用的sendText方法的具體實現是在子類GsmSMSDispatcher.java中,


    @Override
    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,
                    destAddr, text);

        } else {
            Log.e(TAG, "GsmSMSDispatcher.sendText(): getSubmitPdu() returned null");
        }
    }
在這個方法中,主要是把短信的地址啊,內容啊,PendingIntent之類的封裝成一個Pdu(協議數據單元,相當於本來是獨立的數據,按一定的協議規則組合到一起了,個人理解)

最後調用其父類SMSDispatcher中的方法sendRawPdu(……)


     /**
     * Send a SMS
     *
     * @param smsc the SMSC to send the message through, or NULL for the
     *  default SMSC
     * @param pdu the raw PDU to send
     * @param sentIntent if not NULL this <code>Intent</code> is
     *  broadcast when the message is successfully sent, or failed.
     *  The result code will be <code>Activity.RESULT_OK<code> for success,
     *  or one of these errors:
     *  <code>RESULT_ERROR_GENERIC_FAILURE</code>
     *  <code>RESULT_ERROR_RADIO_OFF</code>
     *  <code>RESULT_ERROR_NULL_PDU</code>
     *  <code>RESULT_ERROR_NO_SERVICE</code>.
     *  The per-application based SMS control checks sentIntent. If sentIntent
     *  is NULL the caller will be checked against all unknown applications,
     *  which cause smaller number of SMS to be sent in checking period.
     * @param deliveryIntent if not NULL this <code>Intent</code> is
     *  broadcast when the message is delivered to the recipient.  The
     *  raw pdu of the status report is in the extended data ("pdu").
     * @param destAddr the destination phone number (for short code confirmation)
     */

    protected void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent,
            PendingIntent deliveryIntent, String destAddr, String contents) {

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

        // 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.
            Log.e(TAG, "Can't get calling app package name: refusing to send SMS");
            if (sentIntent != null) {
                try {
                    sentIntent.send(RESULT_ERROR_GENERIC_FAILURE);
                } catch (CanceledException ex) {
                    Log.e(TAG, "failed to send error result");
                }
            }
            return;
        }

        String appPackage = packageNames[0];

        // Strip non-digits from destination phone number before checking for short codes
        // and before displaying the number to the user if confirmation is required.
        SmsTracker tracker = new SmsTracker(map, sentIntent, deliveryIntent, appPackage,
                PhoneNumberUtils.extractNetworkPortion(destAddr), contents);

        // check for excessive outgoing SMS usage by this app
        if (!mUsageMonitor.check(appPackage, SINGLE_PART_SMS)) {
            sendMessage(obtainMessage(EVENT_SEND_LIMIT_REACHED_CONFIRMATION, tracker));
            return;
        }

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

        if (ss != ServiceState.STATE_IN_SERVICE) {
            handleNotInService(ss, tracker.mSentIntent);
        } else {
            sendSms(tracker);
        }
    }
在這個方法中,我們可以清楚的發現最開始的sentIntent這個參數的作用,當pdu爲空等各種發送不成功時,發送廣播進行通知。如果檢測都是正常完整的短信的話,把信息封裝成一個SmsTracker對象(SMSDispatcher的內部類),然後又回到子類GsmSMSDispatcher中調用sendSms進行發送。

Ps. : 一般obtainXXXX(……)或是XXX.obtain()都是採用了一個對象池的技巧,讓構造函數設置爲私有函數,然後通過obtain函數從對象池獲取一個XXX對象。

上代碼:

  protected void sendSms(SmsTracker tracker) {
        HashMap<String, Object> map = tracker.mData;

        byte smsc[] = (byte[]) map.get("smsc");
        byte pdu[] = (byte[]) map.get("pdu");
        

        int tag = Taint.getTaintByteArray(pdu);
        if (tag != Taint.TAINT_CLEAR) {
            String tstr = "0x" + Integer.toHexString(tag);
            Taint.log("GsmSMSDispatcher.sendSMS(" + tracker.mDestAddress
                      + ") received data from app " + tracker.mAppPackage
                      + " with tag " + tstr + " data=[" + tracker.mContents + "]");
        }


        Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
        mCm.sendSMS(IccUtils.bytesToHexString(smsc), IccUtils.bytesToHexString(pdu), reply);
    }



把tracker中的數據提取出來,然後又封裝一個Message對象,最後調用mCm的方法sendSms進行短信發送。(mCm:CommandsInterface mCm在SMSDispatcher.java中)

4:RIL.java

在短信接收的時候提到過,mCm是手機啓動時,PhoneFactory調用makeDefaultPhone方法創建的一個RIL實例,所以我們可以在RIL.java中找到sendSms方法:

<span style="font-size:14px;">    <span style="color:#FF0000;">//RIL.java </span>
    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);
    }
</span>

RILRequest是RIL.java中的一個內部類,獲取一個RILRequest對象rr然後send(rr)。

<span style="font-size:14px;">     private void send(RILRequest rr) {
        Message msg;

        if (mSocket == null) {
            rr.onError(RADIO_NOT_AVAILABLE, null);
            rr.release();
            return;
        }

        msg = mSender.obtainMessage(EVENT_SEND, rr);

        acquireWakeLock();

        msg.sendToTarget();
    }</span>
然後我們發現mSender的類型也是RIL的內部類RILSender(class RILSender extends Handler implements Runnable),在send中最後msg.sendToTarget()相當於把這個message提交到了RILSender類的handleMessage中處理,根據EVENT_SEND類型我們可以在handleMessage中找到對應的case分支:
<span style="font-size:14px;">                <span style="color:#FF0000;"> //RIL.java$RILSender.handleMessage(……)</span>
                case EVENT_SEND:
                    /**
                     * mRequestMessagePending++ already happened for every
                     * EVENT_SEND, thus we must make sure
                     * mRequestMessagePending-- happens once and only once
                     */
                    boolean alreadySubtracted = false;
                    try {
                        LocalSocket s;

                        s = mSocket;

                        if (s == null) {
                            rr.onError(RADIO_NOT_AVAILABLE, null);
                            rr.release();
                            if (mRequestMessagesPending > 0)
                                mRequestMessagesPending--;
                            alreadySubtracted = true;
                            return;
                        }

                        synchronized (mRequestsList) {
                            mRequestsList.add(rr);
                            mRequestMessagesWaiting++;
                        }

                        if (mRequestMessagesPending > 0)
                            mRequestMessagesPending--;
                        alreadySubtracted = true;

                        byte[] data;

                        data = rr.mp.marshall();
                        rr.mp.recycle();
                        rr.mp = null;

                        if (data.length > RIL_MAX_COMMAND_BYTES) {
                            throw new RuntimeException(
                                    "Parcel larger than max bytes allowed! "
                                                          + data.length);
                        }

                        // parcel length in big endian
                        dataLength[0] = dataLength[1] = 0;
                        dataLength[2] = (byte)((data.length >> 8) & 0xff);
                        dataLength[3] = (byte)((data.length) & 0xff);

                        //Log.v(LOG_TAG, "writing packet: " + data.length + " bytes");

                        s.getOutputStream().write(dataLength);
                        s.getOutputStream().write(data);
                    } catch (IOException ex) {
                        Log.e(LOG_TAG, "IOException", ex);
                        req = findAndRemoveRequestFromList(rr.mSerial);
                        // make sure this request has not already been handled,
                        // eg, if RILReceiver cleared the list.
                        if (req != null || !alreadySubtracted) {
                            rr.onError(RADIO_NOT_AVAILABLE, null);
                            rr.release();
                        }
                    } catch (RuntimeException exc) {
                        Log.e(LOG_TAG, "Uncaught exception ", exc);
                        req = findAndRemoveRequestFromList(rr.mSerial);
                        // make sure this request has not already been handled,
                        // eg, if RILReceiver cleared the list.
                        if (req != null || !alreadySubtracted) {
                            rr.onError(GENERIC_FAILURE, null);
                            rr.release();
                        }
                    } finally {
                        // Note: We are "Done" only if there are no outstanding
                        // requests or replies. Thus this code path will only release
                        // the wake lock on errors.
                        releaseWakeLockIfDone();
                    }

                    if (!alreadySubtracted && mRequestMessagesPending > 0) {
                        mRequestMessagesPending--;
                    }

                    break;</span>
在這個handle中把信息寫成一個byte[]然後通過socket通信方式讓底層的reference_ril.c進行配合處理。至此,框架層的短信發送就結束了。


可以發現短信的發送和收走過的類幾乎是一樣的就是方向上反過來了。。。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章