Android6.0中对短信的处理比起老版本还是变化有点大的。在分析源代码之前,我们可以先猜测一下Android中接收短信的大致流程。首先根据之前分析phone应用的经验,猜测最先接收到短信消息的肯定是Modem,接着上报的RILJ,RILJ在通知到XXXTracker,之后也许会有个SmsManager的东西作统一管理,再之后就是App层。当然,这仅仅是猜测,到底是不是需要看代码。
RILJ与RILD以及Modem的交互方式在之前的phone应用分析中就已经详细叙述了,这里直接从RILJ开始。由于接收短信底层主动上报的消息(非URC消息),因此由RILJ. processUnsolicited()处理。我们已经知道processUnsolicited()对消息的处理基本上都是分两个步骤的:1,解析出消息包;2,将详细包通知到其Registrant。如下:
private void processUnsolicited (Parcel p) { int response; Object ret;
response = p.readInt();
try {switch(response) { …… //这里其实针对的是GSM短信 case RIL_UNSOL_RESPONSE_NEW_SMS: ret = responseString(p); break; case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT: ret = responseString(p); break; case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM: ret = responseInts(p); break; …… //新来CDMA短信 case RIL_UNSOL_RESPONSE_CDMA_NEW_SMS: ret = responseCdmaSms(p); break; case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS: ret = responseRaw(p); break; case RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL: ret = responseVoid(p); break; …… }} catch (Throwable tr) { Rlog.e(RILJ_LOG_TAG, "Exception processing unsol response: " + response + "Exception:" + tr.toString()); return; }
switch(response) { …… //对GSM短信的处理,其实就是通知其注册者 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; } case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT: if (RILJ_LOGD) unsljLogRet(response, ret);
if (mSmsStatusRegistrant != null) { mSmsStatusRegistrant.notifyRegistrant( new AsyncResult(null, ret, null)); } break; case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM: if (RILJ_LOGD) unsljLogRet(response, ret);
int[] smsIndex = (int[])ret;
if(smsIndex.length == 1) { if (mSmsOnSimRegistrant != null) { mSmsOnSimRegistrant. notifyRegistrant(new AsyncResult(null, smsIndex, null)); } } else { if (RILJ_LOGD) riljLog(" NEW_SMS_ON_SIM ERROR with wrong length " + smsIndex.length); } break; ……
case RIL_UNSOL_SIM_SMS_STORAGE_FULL: if (RILJ_LOGD) unsljLog(response);
if (mIccSmsFullRegistrant != null) { mIccSmsFullRegistrant.notifyRegistrant(); } break;
…… //对新来CDMA短信的处理,也是通知其注册者 case RIL_UNSOL_RESPONSE_CDMA_NEW_SMS: if (RILJ_LOGD) unsljLog(response);
SmsMessage sms = (SmsMessage) ret;
if (mCdmaSmsRegistrant != null) { mCdmaSmsRegistrant .notifyRegistrant(new AsyncResult(null, sms, null)); } break;
case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS: if (RILJ_LOGD) unsljLogvRet(response, IccUtils.bytesToHexString((byte[])ret));
if (mGsmBroadcastSmsRegistrant != null) { mGsmBroadcastSmsRegistrant .notifyRegistrant(new AsyncResult(null, ret, null)); } break;
case RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL: if (RILJ_LOGD) unsljLog(response);
if (mIccSmsFullRegistrant != null) { mIccSmsFullRegistrant.notifyRegistrant(); } break; …… } } |
可以看到,对于不同类型的短信是作了区分处理,GSM短信通知给了mGsmSmsRegistrant,而CDMA短信通知给了mCdmaSmsRegistrant。在之前本博客对UICC卡的分析文章中:一张UICC卡是对应一个phone的,一个Phone对应一个RILJ。那这里就有点奇怪了,为什么一个RILJ会同时对GSM和CDMA短信作处理呢?难道一个RILJ有可能管理两个RILD么?这块还有待研究。
接着之前的分析,RILJ到底将来短信的消息通知给了谁呢,查找代码发现并没有XXXRegistrant.add()的地方,倒是有这样的一个函数,
@Override public void setOnNewCdmaSms(Handler h, int what, Object obj) { mCdmaSmsRegistrant = new Registrant (h, what, obj); } |
在之前的phone应用分析中其实也遇到过这样的注册方式,通过这样的方式其实就是明确指出了只有一个注册者会接收此通知消息。在SoureInsight中查找方法的调用者发现CdmaInboundSmsHandler在器构造方法中调用了注册函数。
private CdmaInboundSmsHandler(Context context, SmsStorageMonitor storageMonitor, PhoneBase phone, CdmaSMSDispatcher smsDispatcher) { super("CdmaInboundSmsHandler", context, storageMonitor, phone, CellBroadcastHandler.makeCellBroadcastHandler(context, phone)); mSmsDispatcher = smsDispatcher; mServiceCategoryProgramHandler = CdmaServiceCategoryProgramHandler.makeScpHandler(context, phone.mCi); //设置为RIL_UNSOL_RESPONSE_CDMA_NEW_SMS的监听者 phone.mCi.setOnNewCdmaSms(getHandler(), EVENT_NEW_SMS, null); } |
当RILJ接收RIL_UNSOL_RESPONSE_CDMA_NEW_SMS,向CdmaInboundSmsHandler发出了一个EVENT_NEW_SMS的广播。根据phone应用分析的callTacker的分析经验,这种Event消息是在Handler的HandleMessage()中去处理。查找整个CdmaInboundSmsHandler及其父类InboundSmsHandler发现并没用重载handleMessage()方法。倒是发现InboundSmsHandler继承了StateMachine。关于stateMachine在之前的netd中有接触过,其具体内容将在以后分析。以下是摘自网络的一点内容:
StateMachine是一个层次状态机(hierarchical state machine):一个状态可以有多个子状态的状态机。状态机中的状态须继承基类State,并实现成员函数processMessage,从而对收到的 Message进行处理;其它可选实现的成员函数为enter()、exit()和getName()。函数enter和exit相当于面向对象编程里的 “状态”的构造和析构函数。函数getName用于返回状态的名称,多用于调试目的。
状态机有多少子状态,可在构建状态机时,使用addState(State state, State parent)来添加所有的子状态,构建出一个层次状态关系。初始状态可由setInitialState函数指定。 使用者应调用StateMachine的start函数让状态机进入工作状态:初始化状态堆栈,调用初始状态(包括其父状态)的enter函数等。
其实说白了就是状态机会根据所处的不同状态调用不同的“HandleMessage()”(这里只是为了更好的说明,其实准确的名字应该是processMessage(),其中的转换一想就明了了),并且状态机的各种状态之间是可以相互转换的。
这里InboundSmsHandler继承了StateMachine,并且实现了以下五种状态。
/** * This parent state throws an exception (for debug builds) or prints an error for unhandled * message types. */ //这种状态在运行时基本上不会存在 class DefaultState extends State { @Override public boolean processMessage(Message msg) { switch (msg.what) { case EVENT_UPDATE_PHONE_OBJECT: { onUpdatePhoneObject((PhoneBase) msg.obj); break; } default: { String errorText = "processMessage: unhandled message type " + msg.what + " currState=" + getCurrentState().getName(); if (Build.IS_DEBUGGABLE) { loge("---- Dumping InboundSmsHandler ----"); loge("Total records=" + getLogRecCount()); for (int i = Math.max(getLogRecSize() - 20, 0); i < getLogRecSize(); i++) { loge("Rec[%d]: %s\n" + i + getLogRec(i).toString()); } loge("---- Dumped InboundSmsHandler ----");
throw new RuntimeException(errorText); } else { loge(errorText); } break; } } return HANDLED; } } |
开启过程中的状态
/** * The Startup state waits for {@link SmsBroadcastUndelivered} to process the raw table and * notify the state machine to broadcast any complete PDUs that might not have been broadcast. */ class StartupState extends State { @Override public boolean processMessage(Message msg) { log("StartupState.processMessage:" + msg.what); switch (msg.what) { case EVENT_NEW_SMS: case EVENT_INJECT_SMS: case EVENT_BROADCAST_SMS: deferMessage(msg);//这里其实就是延迟处理的意思 return HANDLED;
case EVENT_START_ACCEPTING_SMS://开始接受短信,便是准备好 transitionTo(mIdleState);//切换到idle状态,时刻准备着 return HANDLED;
case EVENT_BROADCAST_COMPLETE: case EVENT_RETURN_TO_IDLE: case EVENT_RELEASE_WAKELOCK: default: // let DefaultState handle these unexpected message types return NOT_HANDLED; } } } |
接着是Idle状态,在没有短信的时候处于这个状态。
/** * In the idle state the wakelock is released until a new SM arrives, then we transition * to Delivering mode to handle it, acquiring the wakelock on exit. */ class IdleState extends State { @Override public void enter() { if (DBG) log("entering Idle state"); sendMessageDelayed(EVENT_RELEASE_WAKELOCK, WAKELOCK_TIMEOUT); }
@Override public void exit() { mWakeLock.acquire(); if (DBG) log("acquired wakelock, leaving Idle state"); }
@Override public boolean processMessage(Message msg) { log("IdleState.processMessage:" + msg.what); if (DBG) log("Idle state processing message type " + msg.what); switch (msg.what) { case EVENT_NEW_SMS://新短信来了,注意这里没有break,意味着什么你懂的 case EVENT_INJECT_SMS: case EVENT_BROADCAST_SMS: deferMessage(msg);//延迟处理 transitionTo(mDeliveringState);//将状态切换到投递状态 return HANDLED;
case EVENT_RELEASE_WAKELOCK: mWakeLock.release(); if (DBG) { if (mWakeLock.isHeld()) { // this is okay as long as we call release() for every acquire() log("mWakeLock is still held after release"); } else { log("mWakeLock released"); } } return HANDLED;
case EVENT_RETURN_TO_IDLE: // already in idle state; ignore return HANDLED;
case EVENT_BROADCAST_COMPLETE: case EVENT_START_ACCEPTING_SMS: default: // let DefaultState handle these unexpected message types return NOT_HANDLED; } } } |
接着是投递状态,短信投递的核心工作都是在此状态下完成的。
/** * In the delivering state, the inbound SMS is processed and stored in the raw table. * The message is acknowledged before we exit this state. If there is a message to broadcast, * transition to {@link WaitingState} state to send the ordered broadcast and wait for the * results. When all messages have been processed, the halting state will release the wakelock. */ 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);//委托此方法做具体处理 //投递完毕,返回Idle状态,注意这里的方式并不是直接调用transitionTo,//因为在handleNewSms()过程中状态可能已经发生变化 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; // handleNewSm()会进入到这里作真正的核心处理 case EVENT_BROADCAST_SMS://开始想App发送广播 // if any broadcasts were sent, transition to waiting state InboundSmsTracker inboundSmsTracker = (InboundSmsTracker) msg.obj; if (processMessagePart(inboundSmsTracker)) { transitionTo(mWaitingState); } else { // if event is sent from SmsBroadcastUndelivered.broadcastSms(), and // processMessagePart() returns false, the state machine will be stuck in // DeliveringState until next message is received. Send message to // transition to idle to avoid that so that wakelock can be released log("No broadcast sent on processing EVENT_BROADCAST_SMS in Delivering " + "state. Return to Idle state"); sendMessage(EVENT_RETURN_TO_IDLE); } 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; } } } |
WaitingState状态其实指明已经有短信正在广播,此时来一条短信,当然需要等到第一条短信广播完毕才能继续发送广播了
/** * The waiting state delegates handling of new SMS to parent {@link DeliveringState}, but * defers handling of the {@link #EVENT_BROADCAST_SMS} phase until after the current * result receiver sends {@link #EVENT_BROADCAST_COMPLETE}. Before transitioning to * {@link DeliveringState}, {@link #EVENT_RETURN_TO_IDLE} is sent to transition to * {@link IdleState} after any deferred {@link #EVENT_BROADCAST_SMS} messages are handled. */ class WaitingState extends State { @Override public boolean processMessage(Message msg) { log("WaitingState.processMessage:" + msg.what); switch (msg.what) { case EVENT_BROADCAST_SMS://广播过程中 // defer until the current broadcast completes deferMessage(msg);//已有短信正在广播,新短信延迟发送, return HANDLED;
case EVENT_BROADCAST_COMPLETE://广播完毕 // return to idle after handling all deferred messages sendMessage(EVENT_RETURN_TO_IDLE);//如果还有短信在广播,这里空处理,注意这里的处理是根据状态来的 transitionTo(mDeliveringState);//继续发送 return HANDLED;
case EVENT_RETURN_TO_IDLE://这里可以解释上面空处理 // not ready to return to idle; ignore return HANDLED;
default: // parent state handles the other message types return NOT_HANDLED; } } } |
至此,RILJ到StateMachine在广播给上层App的流程大概如下图所示:
当然,还有几个细节需要具体讨论:1,短信消息是如何广播出去的?2,到底是谁接收了这个广播?
根据上一节的分析,DeliveringState状态下调用了handleNewSms((AsyncResult)msg.obj);对新来短信做了处理,这节就从这里展开。首先来看handleNewSms()。
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);//dispatch短信 } 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); } } |
可以看到首先是解析出了SmsMessage,然后dispatch
public int dispatchMessage(SmsMessageBase smsb) { // If sms is null, there was a parsing error. if (smsb == null) { loge("dispatchSmsMessage: message is null"); return Intents.RESULT_SMS_GENERIC_ERROR; }
if (mSmsReceiveDisabled) { // Device doesn't support receiving SMS, log("Received short message on device which doesn't support " + "receiving SMS. Ignored."); return Intents.RESULT_SMS_HANDLED; }
return dispatchMessageRadioSpecific(smsb);//这里 } |
dispatchMessageRadioSpecific(smsb)其实是过滤一些特殊的短信,对于一些特殊的短信走的是特殊的处理,非特殊短信则直接放行让后面的函数处理。dispatchMessageRadioSpecific为抽象方法,在子类中实现。
/** * Process Cell Broadcast, Voicemail Notification, and other 3GPP/3GPP2-specific messages. * @param smsb the SmsMessageBase object from the RIL * @return true if the message was handled here; false to continue processing */ @Override protected int dispatchMessageRadioSpecific(SmsMessageBase smsb) { if (isInEmergencyCallMode()) { return Activity.RESULT_OK; }
SmsMessage sms = (SmsMessage) smsb; boolean isBroadcastType = (SmsEnvelope.MESSAGE_TYPE_BROADCAST == sms.getMessageType());
// Handle CMAS emergency broadcast messages. if (isBroadcastType) { log("Broadcast type message"); SmsCbMessage cbMessage = sms.parseBroadcastSms(); if (cbMessage != null) { mCellBroadcastHandler.dispatchSmsMessage(cbMessage); } else { loge("error trying to parse broadcast SMS"); } return Intents.RESULT_SMS_HANDLED; }
// Initialize fingerprint field, and see if we have a network duplicate SMS. mLastDispatchedSmsFingerprint = sms.getIncomingSmsFingerprint(); if (mLastAcknowledgedSmsFingerprint != null && Arrays.equals(mLastDispatchedSmsFingerprint, mLastAcknowledgedSmsFingerprint)) { return Intents.RESULT_SMS_HANDLED; }
// Decode BD stream and set sms variables. sms.parseSms(); int teleService = sms.getTeleService();
switch (teleService) { case SmsEnvelope.TELESERVICE_VMN: case SmsEnvelope.TELESERVICE_MWI: // handle voicemail indication handleVoicemailTeleservice(sms); return Intents.RESULT_SMS_HANDLED;
case SmsEnvelope.TELESERVICE_WMT: case SmsEnvelope.TELESERVICE_WEMT: if (sms.isStatusReportMessage()) { mSmsDispatcher.sendStatusReportMessage(sms); return Intents.RESULT_SMS_HANDLED; } break;
case SmsEnvelope.TELESERVICE_SCPT: mServiceCategoryProgramHandler.dispatchSmsMessage(sms); return Intents.RESULT_SMS_HANDLED;
case SmsEnvelope.TELESERVICE_WAP: // handled below, after storage check break;
default: loge("unsupported teleservice 0x" + Integer.toHexString(teleService)); return Intents.RESULT_SMS_UNSUPPORTED; }
if (!mStorageMonitor.isStorageAvailable() && sms.getMessageClass() != SmsConstants.MessageClass.CLASS_0) { // It's a storable message and there's no storage available. Bail. // (See C.S0015-B v2.0 for a description of "Immediate Display" // messages, which we represent as CLASS_0.) return Intents.RESULT_SMS_OUT_OF_MEMORY; }
if (SmsEnvelope.TELESERVICE_WAP == teleService) { return processCdmaWapPdu(sms.getUserData(), sms.mMessageRef, sms.getOriginatingAddress(), sms.getTimestampMillis()); }
return dispatchNormalMessage(smsb);//非特殊消息,直接放行给normal方法处理 } |
接着是普通短信的处理方法:
/** * Dispatch a normal incoming SMS. This is called from {@link #dispatchMessageRadioSpecific} * if no format-specific handling was required. Saves the PDU to the SMS provider raw table, * creates an {@link InboundSmsTracker}, then sends it to the state machine as an * {@link #EVENT_BROADCAST_SMS}. Returns {@link Intents#RESULT_SMS_HANDLED} or an error value. * * @param sms the message to dispatch * @return {@link Intents#RESULT_SMS_HANDLED} if the message was accepted, or an error status */ protected int dispatchNormalMessage(SmsMessageBase sms) { SmsHeader smsHeader = sms.getUserDataHeader(); InboundSmsTracker tracker;
if ((smsHeader == null) || (smsHeader.concatRef == null)) { // Message is not concatenated. int destPort = -1; if (smsHeader != null && smsHeader.portAddrs != null) { // The message was sent to a port. destPort = smsHeader.portAddrs.destPort; if (DBG) log("destination port: " + destPort); }
tracker = new InboundSmsTracker(sms.getPdu(), sms.getTimestampMillis(), destPort, is3gpp2(), false); } else { // Create a tracker for this message segment. SmsHeader.ConcatRef concatRef = smsHeader.concatRef; SmsHeader.PortAddrs portAddrs = smsHeader.portAddrs; int destPort = (portAddrs != null ? portAddrs.destPort : -1); //根据新来短信新建一个InboundSmsTacker tracker = new InboundSmsTracker(sms.getPdu(), sms.getTimestampMillis(), destPort, is3gpp2(), sms.getOriginatingAddress(), concatRef.refNumber, concatRef.seqNumber, concatRef.msgCount, false); }
if (VDBG) log("created tracker: " + tracker); return addTrackerToRawTableAndSendMessage(tracker);//处理tracker } |
接着进入addTrackerToRawTableAndSendMessage(tracker);
/** * Helper to add the tracker to the raw table and then send a message to broadcast it, if * successful. Returns the SMS intent status to return to the SMSC. * @param tracker the tracker to save to the raw table and then deliver * @return {@link Intents#RESULT_SMS_HANDLED} or {@link Intents#RESULT_SMS_GENERIC_ERROR} * or {@link Intents#RESULT_SMS_DUPLICATED} */ protected int addTrackerToRawTableAndSendMessage(InboundSmsTracker tracker) { switch(addTrackerToRawTable(tracker)) {//别忽略了这个方法① case Intents.RESULT_SMS_HANDLED: //这里会回到状态机的proessMessage sendMessage(EVENT_BROADCAST_SMS, tracker);//② return Intents.RESULT_SMS_HANDLED;
case Intents.RESULT_SMS_DUPLICATED: return Intents.RESULT_SMS_HANDLED;
case Intents.RESULT_SMS_GENERIC_ERROR: default: return Intents.RESULT_SMS_GENERIC_ERROR; } } |
首先来看①,其实就是向短信息写入raw表,此表存放所有的短信息
private int addTrackerToRawTable(InboundSmsTracker tracker) { if (tracker.getMessageCount() != 1) { // check for duplicate message segments Cursor cursor = null; try { // sequence numbers are 1-based except for CDMA WAP, which is 0-based int sequence = tracker.getSequenceNumber();
// convert to strings for query String address = tracker.getAddress(); String refNumber = Integer.toString(tracker.getReferenceNumber()); String count = Integer.toString(tracker.getMessageCount());
String seqNumber = Integer.toString(sequence);
// set the delete selection args for multi-part message String[] deleteWhereArgs = {address, refNumber, count}; tracker.setDeleteWhere(SELECT_BY_REFERENCE, deleteWhereArgs);
// Check for duplicate message segments cursor = mResolver.query(sRawUri, PDU_PROJECTION, "address=? AND reference_number=? AND count=? AND sequence=?", new String[] {address, refNumber, count, seqNumber}, null);
// moveToNext() returns false if no duplicates were found if (cursor.moveToNext()) { loge("Discarding duplicate message segment, refNumber=" + refNumber + " seqNumber=" + seqNumber); String oldPduString = cursor.getString(PDU_COLUMN); byte[] pdu = tracker.getPdu(); byte[] oldPdu = HexDump.hexStringToByteArray(oldPduString); if (!Arrays.equals(oldPdu, tracker.getPdu())) { loge("Warning: dup message segment PDU of length " + pdu.length + " is different from existing PDU of length " + oldPdu.length); } return Intents.RESULT_SMS_DUPLICATED; // reject message } cursor.close(); } catch (SQLException e) { loge("Can't access multipart SMS database", e); return Intents.RESULT_SMS_GENERIC_ERROR; // reject message } finally { if (cursor != null) { cursor.close(); } } }
ContentValues values = tracker.getContentValues();
if (VDBG) log("adding content values to raw table: " + values.toString()); Uri newUri = mResolver.insert(sRawUri, values); if (DBG) log("URI of new row -> " + newUri);
try { long rowId = ContentUris.parseId(newUri); if (tracker.getMessageCount() == 1) { // set the delete selection args for single-part message tracker.setDeleteWhere(SELECT_BY_ID, new String[]{Long.toString(rowId)}); } return Intents.RESULT_SMS_HANDLED; } catch (Exception e) { loge("error parsing URI for new row: " + newUri, e); return Intents.RESULT_SMS_GENERIC_ERROR; } } |
接着分析②,首先需要明确的是此时的状态还是投递状态,因此,执行以下processMessage
@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 //首先是解出tracker InboundSmsTracker inboundSmsTracker = (InboundSmsTracker) msg.obj; if (processMessagePart(inboundSmsTracker)) { //处理中 transitionTo(mWaitingState); } else {//非处理状态 // if event is sent from SmsBroadcastUndelivered.broadcastSms(), and // processMessagePart() returns false, the state machine will be stuck in // DeliveringState until next message is received. Send message to // transition to idle to avoid that so that wakelock can be released log("No broadcast sent on processing EVENT_BROADCAST_SMS in Delivering " + "state. Return to Idle state"); sendMessage(EVENT_RETURN_TO_IDLE); } 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; } } |
进入processMessagePart(),此方法是核心的处理方法,主要将短信广播大到了App层
/** * 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;//需要将短信转化成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(); } } } //这里是一个广播receiver,接收系统广播,并将短信广播到第三方App SmsBroadcastReceiver 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 if (result == Activity.RESULT_OK) { return true; } else { deleteFromRawTable(tracker.getDeleteWhere(), tracker.getDeleteWhereArgs()); return false; } }
List UiccCard card = UiccController.getInstance().getUiccCard(mPhone.getPhoneId()); if (card != null) { carrierPackages = card.getCarrierPackageNamesForIntent( mContext.getPackageManager(), new Intent(CarrierMessagingService.SERVICE_INTERFACE)); } else { loge("UiccCard not initialized."); }
List getSystemAppForIntent(new Intent(CarrierMessagingService.SERVICE_INTERFACE));
if (carrierPackages != null && carrierPackages.size() == 1) { log("Found carrier package."); CarrierSmsFilter smsFilter = new CarrierSmsFilter(pdus, destPort, tracker.getFormat(), resultReceiver); CarrierSmsFilterCallback smsFilterCallback = new CarrierSmsFilterCallback(smsFilter); smsFilter.filterSms(carrierPackages.get(0), smsFilterCallback); } else if (systemPackages != null && systemPackages.size() == 1) { log("Found system package."); CarrierSmsFilter smsFilter = new CarrierSmsFilter(pdus, destPort, tracker.getFormat(), resultReceiver); CarrierSmsFilterCallback smsFilterCallback = new CarrierSmsFilterCallback(smsFilter); smsFilter.filterSms(systemPackages.get(0), smsFilterCallback); } else { logv("Unable to find carrier package: " + carrierPackages + ", nor systemPackages: " + systemPackages); dispatchSmsDeliveryIntent(pdus, tracker.getFormat(), destPort, resultReceiver);//这里发送个系统SMSAPP }
return true; } |
来看dispatchSmsDeliveryIntent()
/** * Creates and dispatches the intent to the default SMS app or the appropriate port. * * @param pdus message pdus * @param format the message format, typically "3gpp" or "3gpp2" * @param destPort the destination port * @param resultReceiver the receiver handling the delivery result */ void dispatchSmsDeliveryIntent(byte[][] pdus, String format, int destPort, BroadcastReceiver resultReceiver) { Intent intent = new Intent(); intent.putExtra("pdus", pdus); intent.putExtra("format", format);
if (destPort == -1) { intent.setAction(Intents.SMS_DELIVER_ACTION); // Direct the intent to only the default SMS app. If we can't find a default SMS app // then sent it to all broadcast receivers. // We are deliberately delivering to the primary user's default SMS App. ComponentName componentName = SmsApplication.getDefaultSmsApplication(mContext, true); if (componentName != null) { // Deliver SMS message only to this receiver. intent.setComponent(componentName); log("Delivering SMS to: " + componentName.getPackageName() + " " + componentName.getClassName()); } else { intent.setComponent(null); }
// TODO: Validate that this is the right place to store the SMS. 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()); } } } else { intent.setAction(Intents.DATA_SMS_RECEIVED_ACTION); Uri uri = Uri.parse("sms://localhost:" + destPort); intent.setData(uri); intent.setComponent(null); }
Bundle options = handleSmsWhitelisting(intent.getComponent()); //开始广播,内部调用了broadcaster方法。 dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS, AppOpsManager.OP_RECEIVE_SMS, options, resultReceiver, UserHandle.OWNER); } |
再来看之前的SmsBroadcastReceiverresultReceiver = new SmsBroadcastReceiver(tracker);
/** * 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();//新建一个action,在之后发送出去 if (action.equals(Intents.SMS_DELIVER_ACTION)) {//普通短信这里 // Now dispatch the notification only intent intent.setAction(Intents.SMS_RECEIVED_ACTION);//设置Action intent.setComponent(null); // All running users will be notified of the received sms. Bundle options = handleSmsWhitelisting(null); //开始广播 dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS, AppOpsManager.OP_RECEIVE_SMS, options, 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. Bundle options = null; try { long duration = mDeviceIdleController.addPowerSaveTempWhitelistAppForMms( mContext.getPackageName(), 0, "mms-broadcast"); BroadcastOptions bopts = BroadcastOptions.makeBasic(); bopts.setTemporaryAppWhitelistDuration(duration); options = bopts.toBundle(); } catch (RemoteException e) { } dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS, AppOpsManager.OP_RECEIVE_SMS, options, 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"); } } } } |
至此,短信广播被发送给了系统SmsApp和第三方App,第三方App好理解,只要在其应用配置文件中配置监听此广播就行了,这里想知道的是系统SMS怎么处理的。
首先需要指出的是本文所说的“系统SmsApp”只是短信接收后触发通知管理。本人查阅资料貌似说接收短信的有一个默认的App(可设置),这个App会第一时间接收到这个广播并且有最高的短信权限。而其他app则权限较低。这里的系统SMSApp是在packages\apps\BasicSmsReceiver,其配置文件配置监听了如下android.provider.Telephony.SMS_RECEIVED
package="com.android.basicsmsreceiver"> android:label="@string/sms_app_name" android:hardwareAccelerated="true"> android:theme="@android:style/Theme.Material.Light.Dialog" android:launchMode="singleTop" /> |
而在Telephony.java中有如下代码
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final StringSMS_RECEIVED_ACTION =
"android.provider.Telephony.SMS_RECEIVED";
对比从状态机中发出的广播SMS_RECEIVED_ACTION,因此可以确定短信广播就是被此App接收了,进入到其onReceive中。
private void addNotification(Context context, String fromAddress, String message) { int notificationId = BasicSmsReceiverApp.getBasicSmsReceiverApp().getNextNotificationId(); //新建一个notification Notification.Builder notification = new Notification.Builder(context) .setTicker(message) .setWhen(System.currentTimeMillis()) .setContentTitle(fromAddress) .setContentText(message) .setSmallIcon(R.drawable.stat_notify_sms) .setContentIntent(createDisplayMessageIntent(context, fromAddress, message, notificationId));
Log.i(LOG_TAG, "addNotification notificationId: " + notificationId); //获取通知管理器服务 NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE); //开始通知 notificationManager.notify(notificationId, notification.getNotification()); } |
当然这里可能很多地方会接收到此条通知,但最重要的肯定是UI,这里直接在同一个App文件中的DialogSmsDisplay.java中。
@Override protected void onNewIntent(Intent intent) { removeDialog(DIALOG_SHOW_MESSAGE);
parseIntent(intent); } |
进入到parseIntent()。
private void parseIntent(Intent intent) { if (intent == null) { return; } Bundle extras = intent.getExtras(); if (extras == null) { return; } //解析户短信的具体内容 mFromAddress = extras.getString(SMS_FROM_ADDRESS_EXTRA); mMessage = extras.getString(SMS_MESSAGE_EXTRA); int notificationId = extras.getInt(SMS_NOTIFICATION_ID_EXTRA);
Log.i(LOG_TAG, "notificationId: " + notificationId);
// Dismiss the notification that brought us here. NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.cancel(notificationId); //显示界面,其实解释显示短信的有关信息 showDialog(DIALOG_SHOW_MESSAGE); } |
至此系统UI显示出来了。