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方法返回的值必然是空,而非空,則代表是有序廣播,那麼無法截斷又如何解釋呢。。。。。。。
奇怪的問題。。。未完待續。。。。。。。。。。。。。