Android5.0框架層短信接收過程分析

本文分析使用的是android5.0的源代碼,涉及的相關文件:

frameworks\opt\telephony\src\java\com\android\internal\telephony\RIL.java

frameworks\base\core\java\com\android\internal\util\StateMachine.java

frameworks\opt\telephony\src\java\com\android\internal\telephony\InboundSmsTracker.java

frameworks\opt\telephony\src\java\com\android\internal\telephony\InboundSmsHandler.java

frameworks\opt\telephony\src\java\com\android\internal\telephony\gsm\GsmInboundSmsHandler.java


先上時序圖如下:




第一部分:RIL.java

當安卓系統收到短信後,首先被提交到RIL.java中進行處理:

1,在RIL類中有一個內部類RILReceiver,在該類的run方法中用來不斷循環,獲取socket傳來的數據,調用readRilMessage方法獲取短信Parcel對象p,調用 processResponse(p)方法進行處理

    class RILReceiver implements Runnable {
        byte[] buffer;

        RILReceiver() {
            buffer = new byte[RIL_MAX_COMMAND_BYTES];
        }

        @Override
        public void
        run() {
            int retryCount = 0;
            String rilSocket = "rild";

            try {for (;;) {
                LocalSocket s = null;
                LocalSocketAddress l;

                if (mInstanceId == null || mInstanceId == 0 ) {
                    rilSocket = SOCKET_NAME_RIL[0];
                } else {
                    rilSocket = SOCKET_NAME_RIL[mInstanceId];
                }

                try {
                    s = new LocalSocket();
                    l = new LocalSocketAddress(rilSocket,
                            LocalSocketAddress.Namespace.RESERVED);
                    s.connect(l);
                } catch (IOException ex){
                    try {
                        if (s != null) {
                            s.close();
                        }
                    } catch (IOException ex2) {
                        //ignore failure to close after failure to connect
                    }

                    // don't print an error message after the the first time
                    // or after the 8th time

                    if (retryCount == 8) {
                        Rlog.e (RILJ_LOG_TAG,
                            "Couldn't find '" + rilSocket
                            + "' socket after " + retryCount
                            + " times, continuing to retry silently");
                    } else if (retryCount > 0 && retryCount < 8) {
                        Rlog.i (RILJ_LOG_TAG,
                            "Couldn't find '" + rilSocket
                            + "' socket; retrying after timeout");
                    }

                    try {
                        Thread.sleep(SOCKET_OPEN_RETRY_MILLIS);
                    } catch (InterruptedException er) {
                    }

                    retryCount++;
                    continue;
                }

                retryCount = 0;

                mSocket = s;
                Rlog.i(RILJ_LOG_TAG, "Connected to '" + rilSocket + "' socket");

                int length = 0;
                try {
                    InputStream is = mSocket.getInputStream();

                    for (;;) {
                        Parcel p;

                        length = readRilMessage(is, buffer);

                        if (length < 0) {
                            // End-of-stream reached
                            break;
                        }

                        p = Parcel.obtain();
                        p.unmarshall(buffer, 0, length);
                        p.setDataPosition(0);

                        //Rlog.v(RILJ_LOG_TAG, "Read packet: " + length + " bytes");

                        processResponse(p);
                        p.recycle();
                    }
                } catch (java.io.IOException ex) {
                    Rlog.i(RILJ_LOG_TAG, "'" + rilSocket + "' socket closed",
                          ex);
                } catch (Throwable tr) {
                    Rlog.e(RILJ_LOG_TAG, "Uncaught exception read length=" + length +
                        "Exception:" + tr.toString());
                }

                Rlog.i(RILJ_LOG_TAG, "Disconnected from '" + rilSocket
                      + "' socket");

                setRadioState (RadioState.RADIO_UNAVAILABLE);

                try {
                    mSocket.close();
                } catch (IOException ex) {
                }

                mSocket = null;
                RILRequest.resetSerial();

                // Clear request list on close
                clearRequestList(RADIO_NOT_AVAILABLE, false);
            }} catch (Throwable tr) {
                Rlog.e(RILJ_LOG_TAG,"Uncaught exception", tr);
            }

            /* We're disconnected so we don't know the ril version */
            notifyRegistrantsRilConnectionChanged(-1);
        }
    }


