关于android 4.4短信(sms)接收流程-状态机篇
2015-05-23 19:53
579 查看
google从4.4版本开始,为了解决重复接收多条短信问题,在短信接收的框架层中增加了一个状态机专门用来接收短信。
首先什么是状态机,这里不多说,网上已经有很多相关的文章,这边引用一个:http://blog.csdn.net/pi9nc/article/details/27503071。如果想直接看源码了解的。可以看StateMachine.java (frameworks\base\core\java\com\android\internal\util)
这里我分几部分来讲短信接收过程中的状态机,
一,涉及到的源文件:
GsmInboundSmsHandler.java (frameworks\opt\telephony\src\java\com\android\internal\telephony\gsm)
InboundSmsHandler.java (frameworks\opt\telephony\src\java\com\android\internal\telephony)
InboundSmsTracker.java (frameworks\opt\telephony\src\java\com\android\internal\telephony)
针对于3gpp规范的短信,基本上就是以上两支文件。对于3gpp2规范的短信,文件会有所差异。
这里的CdmaInboundSmsHandler即为3gpp2规范短信的内容。这次不会涉及这个class.
二,短信接收流程,
1,在GsmInboundSmsHandler的构造方法里:
phone.mCi.setOnNewGsmSms(getHandler(), EVENT_NEW_SMS, null);这一句很关键,这是在告诉RILJ,当有新短信来的时候,RILJ会把短信发给GsmInboundSmsHandler,实际上是由其父类处理。也就是InboundSmsHandler.
2,我们看一下InboundSmsHandler到底是什么:
public abstract class InboundSmsHandler extends StateMachine {
这下清楚了。这就是接收短信的状态机。短信到达framework层后,首先由这个状态机接收。
我们再来看一上这个状态机长什么样:
当状态机初始化好后,会停留在Idle状态。当短信到来的时候,首先会由Idle状态接收到,之后就会按以下流程进行处理
这两张状态图怎么看呢,我们先来看startup状态的源码:
class StartupState extends State { @Override public boolean processMessage(Message msg) { switch (msg.what) { case EVENT_NEW_SMS: case EVENT_BROADCAST_SMS: deferMessage(msg); return HANDLED; case EVENT_START_ACCEPTING_SMS: transitionTo(mIdleState); return HANDLED;
当SmsBroadcastUndelivered处理好table后,会发出EVENT_START_ACCEPTING_SMS.这时候,会从StartupState状态切到Idle状态。只有在Idle状态,才可以处理短信。
回到前面,我们最初在GsmInboundSmsHandler的构造方法里:
phone.mCi.setOnNewGsmSms(getHandler(), EVENT_NEW_SMS, null);也就是说当RILJ有短信的时候,会向GsmInboundSmsHandler发送EVENT_NEW_SMS,
现在这里讲的就是在Idle状态下收到了EVENT_NEW_SMS。我们接着看
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) { if (DBG) log("Idle state processing message type " + msg.what); switch (msg.what) { case EVENT_NEW_SMS: case EVENT_BROADCAST_SMS: deferMessage(msg); transitionTo(mDeliveringState); return HANDLED;在Idle状态收到后event_new_sms后,接着将状态切到DeliveringState.
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) { switch (msg.what) { case EVENT_NEW_SMS: // handle new SMS from RIL handleNewSms((AsyncResult) msg.obj); sendMessage(EVENT_RETURN_TO_IDLE); return HANDLED;
在DeliveringState状态下,终于看到处理短信的方法了: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);调用<span style="font-family: Arial, Helvetica, sans-serif;">dispatchMessage</span> } 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); } }
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:
注意,这个方法的实现在GsmInboundSmsHandler,不在是它的父类。
protected int dispatchMessageRadioSpecific(SmsMessageBase smsb) { SmsMessage sms = (SmsMessage) smsb; if (sms.isTypeZero()) {//判断这个是不是typezero类型的短信。关于typezero。可以看规范<span style="font-family: Arial, Helvetica, sans-serif;">3GPP TS 23.040 ,进3gpp官网下载该规范即可</span> // As per 3GPP TS 23.040 9.2.3.9, Type Zero messages should not be // Displayed/Stored/Notified. They should only be acknowledged. log("Received short message type 0, Don't display or store it. Send Ack"); return Intents.RESULT_SMS_HANDLED; } // Send SMS-PP data download messages to UICC. See 3GPP TS 31.111 section 7.1.1. if (sms.isUsimDataDownload()) { UsimServiceTable ust = mPhone.getUsimServiceTable(); return mDataDownloadHandler.handleUsimDataDownload(ust, sms); } boolean handled = false; if (sms.isMWISetMessage()) {//这里wmi,有关语音信箱的短信。 mPhone.setVoiceMessageWaiting(1, -1); // line 1: unknown number of msgs waiting handled = sms.isMwiDontStore(); if (DBG) log("Received voice mail indicator set SMS shouldStore=" + !handled); } else if (sms.isMWIClearMessage()) { mPhone.setVoiceMessageWaiting(1, 0); // line 1: no msgs waiting handled = sms.isMwiDontStore(); if (DBG) log("Received voice mail indicator clear SMS shouldStore=" + !handled); } if (handled) { return Intents.RESULT_SMS_HANDLED; } if (!mStorageMonitor.isStorageAvailable() && sms.getMessageClass() != SmsConstants.MessageClass.CLASS_0) { // It's a storable message and there's no storage available. Bail. // (See TS 23.038 for a description of class 0 messages.) return Intents.RESULT_SMS_OUT_OF_MEMORY; } return dispatchNormalMessage(smsb);//看这里 }
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); 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); }InboundSmsTracker终于出场了。这里不多说,就是把短信封装到InboundSmsTracker。接着看addTrackerToRawTableAndSendMessage:
protected int addTrackerToRawTableAndSendMessage(InboundSmsTracker tracker) { switch(addTrackerToRawTable(tracker)) { case Intents.RESULT_SMS_HANDLED: 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; } }
这里有个很重要的方法,千万别漏掉addTrackerToRawTable。它是把短信放入raw表里。什么是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);//保存到raw表 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; } }
现在回到上一个方法:
case Intents.RESULT_SMS_HANDLED: sendMessage(EVENT_BROADCAST_SMS, tracker); return Intents.RESULT_SMS_HANDLED;这里是发出了EVENT_BROADCAST_SMS.
特别注意,刚才我们的状态是DeliveringState。所以,现在我们回到这个状态下看怎么处理这个event:
case EVENT_BROADCAST_SMS: // if any broadcasts were sent, transition to waiting state if (processMessagePart((InboundSmsTracker) msg.obj)) { transitionTo(mWaitingState); } return HANDLED;
调用了processMessagePart,同时把状态切为mWaitingState
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);//创建了一个receiver,用于向app广播完后。做扫尾处理。 if (destPort == SmsHeader.PORT_WAP_PUSH) {//判断这个短信是不是wap push.也就是端口短信。我们普通短信都不是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; if (destPort == -1) { intent = new Intent(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. 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 { Uri uri = Uri.parse("sms://localhost:" + destPort); intent = new Intent(Intents.DATA_SMS_RECEIVED_ACTION, uri); } intent.putExtra("pdus", pdus); intent.putExtra("format", tracker.getFormat()); dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS, AppOpsManager.OP_RECEIVE_SMS, resultReceiver);//将短信广播出去。注意,在4.4版本里,这里只是广播给系统默认的default sms app.其它sms app是收不到短信的。 return true; }这个方法发送的短信广播只是系统默认的default sms app.其它sms app是收不到短信的。那么其它接收短信的app.靠什么收到短信呢。别急,我们刚才有讲过
BroadcastReceiver resultReceiver = new SmsBroadcastReceiver(tracker);//创建了一个receiver,用于向app广播完后。做扫尾处理。我们来看一下这个receiver:
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_DELIVER_ACTION)) { // Now dispatch the notification only intent intent.setAction(Intents.SMS_RECEIVED_ACTION); intent.setComponent(null); dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS, AppOpsManager.OP_RECEIVE_SMS, this); } else if这里再次广播了一个intent.只要第三方app去接收这个intent。就能收到短信了。
接下来就是把状态机切回idle状态。这里不再描述。大家往下跟一下代码就清楚了。
相关文章推荐
- [Android][KK][SMS]Frameworks学习——接收短信流程分析
- android中Mms学习笔记——短信(sms)接收流程(三)
- 【SMS】android 短信接收流程分析——-拦截短信示例代码
- [Android][KK][SMS]Frameworks学习——发送短信流程分析
- android 接收短信流程
- Android Mms之短信接收流程--从Framework到App
- android 短信接收流程分析——为更好的拦截短信做准备
- Android系统中关于短信(SMS)的操作
- Android监听SMS发送状态并获取短信服务中心号码
- android接收短信(SmsMessage.createFromPdu((byte[])obj)不推荐使用的处理方法)
- 【Android】利用广播Broadcast接收SMS短信
- android 短信接收流程分析——为更好的拦截短信做准备
- android 4.4短信接收部分的变化
- android中Mms学习笔记——短信(sms)发送流程(二)
- Android 4.4 如何开发自己的短信 SMS App
- Android短信----接收流程---框架层(Frameworks)
- Android 4.4 Kitkat Phone工作流程浅析(九)__状态通知流程分析
- android接收短信——framework处理流程(android 5.1)
- Android 4.4 Kitkat Phone工作流程浅析(八)__Phone状态分析
- android短信接收流程