当前位置:   article > 正文

关于android 4.4短信(sms)接收流程-状态机篇_sms activity

sms activity

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状态的源码:

  1.  class StartupState extends State {
  2.         @Override
  3.         public boolean processMessage(Message msg) {
  4.             switch (msg.what) {
  5.                 case EVENT_NEW_SMS:
  6.                 case EVENT_BROADCAST_SMS:
  7.                     deferMessage(msg);
  8.                     return HANDLED;
  9.                 case EVENT_START_ACCEPTING_SMS:
  10.                     transitionTo(mIdleState);
  11.                     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。我们接着看

  1. class IdleState extends State {
  2. @Override
  3. public void enter() {
  4. if (DBG) log("entering Idle state");
  5. sendMessageDelayed(EVENT_RELEASE_WAKELOCK, WAKELOCK_TIMEOUT);
  6. }
  7. @Override
  8. public void exit() {
  9. mWakeLock.acquire();
  10. if (DBG) log("acquired wakelock, leaving Idle state");
  11. }
  12. @Override
  13. public boolean processMessage(Message msg) {
  14. if (DBG) log("Idle state processing message type " + msg.what);
  15. switch (msg.what) {
  16. case EVENT_NEW_SMS:
  17. case EVENT_BROADCAST_SMS:
  18. deferMessage(msg);
  19. transitionTo(mDeliveringState);
  20. return HANDLED;
在Idle状态收到后event_new_sms后,接着将状态切到DeliveringState.

  1. class DeliveringState extends State {
  2. @Override
  3. public void enter() {
  4. if (DBG) log("entering Delivering state");
  5. }
  6. @Override
  7. public void exit() {
  8. if (DBG) log("leaving Delivering state");
  9. }
  10. @Override
  11. public boolean processMessage(Message msg) {
  12. switch (msg.what) {
  13. case EVENT_NEW_SMS:
  14. // handle new SMS from RIL
  15. handleNewSms((AsyncResult) msg.obj);
  16. sendMessage(EVENT_RETURN_TO_IDLE);
  17. return HANDLED;

在DeliveringState状态下,终于看到处理短信的方法了:handleNewSms:


  1. void handleNewSms(AsyncResult ar) {
  2. if (ar.exception != null) {
  3. loge("Exception processing incoming SMS: " + ar.exception);
  4. return;
  5. }
  6. int result;
  7. try {
  8. SmsMessage sms = (SmsMessage) ar.result;//首先把短信读出来
  9. result = dispatchMessage(sms.mWrappedSmsMessage);调用<span style="font-family: Arial, Helvetica, sans-serif;">dispatchMessage</span>
  10. } catch (RuntimeException ex) {
  11. loge("Exception dispatching message", ex);
  12. result = Intents.RESULT_SMS_GENERIC_ERROR;
  13. }
  14. // RESULT_OK means that the SMS will be acknowledged by special handling,
  15. // e.g. for SMS-PP data download. Any other result, we should ack here.
  16. if (result != Activity.RESULT_OK) {
  17. boolean handled = (result == Intents.RESULT_SMS_HANDLED);
  18. notifyAndAcknowledgeLastIncomingSms(handled, result, null);
  19. }
  20. }

  1. public int dispatchMessage(SmsMessageBase smsb) {
  2. // If sms is null, there was a parsing error.
  3. if (smsb == null) {
  4. loge("dispatchSmsMessage: message is null");
  5. return Intents.RESULT_SMS_GENERIC_ERROR;
  6. }
  7. if (mSmsReceiveDisabled) {
  8. // Device doesn't support receiving SMS,
  9. log("Received short message on device which doesn't support "
  10. + "receiving SMS. Ignored.");
  11. return Intents.RESULT_SMS_HANDLED;
  12. }
  13. return dispatchMessageRadioSpecific(smsb);
  14. }

我们接着看dispatchMessageRadioSpecific:

注意,这个方法的实现在GsmInboundSmsHandler,不在是它的父类。

  1. protected int dispatchMessageRadioSpecific(SmsMessageBase smsb) {
  2. SmsMessage sms = (SmsMessage) smsb;
  3. if (sms.isTypeZero()) {//判断这个是不是typezero类型的短信。关于typezero。可以看规范<span style="font-family: Arial, Helvetica, sans-serif;">3GPP TS 23.040 ,进3gpp官网下载该规范即可</span>
  4. // As per 3GPP TS 23.040 9.2.3.9, Type Zero messages should not be
  5. // Displayed/Stored/Notified. They should only be acknowledged.
  6. log("Received short message type 0, Don't display or store it. Send Ack");
  7. return Intents.RESULT_SMS_HANDLED;
  8. }
  9. // Send SMS-PP data download messages to UICC. See 3GPP TS 31.111 section 7.1.1.
  10. if (sms.isUsimDataDownload()) {
  11. UsimServiceTable ust = mPhone.getUsimServiceTable();
  12. return mDataDownloadHandler.handleUsimDataDownload(ust, sms);
  13. }
  14. boolean handled = false;
  15. if (sms.isMWISetMessage()) {//这里wmi,有关语音信箱的短信。
  16. mPhone.setVoiceMessageWaiting(1, -1); // line 1: unknown number of msgs waiting
  17. handled = sms.isMwiDontStore();
  18. if (DBG) log("Received voice mail indicator set SMS shouldStore=" + !handled);
  19. } else if (sms.isMWIClearMessage()) {
  20. mPhone.setVoiceMessageWaiting(1, 0); // line 1: no msgs waiting
  21. handled = sms.isMwiDontStore();
  22. if (DBG) log("Received voice mail indicator clear SMS shouldStore=" + !handled);
  23. }
  24. if (handled) {
  25. return Intents.RESULT_SMS_HANDLED;
  26. }
  27. if (!mStorageMonitor.isStorageAvailable() &&
  28. sms.getMessageClass() != SmsConstants.MessageClass.CLASS_0) {
  29. // It's a storable message and there's no storage available. Bail.
  30. // (See TS 23.038 for a description of class 0 messages.)
  31. return Intents.RESULT_SMS_OUT_OF_MEMORY;
  32. }
  33. return dispatchNormalMessage(smsb);//看这里
  34. }


  1. protected int dispatchNormalMessage(SmsMessageBase sms) {
  2. SmsHeader smsHeader = sms.getUserDataHeader();
  3. InboundSmsTracker tracker;
  4. if ((smsHeader == null) || (smsHeader.concatRef == null)) {
  5. // Message is not concatenated.
  6. int destPort = -1;
  7. if (smsHeader != null && smsHeader.portAddrs != null) {
  8. // The message was sent to a port.
  9. destPort = smsHeader.portAddrs.destPort;
  10. if (DBG) log("destination port: " + destPort);
  11. }
  12. tracker = new InboundSmsTracker(sms.getPdu(), sms.getTimestampMillis(), destPort,
  13. is3gpp2(), false);
  14. } else {
  15. // Create a tracker for this message segment.
  16. SmsHeader.ConcatRef concatRef = smsHeader.concatRef;
  17. SmsHeader.PortAddrs portAddrs = smsHeader.portAddrs;
  18. int destPort = (portAddrs != null ? portAddrs.destPort : -1);
  19. tracker = new InboundSmsTracker(sms.getPdu(), sms.getTimestampMillis(), destPort,
  20. is3gpp2(), sms.getOriginatingAddress(), concatRef.refNumber,
  21. concatRef.seqNumber, concatRef.msgCount, false);
  22. }
  23. if (VDBG) log("created tracker: " + tracker);
  24. return addTrackerToRawTableAndSendMessage(tracker);
  25. }
InboundSmsTracker终于出场了。这里不多说,就是把短信封装到InboundSmsTracker。接着看addTrackerToRawTableAndSendMessage:

  1. protected int addTrackerToRawTableAndSendMessage(InboundSmsTracker tracker) {
  2. switch(addTrackerToRawTable(tracker)) {
  3. case Intents.RESULT_SMS_HANDLED:
  4. sendMessage(EVENT_BROADCAST_SMS, tracker);
  5. return Intents.RESULT_SMS_HANDLED;
  6. case Intents.RESULT_SMS_DUPLICATED:
  7. return Intents.RESULT_SMS_HANDLED;
  8. case Intents.RESULT_SMS_GENERIC_ERROR:
  9. default:
  10. return Intents.RESULT_SMS_GENERIC_ERROR;
  11. }
  12. }

这里有个很重要的方法,千万别漏掉addTrackerToRawTable。它是把短信放入raw表里。什么是raw表,打开短信数据库看一下就懂了。

  1. private int addTrackerToRawTable(InboundSmsTracker tracker) {
  2. if (tracker.getMessageCount() != 1) {
  3. // check for duplicate message segments
  4. Cursor cursor = null;
  5. try {
  6. // sequence numbers are 1-based except for CDMA WAP, which is 0-based
  7. int sequence = tracker.getSequenceNumber();
  8. // convert to strings for query
  9. String address = tracker.getAddress();
  10. String refNumber = Integer.toString(tracker.getReferenceNumber());
  11. String count = Integer.toString(tracker.getMessageCount());
  12. String seqNumber = Integer.toString(sequence);
  13. // set the delete selection args for multi-part message
  14. String[] deleteWhereArgs = {address, refNumber, count};
  15. tracker.setDeleteWhere(SELECT_BY_REFERENCE, deleteWhereArgs);
  16. // Check for duplicate message segments
  17. cursor = mResolver.query(sRawUri, PDU_PROJECTION,
  18. "address=? AND reference_number=? AND count=? AND sequence=?",
  19. new String[] {address, refNumber, count, seqNumber}, null);
  20. // moveToNext() returns false if no duplicates were found
  21. if (cursor.moveToNext()) {
  22. loge("Discarding duplicate message segment, refNumber=" + refNumber
  23. + " seqNumber=" + seqNumber);
  24. String oldPduString = cursor.getString(PDU_COLUMN);
  25. byte[] pdu = tracker.getPdu();
  26. byte[] oldPdu = HexDump.hexStringToByteArray(oldPduString);
  27. if (!Arrays.equals(oldPdu, tracker.getPdu())) {
  28. loge("Warning: dup message segment PDU of length " + pdu.length
  29. + " is different from existing PDU of length " + oldPdu.length);
  30. }
  31. return Intents.RESULT_SMS_DUPLICATED; // reject message
  32. }
  33. cursor.close();
  34. } catch (SQLException e) {
  35. loge("Can't access multipart SMS database", e);
  36. return Intents.RESULT_SMS_GENERIC_ERROR; // reject message
  37. } finally {
  38. if (cursor != null) {
  39. cursor.close();
  40. }
  41. }
  42. }
  43. ContentValues values = tracker.getContentValues();
  44. if (VDBG) log("adding content values to raw table: " + values.toString());
  45. Uri newUri = mResolver.insert(sRawUri, values);//保存到raw表
  46. if (DBG) log("URI of new row -> " + newUri);
  47. try {
  48. long rowId = ContentUris.parseId(newUri);
  49. if (tracker.getMessageCount() == 1) {
  50. // set the delete selection args for single-part message
  51. tracker.setDeleteWhere(SELECT_BY_ID, new String[]{Long.toString(rowId)});
  52. }
  53. return Intents.RESULT_SMS_HANDLED;
  54. } catch (Exception e) {
  55. loge("error parsing URI for new row: " + newUri, e);
  56. return Intents.RESULT_SMS_GENERIC_ERROR;
  57. }
  58. }

现在回到上一个方法:

  1. case Intents.RESULT_SMS_HANDLED:
  2. sendMessage(EVENT_BROADCAST_SMS, tracker);
  3. return Intents.RESULT_SMS_HANDLED;
这里是发出了EVENT_BROADCAST_SMS.

特别注意,刚才我们的状态是DeliveringState。所以,现在我们回到这个状态下看怎么处理这个event:

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

调用了processMessagePart,同时把状态切为mWaitingState

  1. boolean processMessagePart(InboundSmsTracker tracker) {
  2. int messageCount = tracker.getMessageCount();
  3. byte[][] pdus;
  4. int destPort = tracker.getDestPort();
  5. if (messageCount == 1) {
  6. // single-part message
  7. pdus = new byte[][]{tracker.getPdu()};
  8. } else {
  9. // multi-part message
  10. Cursor cursor = null;
  11. try {
  12. // used by several query selection arguments
  13. String address = tracker.getAddress();
  14. String refNumber = Integer.toString(tracker.getReferenceNumber());
  15. String count = Integer.toString(tracker.getMessageCount());
  16. // query for all segments and broadcast message if we have all the parts
  17. String[] whereArgs = {address, refNumber, count};
  18. cursor = mResolver.query(sRawUri, PDU_SEQUENCE_PORT_PROJECTION,
  19. SELECT_BY_REFERENCE, whereArgs, null);
  20. int cursorCount = cursor.getCount();
  21. if (cursorCount < messageCount) {
  22. // Wait for the other message parts to arrive. It's also possible for the last
  23. // segment to arrive before processing the EVENT_BROADCAST_SMS for one of the
  24. // earlier segments. In that case, the broadcast will be sent as soon as all
  25. // segments are in the table, and any later EVENT_BROADCAST_SMS messages will
  26. // get a row count of 0 and return.
  27. return false;
  28. }
  29. // All the parts are in place, deal with them
  30. pdus = new byte[messageCount][];
  31. while (cursor.moveToNext()) {
  32. // subtract offset to convert sequence to 0-based array index
  33. int index = cursor.getInt(SEQUENCE_COLUMN) - tracker.getIndexOffset();
  34. pdus[index] = HexDump.hexStringToByteArray(cursor.getString(PDU_COLUMN));
  35. // Read the destination port from the first segment (needed for CDMA WAP PDU).
  36. // It's not a bad idea to prefer the port from the first segment in other cases.
  37. if (index == 0 && !cursor.isNull(DESTINATION_PORT_COLUMN)) {
  38. int port = cursor.getInt(DESTINATION_PORT_COLUMN);
  39. // strip format flags and convert to real port number, or -1
  40. port = InboundSmsTracker.getRealDestPort(port);
  41. if (port != -1) {
  42. destPort = port;
  43. }
  44. }
  45. }
  46. } catch (SQLException e) {
  47. loge("Can't access multipart SMS database", e);
  48. return false;
  49. } finally {
  50. if (cursor != null) {
  51. cursor.close();
  52. }
  53. }
  54. }
  55. BroadcastReceiver resultReceiver = new SmsBroadcastReceiver(tracker);//创建了一个receiver,用于向app广播完后。做扫尾处理。
  56. if (destPort == SmsHeader.PORT_WAP_PUSH) {//判断这个短信是不是wap push.也就是端口短信。我们普通短信都不是wap push
  57. // Build up the data stream
  58. ByteArrayOutputStream output = new ByteArrayOutputStream();
  59. for (byte[] pdu : pdus) {
  60. // 3GPP needs to extract the User Data from the PDU; 3GPP2 has already done this
  61. if (!tracker.is3gpp2()) {
  62. SmsMessage msg = SmsMessage.createFromPdu(pdu, SmsConstants.FORMAT_3GPP);
  63. pdu = msg.getUserData();
  64. }
  65. output.write(pdu, 0, pdu.length);
  66. }
  67. int result = mWapPush.dispatchWapPdu(output.toByteArray(), resultReceiver, this);
  68. if (DBG) log("dispatchWapPdu() returned " + result);
  69. // result is Activity.RESULT_OK if an ordered broadcast was sent
  70. return (result == Activity.RESULT_OK);
  71. }
  72. Intent intent;
  73. if (destPort == -1) {
  74. intent = new Intent(Intents.SMS_DELIVER_ACTION);
  75. // Direct the intent to only the default SMS app. If we can't find a default SMS app
  76. // then sent it to all broadcast receivers.
  77. ComponentName componentName = SmsApplication.getDefaultSmsApplication(mContext, true);
  78. if (componentName != null) {
  79. // Deliver SMS message only to this receiver
  80. intent.setComponent(componentName);
  81. log("Delivering SMS to: " + componentName.getPackageName() +
  82. " " + componentName.getClassName());
  83. }
  84. } else {
  85. Uri uri = Uri.parse("sms://localhost:" + destPort);
  86. intent = new Intent(Intents.DATA_SMS_RECEIVED_ACTION, uri);
  87. }
  88. intent.putExtra("pdus", pdus);
  89. intent.putExtra("format", tracker.getFormat());
  90. dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
  91. AppOpsManager.OP_RECEIVE_SMS, resultReceiver);//将短信广播出去。注意,在4.4版本里,这里只是广播给系统默认的default sms app.其它sms app是收不到短信的。
  92. return true;
  93. }
这个方法发送的短信广播只是系统默认的default sms app.其它sms app是收不到短信的。那么其它接收短信的app.靠什么收到短信呢。别急,我们刚才有讲过

 BroadcastReceiver resultReceiver = new SmsBroadcastReceiver(tracker);//创建了一个receiver,用于向app广播完后。做扫尾处理。
我们来看一下这个receiver:

  1. private final class SmsBroadcastReceiver extends BroadcastReceiver {
  2. private final String mDeleteWhere;
  3. private final String[] mDeleteWhereArgs;
  4. private long mBroadcastTimeNano;
  5. SmsBroadcastReceiver(InboundSmsTracker tracker) {
  6. mDeleteWhere = tracker.getDeleteWhere();
  7. mDeleteWhereArgs = tracker.getDeleteWhereArgs();
  8. mBroadcastTimeNano = System.nanoTime();
  9. }
  10. @Override
  11. public void onReceive(Context context, Intent intent) {
  12. String action = intent.getAction();
  13. if (action.equals(Intents.SMS_DELIVER_ACTION)) {
  14. // Now dispatch the notification only intent
  15. intent.setAction(Intents.SMS_RECEIVED_ACTION);
  16. intent.setComponent(null);
  17. dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
  18. AppOpsManager.OP_RECEIVE_SMS, this);
  19. } else if
这里再次广播了一个intent.只要第三方app去接收这个intent。就能收到短信了。

接下来就是把状态机切回idle状态。这里不再描述。大家往下跟一下代码就清楚了。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/285884
推荐阅读
相关标签
  

闽ICP备14008679号