2,在processResponse方法中,對應接收短信的Parcel類型是RESPONSE_UNSOLICITED(未經請求),發送短信時是RESPONSE_SOLICITED(被請求的),因此接收短信將調用processUnsolicited (p)方法。

private void processResponse (Parcel p) {
        int type;

        type = p.readInt();

        if (type == RESPONSE_UNSOLICITED) {
            processUnsolicited (p);
        } else if (type == RESPONSE_SOLICITED) {
            RILRequest rr = processSolicited (p);
            if (rr != null) {
                rr.release();
                decrementWakeLock();
            }
        }
    }

3,processUnsolicited方法將對不同的命令調用不同的操作,通過調試可以發現將調用的是RIL_UNSOL_RESPONSE_NEW_SMS分支的操作,把從Parcel中取出來的字符串封裝成一個SmsMessage對象

private void processUnsolicited (Parcel p) {
  …………
  case RIL_UNSOL_RESPONSE_NEW_SMS: {
                if (RILJ_LOGD) unsljLog(response);

                // FIXME this should move up a layer
                String a[] = new String[2];

                a[1] = (String)ret;

                SmsMessage sms;

                sms = SmsMessage.newFromCMT(a);
                if (mGsmSmsRegistrant != null) {
                    mGsmSmsRegistrant
                        .notifyRegistrant(new AsyncResult(null, sms, null));
                }
            break;
…………
}
4,然後調用   mGsmSmsRegistrant.notifyRegistrant(new AsyncResult(null, sms, null));

這裏需要注意的是mGsmSmsRegistrant這個對象。該對象是在 RIL類的父類BaseCommands類中申明,並通過setOnNewGsmSms方法來進行初始化的,給這個Registrant指定handler h,待發送給handler的消息的類型 what 以及待發送給handler的待處理的對象obj

   frameworks\opt\telephony\src\java\com\android\internal\telephony\BaseCommands.java
 @Override
    public void setOnNewGsmSms(Handler h, int what, Object obj) {
        mGsmSmsRegistrant = new Registrant (h, what, obj);//注意這個what
    }
知道了這個mGsmSmsRegistrant這個對象是一個Registrant對象後,我們來看他的notifyRegistrant方法

   public void notifyRegistrant(AsyncResult ar)
    {
        internalNotifyRegistrant (ar.result, ar.exception);
    }

    /*package*/ void
    internalNotifyRegistrant (Object result, Throwable exception)
    {
        Handler h = getHandler();

        if (h == null) {
            clear();
        } else {
            Message msg = Message.obtain();

            msg.what = what;//這個what是在new Registrant(Handler h, int what, Object obj)進行初始化時賦值的一個成員變量

            
            msg.obj = new AsyncResult(userObj, result, exception);
            
            h.sendMessage(msg);
        }
    }

通過internalNotifyRegistrant方法我們可以發現,msg中包含了消息的類型,msg.obj中包含了我們將要處理的短信對象(SMsMessage對象),然後通過初始化時指定的Handler對象來處理這個msg,因此,我們需要找到這個處理短信數據的對象的Handler類。

在GsmInboundSmsHandler.java的GsmInboundSmsHandler類中發現GsmInboundSmsHandler構造函數中調用了setOnNewGsmSms方法

 frameworks\opt\telephony\src\java\com\android\internal\telephony\gsm\GsmInboundSmsHandler.java

private GsmInboundSmsHandler(Context context, SmsStorageMonitor storageMonitor,
            PhoneBase phone) {
        super("GsmInboundSmsHandler", context, storageMonitor, phone,
                GsmCellBroadcastHandler.makeGsmCellBroadcastHandler(context, phone));
        phone.mCi.setOnNewGsmSms(getHandler(), EVENT_NEW_SMS, null);
        mDataDownloadHandler = new UsimDataDownloadHandler(phone.mCi);
    }

GsmInboundSmsHandler父類是InboundSmsHandler
InboundSmsHandler的父類是StateMachine
getHandler()是StateMachine類中的方法,返回成員變量private SmHandler mSmHandler;
    public final Handler getHandler() {
        return mSmHandler;
    }
因此,我們可以發現。 mGsmSmsRegistrant.notifyRegistrant(new AsyncResult(null, sms, null));這個函數的實質就是,向SmHandler對象發送了一個消息Msg,消息的類型是EVENT_NEW_SMS,消息包含的對象時短信SmsMessgae對象。

至此,第一部分RIL.java部分的短信處理完畢


第二部分:StateMachine,InboundSmsHandler,GsmInboundSmsHandler 通過狀態機處理短信接收流程

這一部分是和android4.X版本中的短信接收較爲不同的部分。這部分使用的是一個狀態機的設計模式來解決短信問題。(暫未完全理解)

接下來涉及到的主要三個類StateMachine,InboundSmsHandler,GsmInboundSmsHandler,他們之間是繼承的關係,InboundSmsHandler 繼承自StateMachine,GsmInboundSmsHandler繼承自InboundSmsHandler。


在第二部分的StateMachine類中主要是通過StateMachine的內部類SmHandler來進行消息的分發處理。(SmHandler代碼都在StateMachine.java中)

(通過時序圖我們可以發現StateMachine(SmHandler)主要是處理消息的分發,主要是負責到達短信的處理, GsmInboundSmsHandler僅負責Gsm短信相關的判斷處理。我們可以發現,消息的分發是最一般的處理,短信處理是次一般的處理,而Gsm短信則是最特殊的處理。不得不說,這源碼寫的真的漂亮)

在第一部分中RIL層向SmHandler對象發送了一個類型爲EVENT_NEW_SMS的消息。由Handler機制,我們可以知道,必然會通過SmHandler的handleMessage方法進行處理,即時序圖中的第5步。

6,SmHandler的handleMessage方法主要是就是調用SmHandler的processMsg方法來處理消息。

 
private final State processMsg(Message msg) {
            StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
            if (mDbg) {
                mSm.log("processMsg: " + curStateInfo.state.getName());
            }

            if (isQuit(msg)) {
                transitionTo(mQuittingState);
            } else {
                while (!curStateInfo.state.processMessage(msg)) {
                    /**
                     * Not processed
                     */
                    curStateInfo = curStateInfo.parentStateInfo;
                    if (curStateInfo == null) {
                        /**
                         * No parents left so it's not handled
                         */
                        mSm.unhandledMessage(msg);
                        break;
                    }
                    if (mDbg) {
                        mSm.log("processMsg: " + curStateInfo.state.getName());
                    }
                }
            }
            return (curStateInfo != null) ? curStateInfo.state : null;
        }

這裏需要注意curStateInfo.state是一個State對象,主要包括在InboundSmsHandler類中定義幾個內部類,DefaultState,StartupState,IdleState,DeliveringState,WaitingState這5個State之間相關轉換,這5個類都是繼承自State類。

我是這麼理解InboundSmsHandler的,中文表述成,已到達短信處理機,這個處理機有5個狀態,分別是DefaultState,StartupState,IdleState,DeliveringState,WaitingState。在我的調試過程中,主要是兩個狀態的轉換,當第一次調用SmHandler的processMsg方法時,curStateInfo.state.processMessage(msg)是通過WaitingState類的processMessage方法處理消息,然後無法進行處理返回後,通過curStateInfo = curStateInfo.parentStateInfo;把WaitingState變成其父狀態DeliveringState。然後再調用curStateInfo.state.processMessage(msg),即調用DeliveringState的processMessage的方法進行短信消息處理。

 InboundSmsHandler.java

class DeliveringState extends State {
        @Override
        public void enter() {
            if (DBG) log("entering Delivering state");
        }

        @Override
        public void exit() {
            if (DBG) log("leaving Delivering state");
        }

        @Override
        public boolean processMessage(Message msg) {
            log("DeliveringState.processMessage:" + msg.what);
            switch (msg.what) {
                case EVENT_NEW_SMS:
                    // handle new SMS from RIL
                    handleNewSms((AsyncResult) msg.obj);
                    sendMessage(EVENT_RETURN_TO_IDLE);
                    return HANDLED;

                case EVENT_INJECT_SMS:
                    // handle new injected SMS
                    handleInjectSms((AsyncResult) msg.obj);
                    sendMessage(EVENT_RETURN_TO_IDLE);
                    return HANDLED;

                case EVENT_BROADCAST_SMS:
                    // if any broadcasts were sent, transition to waiting state
                    if (processMessagePart((InboundSmsTracker) msg.obj)) {
                        transitionTo(mWaitingState);
                    }
                    return HANDLED;

                case EVENT_RETURN_TO_IDLE:
                    // return to idle after processing all other messages
                    transitionTo(mIdleState);
                    return HANDLED;

                case EVENT_RELEASE_WAKELOCK:
                    mWakeLock.release();    // decrement wakelock from previous entry to Idle
                    if (!mWakeLock.isHeld()) {
                        // wakelock should still be held until 3 seconds after we enter Idle
                        loge("mWakeLock released while delivering/broadcasting!");
                    }
                    return HANDLED;

                // we shouldn't get this message type in this state, log error and halt.
                case EVENT_BROADCAST_COMPLETE:
                case EVENT_START_ACCEPTING_SMS:
                default:
                    // let DefaultState handle these unexpected message types
                    return NOT_HANDLED;
            }
        }
    }

已知消息類型是EVENT_NEW_SMS,即執行EVENT_NEW_SMS分支的代碼,即時序圖中第13步調用handleNewSms((AsyncResult) msg.obj)

   void handleNewSms(AsyncResult ar) {
        if (ar.exception != null) {
            loge("Exception processing incoming SMS: " + ar.exception);
            return;
        }

        int result;
        try {
            SmsMessage sms = (SmsMessage) ar.result;
            result = dispatchMessage(sms.mWrappedSmsMessage);
        } catch (RuntimeException ex) {
            loge("Exception dispatching message", ex);
            result = Intents.RESULT_SMS_GENERIC_ERROR;
        }

        // RESULT_OK means that the SMS will be acknowledged by special handling,
        // e.g. for SMS-PP data download. Any other result, we should ack here.
        if (result != Activity.RESULT_OK) {
            boolean handled = (result == Intents.RESULT_SMS_HANDLED);
            notifyAndAcknowledgeLastIncomingSms(handled, result, null);
        }
    }

14,該方法取出消息中包含的SmsMessage對象。然後調用方法dispatchMessage;

15,在dispatchMessage中調用dispatchMessageRadioSpecific方法,該方法是在GsmInboundSmsHandler類中定義的方法。

16,dispatchMessageRadioSpecific方法的最後重新回到InboundSmsHandler.java中的dispatchNormalMessage方法,把短信重新包裝成一個InboundSmsTracker對象

17-19,然後調用addTrackerToRawTableAndSendMessage(tracker)方法,該方法會調用父類StateMachine的sendMessage(EVENT_BROADCAST_SMS, tracker)方法,向其內部類SmHandler對象發送一個消息,消息的類型是EVENT_BROADCAST_SMS,消息包含的對象是InboundSmsTracker對象

20-23,StateMachine的內部類SmHandler對象再次處理消息,最後消息也將在DeliveringState的processMessage方法中處理,執行其EVENT_BROADCAST_SMS分支的代碼。

24:最後調用InboundSmsHandler類中的processMessagePart((InboundSmsTracker) msg.obj)方法

   /**
     * Process the inbound SMS segment. If the message is complete, send it as an ordered
     * broadcast to interested receivers and return true. If the message is a segment of an
     * incomplete multi-part SMS, return false.
     * @param tracker the tracker containing the message segment to process
     * @return true if an ordered broadcast was sent; false if waiting for more message segments
     */
    boolean processMessagePart(InboundSmsTracker tracker) {
        int messageCount = tracker.getMessageCount();
        byte[][] pdus;
        int destPort = tracker.getDestPort();

        if (messageCount == 1) {
            // single-part message
            pdus = new byte[][]{tracker.getPdu()};
        } else {
            // multi-part message
            Cursor cursor = null;
            try {
                // used by several query selection arguments
                String address = tracker.getAddress();
                String refNumber = Integer.toString(tracker.getReferenceNumber());
                String count = Integer.toString(tracker.getMessageCount());

                // query for all segments and broadcast message if we have all the parts
                String[] whereArgs = {address, refNumber, count};
                cursor = mResolver.query(sRawUri, PDU_SEQUENCE_PORT_PROJECTION,
                        SELECT_BY_REFERENCE, whereArgs, null);

                int cursorCount = cursor.getCount();
                if (cursorCount < messageCount) {
                    // Wait for the other message parts to arrive. It's also possible for the last
                    // segment to arrive before processing the EVENT_BROADCAST_SMS for one of the
                    // earlier segments. In that case, the broadcast will be sent as soon as all
                    // segments are in the table, and any later EVENT_BROADCAST_SMS messages will
                    // get a row count of 0 and return.
                    return false;
                }

                // All the parts are in place, deal with them
                pdus = new byte[messageCount][];
                while (cursor.moveToNext()) {
                    // subtract offset to convert sequence to 0-based array index
                    int index = cursor.getInt(SEQUENCE_COLUMN) - tracker.getIndexOffset();

                    pdus[index] = HexDump.hexStringToByteArray(cursor.getString(PDU_COLUMN));

                    // Read the destination port from the first segment (needed for CDMA WAP PDU).
                    // It's not a bad idea to prefer the port from the first segment in other cases.
                    if (index == 0 && !cursor.isNull(DESTINATION_PORT_COLUMN)) {
                        int port = cursor.getInt(DESTINATION_PORT_COLUMN);
                        // strip format flags and convert to real port number, or -1
                        port = InboundSmsTracker.getRealDestPort(port);
                        if (port != -1) {
                            destPort = port;
                        }
                    }
                }
            } catch (SQLException e) {
                loge("Can't access multipart SMS database", e);
                return false;
            } finally {
                if (cursor != null) {
                    cursor.close();
                }
            }
        }

        BroadcastReceiver resultReceiver = new SmsBroadcastReceiver(tracker);

        if (destPort == SmsHeader.PORT_WAP_PUSH) {
            // Build up the data stream
            ByteArrayOutputStream output = new ByteArrayOutputStream();
            for (byte[] pdu : pdus) {
                // 3GPP needs to extract the User Data from the PDU; 3GPP2 has already done this
                if (!tracker.is3gpp2()) {
                    SmsMessage msg = SmsMessage.createFromPdu(pdu, SmsConstants.FORMAT_3GPP);
                    pdu = msg.getUserData();
                }
                output.write(pdu, 0, pdu.length);
            }
            int result = mWapPush.dispatchWapPdu(output.toByteArray(), resultReceiver, this);
            if (DBG) log("dispatchWapPdu() returned " + result);
            // result is Activity.RESULT_OK if an ordered broadcast was sent
            return (result == Activity.RESULT_OK);
        }

        Intent intent = new Intent(Intents.SMS_FILTER_ACTION);
        List<String> carrierPackages = null;
        UiccCard card = UiccController.getInstance().getUiccCard();
        if (card != null) {
            carrierPackages = card.getCarrierPackageNamesForIntent(
                    mContext.getPackageManager(), intent);
        }
        if (carrierPackages != null && carrierPackages.size() == 1) {
            intent.setPackage(carrierPackages.get(0));
            intent.putExtra("destport", destPort);
        } else {
            setAndDirectIntent(intent, destPort);
        }

        intent.putExtra("pdus", pdus);
        intent.putExtra("format", tracker.getFormat());
        dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
                AppOpsManager.OP_RECEIVE_SMS, resultReceiver, UserHandle.OWNER);
        return true;
    }

該方法主要是創建了 BroadcastReceiver resultReceiver = new SmsBroadcastReceiver(tracker);一個廣播接收器。

創建了intent,並通過setAndDirectIntent方法把intent的action設置成  Intents.SMS_DELIVER_ACTION。這代表這個廣播只能被默認短信應用接收。

最後調用dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,AppOpsManager.OP_RECEIVE_SMS, resultReceiver, UserHandle.OWNER);發送廣播。

  /**
     * Dispatch the intent with the specified permission, appOp, and result receiver, using
     * this state machine's handler thread to run the result receiver.
     *
     * @param intent the intent to broadcast
     * @param permission receivers are required to have this permission
     * @param appOp app op that is being performed when dispatching to a receiver
     * @param user user to deliver the intent to
     */
    protected void dispatchIntent(Intent intent, String permission, int appOp,
            BroadcastReceiver resultReceiver, UserHandle user) {
        intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
        SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
        if (user.equals(UserHandle.ALL)) {
            // Get a list of currently started users.
            int[] users = null;
            try {
                users = ActivityManagerNative.getDefault().getRunningUserIds();
            } catch (RemoteException re) {
            }
            if (users == null) {
                users = new int[] {user.getIdentifier()};
            }
            // Deliver the broadcast only to those running users that are permitted
            // by user policy.
            for (int i = users.length - 1; i >= 0; i--) {
                UserHandle targetUser = new UserHandle(users[i]);
                if (users[i] != UserHandle.USER_OWNER) {
                    // Is the user not allowed to use SMS?
                    if (mUserManager.hasUserRestriction(UserManager.DISALLOW_SMS, targetUser)) {
                        continue;
                    }
                    // Skip unknown users and managed profiles as well
                    UserInfo info = mUserManager.getUserInfo(users[i]);
                    if (info == null || info.isManagedProfile()) {
                        continue;
                    }
                }
                // Only pass in the resultReceiver when the USER_OWNER is processed.
                mContext.sendOrderedBroadcastAsUser(intent, targetUser, permission, appOp,
                        users[i] == UserHandle.USER_OWNER ? resultReceiver : null,
                        getHandler(), Activity.RESULT_OK, null, null);
            }
        } else {
            mContext.sendOrderedBroadcastAsUser(intent, user, permission, appOp,
                    resultReceiver,
                    getHandler(), Activity.RESULT_OK, null, null);
        }
    }

29,最後調用sendOrderedBroadcastAsUser方法發送廣播,且該廣播的最後一個接收者是之前創建的SmsBroadcastReceiver類型的廣播


30,因此,當發送完這條給默認短信應用專用的短信廣播後,將會回到SmsBroadcastReceiver的onReceive方法中

    /**
     * Handler for an {@link InboundSmsTracker} broadcast. Deletes PDUs from the raw table and
     * logs the broadcast duration (as an error if the other receivers were especially slow).
     */
    private final class SmsBroadcastReceiver extends BroadcastReceiver {
        private final String mDeleteWhere;
        private final String[] mDeleteWhereArgs;
        private long mBroadcastTimeNano;

        SmsBroadcastReceiver(InboundSmsTracker tracker) {
            mDeleteWhere = tracker.getDeleteWhere();
            mDeleteWhereArgs = tracker.getDeleteWhereArgs();
            mBroadcastTimeNano = System.nanoTime();
        }

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (action.equals(Intents.SMS_FILTER_ACTION)) {
                int rc = getResultCode();
                if (rc == Activity.RESULT_OK) {
                  // Overwrite pdus data if the SMS filter has set it.
                  Bundle resultExtras = getResultExtras(false);
                  if (resultExtras != null && resultExtras.containsKey("pdus")) {
                      intent.putExtra("pdus", (byte[][]) resultExtras.get("pdus"));
                  }
                  if (intent.hasExtra("destport")) {
                      int destPort = intent.getIntExtra("destport", -1);
                      intent.removeExtra("destport");
                      setAndDirectIntent(intent, destPort);
                      if (SmsManager.getDefault().getAutoPersisting()) {
                          final Uri uri = writeInboxMessage(intent);
                          if (uri != null) {
                              // Pass this to SMS apps so that they know where it is stored
                              intent.putExtra("uri", uri.toString());
                          }
                      }
                      dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
                                     AppOpsManager.OP_RECEIVE_SMS, this, UserHandle.OWNER);
                  } else {
                      loge("destport doesn't exist in the extras for SMS filter action.");
                  }
                } else {
                  // Drop this SMS.
                  log("SMS filtered by result code " + rc);
                  deleteFromRawTable(mDeleteWhere, mDeleteWhereArgs);
                  sendMessage(EVENT_BROADCAST_COMPLETE);
                }
            } else if (action.equals(Intents.SMS_DELIVER_ACTION)) {
                // Now dispatch the notification only intent
                intent.setAction(Intents.SMS_RECEIVED_ACTION);
                intent.setComponent(null);
                // All running users will be notified of the received sms.
                dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
                        AppOpsManager.OP_RECEIVE_SMS, this, UserHandle.ALL);
            } else if (action.equals(Intents.WAP_PUSH_DELIVER_ACTION)) {
                // Now dispatch the notification only intent
                intent.setAction(Intents.WAP_PUSH_RECEIVED_ACTION);
                intent.setComponent(null);
                // Only the primary user will receive notification of incoming mms.
                // That app will do the actual downloading of the mms.
                dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
                        AppOpsManager.OP_RECEIVE_SMS, this, UserHandle.OWNER);
            } else {
                // Now that the intents have been deleted we can clean up the PDU data.
                if (!Intents.DATA_SMS_RECEIVED_ACTION.equals(action)
                        && !Intents.SMS_RECEIVED_ACTION.equals(action)
                        && !Intents.DATA_SMS_RECEIVED_ACTION.equals(action)
                        && !Intents.WAP_PUSH_RECEIVED_ACTION.equals(action)) {
                    loge("unexpected BroadcastReceiver action: " + action);
                }

                int rc = getResultCode();
                if ((rc != Activity.RESULT_OK) && (rc != Intents.RESULT_SMS_HANDLED)) {
                    loge("a broadcast receiver set the result code to " + rc
                            + ", deleting from raw table anyway!");
                } else if (DBG) {
                    log("successful broadcast, deleting from raw table.");
                }

                deleteFromRawTable(mDeleteWhere, mDeleteWhereArgs);
                sendMessage(EVENT_BROADCAST_COMPLETE);

                int durationMillis = (int) ((System.nanoTime() - mBroadcastTimeNano) / 1000000);
                if (durationMillis >= 5000) {
                    loge("Slow ordered broadcast completion time: " + durationMillis + " ms");
                } else if (DBG) {
                    log("ordered broadcast completed in: " + durationMillis + " ms");
                }
            }
        }
    }

看到else if (action.equals(Intents.SMS_DELIVER_ACTION))分支處,重新設置intent的action爲 Intents.SMS_RECEIVED_ACTION,然後同樣通過dispatchIntent方法,將這條廣播發送給所有人。即所有普通應用將收到這條廣播。



疑問:爲什麼sendOrderedBroadcastAsUser方法發送的有序廣播無法被截斷?

分析完了短信接收,最大的疑問就是都是通過dispatchIntent方法最後調用sendOrderedBroadcastAsUser方法來發送廣播,這樣子理論上來說都是發送的有序廣播。那麼和網上的說法發送兩條廣播一條是發送給默認短信應用的有序廣播,一條是發給所有人的無序廣播的說法不一致啊。。


接下來,我進行了試驗測試,

我寫了兩個應用A,B,都註冊短信廣播,並且A的廣播優先級高於B,A和B都獲取短信後調用abortBroadcast截斷廣播。

A廣播代碼:

package com.yyt.sockettest;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Message;
import android.telephony.SmsMessage;
import android.util.Log;
import android.widget.Toast;


public class SmsBroadCastReceiver extends BroadcastReceiver {

	@Override
	public void onReceive(Context context, Intent intent) {
		// TODO Auto-generated method stub
		
		Bundle bundleres = getResultExtras(true);
		
		if(bundleres==null){
			Log.e("smsA", "bundleRes null");
		}
			
		else{
			Log.e("smsA", "bundleRes not null");
			int xx=bundleres.getInt("myCount");
			Log.e("Broadcast ResInt", Integer.toString(xx));
		}
			
		bundleres.putInt("myCount", 100);
		
		setResultExtras(bundleres);
		
	      Bundle bundle = intent.getExtras();   
	        Object[] object = (Object[])bundle.get("pdus");   
	        SmsMessage sms[]=new SmsMessage[object.length];   
	        for(int i=0;i<object.length;i++)   
	        {   
	            sms[i] = SmsMessage.createFromPdu((byte[])object[i]);   
	            Toast.makeText(context, "來自"+sms[i].getDisplayOriginatingAddress()+" 的消息是:"+sms[i].getDisplayMessageBody(), Toast.LENGTH_SHORT).show();   
	        }   
	        Log.e("smssms", sms[0].getDisplayMessageBody());
	        //終止廣播,在這裏我們可以稍微處理,根據用戶輸入的號碼可以實現短信防火牆。
	        abortBroadcast();   
	        Message msg = Message.obtain();
	        msg.obj = sms[0].getDisplayMessageBody();
	        MainActivity.handler.sendMessage(msg);
	}
	
}
靜態註冊的廣播,優先級爲1000
  <receiver android:name=".SmsBroadCastReceiver">  
            <intent-filter android:priority="1000">  
                <action android:name="android.provider.Telephony.SMS_RECEIVED"/>  
            </intent-filter>  
        </receiver>  

B廣播代碼:

package com.example.testsms2;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Message;
import android.telephony.SmsMessage;
import android.util.Log;
import android.widget.Toast;


public class SmsBroadCastReceiver extends BroadcastReceiver {

	@Override
	public void onReceive(Context context, Intent intent) {
		// TODO Auto-generated method stub
		Bundle bundleres = getResultExtras(true);
		if(bundleres==null){
			Log.e("smsB", "bundleRes null");
		}
			
		else{
			Log.e("smsB", "bundleRes not null");
			int xx=bundleres.getInt("myCount");
			Log.e("Broadcast ResInt22", Integer.toString(xx));
		}
			
		bundleres.putInt("myCount", 500);
		setResultExtras(bundleres);
	      Bundle bundle = intent.getExtras();   
	        Object[] object = (Object[])bundle.get("pdus");   
	        SmsMessage sms[]=new SmsMessage[object.length];   
	        for(int i=0;i<object.length;i++)   
	        {   
	            sms[i] = SmsMessage.createFromPdu((byte[])object[i]);   
	            Toast.makeText(context, "來自"+sms[i].getDisplayOriginatingAddress()+" 的消息是:"+sms[i].getDisplayMessageBody(), Toast.LENGTH_SHORT).show();   
	        }   
	        Log.e("smssms2222", sms[0].getDisplayMessageBody());
	        //終止廣播,在這裏我們可以稍微處理,根據用戶輸入的號碼可以實現短信防火牆。
	        abortBroadcast();   
	        Message msg = Message.obtain();
	        msg.obj = sms[0].getDisplayMessageBody();

	}
	
}

在MainActivity的OnCreate方法中動態註冊
		 receiver = new SmsBroadCastReceiver();
		 IntentFilter iFilter =new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
		 iFilter.setPriority(20000);
		 registerReceiver(receiver, iFilter);



模擬發送短信測試後,發現A,B應用都能獲取到短信。。。
我又進行測試,通過getResultExtras發現A,B都能獲取到非空的budle對象,且B應用輸出數字0,A應用輸出數字500,說明B確實優先A收到的短信,但是截斷不成功。

理論上說如果接收的是無序廣播,那麼getResultExtras方法返回的值必然是空,而非空,則代表是有序廣播,那麼無法截斷又如何解釋呢。。。。。。。

奇怪的問題。。。未完待續。。。。。。。。。。。。。




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