最近有客戶反饋Android接收不到簡訊,於是一頭扎進RIL裡面找原因。最後發現不是RIL的問題,而是BC72上報簡訊的格式不對,AT+CNMA=1無作用等幾個小問題導致的。儘管問題不在RIL,但總算把RIL簡訊接收流程搞清楚了。 接收到新信息的log: D/ATC ( 1269): AT< +CM ...
最近有客戶反饋Android接收不到簡訊,於是一頭扎進RIL裡面找原因。最後發現不是RIL的問題,而是BC72上報
簡訊的格式不對,AT+CNMA=1無作用等幾個小問題導致的。儘管問題不在RIL,但總算把RIL簡訊接收流程搞清楚了。
接收到新信息的log:
D/ATC ( 1269): AT< +CMT:,27
D/ATC ( 1268): AT< 0891683108705505F0040d91683117358313f500009101329154922307ea31da2c36a301
D/RILJ ( 1792): [UNSL]< UNSOL_RESPONSE_NEW_SMS
D/SmsMessage( 1792): SMS SC address: +8613800755500
V/SmsMessage( 1792): SMS originating address: +8613715338315
V/SmsMessage( 1792): SMS TP-PID:0 data coding scheme: 0
D/SmsMessage( 1792): SMS SC timestamp: 1571831129000
V/SmsMessage( 1792): SMS message body (raw): 'jchfbfh'
D/GsmInboundSmsHandler( 1776): Idle state processing message type 1
D/GsmInboundSmsHandler( 1776): acquired wakelock, leaving Idle state
D/GsmInboundSmsHandler( 1776): entering Delivering state
D/GsmInboundSmsHandler( 1776): URI of new row -> content://raw/3
D/RILJ ( 1775): [3706]> SMS_ACKNOWLEDGE true 0
D/RILC ( 1254): onRequest: SMS_ACKNOWLEDGE
D/ATC ( 1254): AT> AT+CNMA=1
D/ATC ( 1254): AT< OK
D/RILJ ( 1775): [3706]< SMS_ACKNOWLEDGE
D/GsmInboundSmsHandler( 1775): Delivering SMS to: com.android.mms com.android.mms.transaction.PrivilegedSmsReceiver
E/GsmInboundSmsHandler( 1775): unexpected BroadcastReceiver action: android.provider.Telephony.SMS_RECEIVED
D/GsmInboundSmsHandler( 1775): successful broadcast, deleting from raw table.
D/SmsMessage( 2124): SMS SC address: +8613800755500
D/GsmInboundSmsHandler( 1775): Deleted 1 rows from raw table.
D/GsmInboundSmsHandler( 1775): ordered broadcast completed in: 276 ms
D/GsmInboundSmsHandler( 1775): leaving Delivering state
D/GsmInboundSmsHandler( 1775): entering Delivering state
D/GsmInboundSmsHandler( 1775): leaving Delivering state
D/GsmInboundSmsHandler( 1775): entering Idle state
V/SmsMessage( 2124): SMS originating address: +8613715338315
V/SmsMessage( 2124): SMS TP-PID:0 data coding scheme: 0
D/SmsMessage( 2124): SMS SC timestamp: 1572253549000
V/SmsMessage( 2124): SMS message body (raw): 'jchfbfh'
D/GsmInboundSmsHandler( 1775): Idle state processing message type 5
D/GsmInboundSmsHandler( 1775): mWakeLock released
一、簡訊接收
1. vendor ril接收到modem上報的簡訊息
hardware/ril/reference-ril/reference-ril.c
static void onUnsolicited (const char *s, const char *sms_pdu) { ... ... if (strStartsWith(s, "+CMT:")) { RIL_onUnsolicitedResponse ( RIL_UNSOL_RESPONSE_NEW_SMS, /* 上報UNSOL_RESPONSE_NEW_SMS消息 */ sms_pdu, strlen(sms_pdu)); } ... ... }
2. RILD把簡訊息發送到RILJ
hardware/ril/libril/ril.cpp
extern "C" void RIL_onUnsolicitedResponse(int unsolResponse, void *data, size_t datalen) { ... ... unsolResponseIndex = unsolResponse - RIL_UNSOL_RESPONSE_BASE; /* 找出消息在s_unsolResponses[]的索引 */ ... ... switch (s_unsolResponses[unsolResponseIndex].wakeType) { /* 禁止進入休眠 */ case WAKE_PARTIAL: grabPartialWakeLock(); shouldScheduleTimeout = true; break; ... ... } ... ... ret = s_unsolResponses[unsolResponseIndex] /* 調用消息處理函數responseString() */ .responseFunction(p, data, datalen); ... ... ret = sendResponse(p); /* 發送Parcel中的信息內容到服務端RILJ */ } static UnsolResponseInfo s_unsolResponses[] = { ... ... /* 消息對應的消息處理函數,新信息到來會喚醒系統 */ {RIL_UNSOL_RESPONSE_NEW_SMS, responseString, WAKE_PARTIAL}, ... ... }; static int responseString(Parcel &p, void *response, size_t responselen) { /* one string only */ startResponse; appendPrintBuf("%s%s", printBuf, (char*)response); closeResponse; writeStringToParcel(p, (const char *)response); /* 把字元串格式的信息存到Parcel容器中 */ return 0; }
二、解析簡訊息
1. RILJ獲取簡訊息
frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java
private void processUnsolicited (Parcel p) { ... ... case RIL_UNSOL_RESPONSE_NEW_SMS: ret = responseString(p); break; ... ... switch(response) { ... ... case RIL_UNSOL_RESPONSE_NEW_SMS: { if (RILJ_LOGD) unsljLog(response); /* 參考log:[UNSL]< UNSOL_RESPONSE_NEW_SMS */ // FIXME this should move up a layer String a[] = new String[2]; a[1] = (String)ret; SmsMessage sms; sms = SmsMessage.newFromCMT(a); /* 解析PDU格式的簡訊息 */ if (mGsmSmsRegistrant != null) { mGsmSmsRegistrant .notifyRegistrant(new AsyncResult(null, sms, null)); } break; } ... ... } ... ... } private Object responseString(Parcel p) { String response; response = p.readString(); /* 信息內容轉換成Object */ return response; }
2. 解析簡訊息
SmsMessage.newFromCMT(a);根據import android.telephony.SmsMessage,得知代碼路徑:
frameworks/opt/telephony/src/java/android/telephony/SmsMessage.java
public static SmsMessage newFromCMT(String[] lines) { // received SMS in 3GPP format SmsMessageBase wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.newFromCMT(lines); /* 是對另一個newFromCMT的封裝,因為有gsm和cdma兩種簡訊, * 即cdma中也有newFromCMT,根據情況按需選擇 */ return new SmsMessage(wrappedMessage); }
com.android.internal.telephony.gsm.SmsMessage.newFromCMT(lines)的實現在
frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/SmsMessage.java
public class SmsMessage extends SmsMessageBase { ... ... public static SmsMessage newFromCMT(String[] lines) { try { SmsMessage msg = new SmsMessage(); msg.parsePdu(IccUtils.hexStringToBytes(lines[1])); /* 解析PDU簡訊 */ return msg; } catch (RuntimeException ex) { Rlog.e(LOG_TAG, "SMS PDU parsing failed: ", ex); return null; } } ... ... }
IccUtils.hexStringToBytes(lines[1])把十六進位的字元串轉換成位元組數組msg.parsePdu()解析這個數組的內容,最後獲得簡訊內容
frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/SmsMessage.java
private void parsePdu(byte[] pdu) { ... ... mScAddress = p.getSCAddress(); if (mScAddress != null) { if (VDBG) Rlog.d(LOG_TAG, "SMS SC address: " + mScAddress); /* 參考log:SMS SC address: +8613800755500 */ } ... ... mMti = firstByte & 0x3; switch (mMti) { ... ... case 3: //GSM 03.40 9.2.3.1: MTI == 3 is Reserved. //This should be processed in the same way as MTI == 0 (Deliver) parseSmsDeliver(p, firstByte); /* 對簡訊類型為Deliver的簡訊進行解析 */ break; ... ... } ... ... } private void parseSmsDeliver(PduParser p, int firstByte) { ... ... mOriginatingAddress = p.getAddress(); if (mOriginatingAddress != null) { if (VDBG) Rlog.v(LOG_TAG, "SMS originating address: " /* 參考log: SMS originating address: +861371533xxxx */ + mOriginatingAddress.address); } ... ... mProtocolIdentifier = p.getByte(); // TP-Data-Coding-Scheme // see TS 23.038 mDataCodingScheme = p.getByte(); if (VDBG) { Rlog.v(LOG_TAG, "SMS TP-PID:" + mProtocolIdentifier + " data coding scheme: " + mDataCodingScheme); /* 參考log: SMS TP-PID:0 data coding scheme: 0 */ } mScTimeMillis = p.getSCTimestampMillis(); if (VDBG) Rlog.d(LOG_TAG, "SMS SC timestamp: " + mScTimeMillis); /* 參考log:SMS SC timestamp: 1571831129000 */ boolean hasUserDataHeader = (firstByte & 0x40) == 0x40; parseUserData(p, hasUserDataHeader); /* 解析信息有效內容 */ ... ... } private void parseUserData(PduParser p, boolean hasUserDataHeader) { ... ... if (VDBG) Rlog.v(LOG_TAG, "SMS message body (raw): '" + mMessageBody + "'"); /* 簡訊內容,參考log: SMS message body (raw): 'jchfbfh' */ ... ... }
三、處理簡訊息
對用戶有效的簡訊內容,最終保存在類型為String的mMessageBody變數中,該變數屬於SmsMessageBase抽象類,而
SmsMessage繼承於SmsMessageBase。
回到前面frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java中processUnsolicited(),
sms = SmsMessage.newFromCMT(a);解析完簡訊息後,返回一個SmsMessage並通知上層應用。
frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java
mGsmSmsRegistrant .notifyRegistrant(new AsyncResult(null, sms, null)); /* 把sms轉成Object類型 */
frameworks/base/core/java/android/os/AsyncResult.java
public class AsyncResult { ... ... /** please note, this sets m.obj to be this */ public AsyncResult (Object uo, Object r, Throwable ex) { userObj = uo; result = r; exception = ex; } ... ... }
根據mGsmSmsRegistrant.notifyRegistrant(new AsyncResult(null, sms, null));找到mGsmSmsRegistrant註冊的代碼:
frameworks/opt/telephony/src/java/com/android/internal/telephony/BaseCommands.java
public abstract class BaseCommands implements CommandsInterface { ... ... @Override public void setOnNewGsmSms(Handler h, int what, Object obj) { /* mGsmSmsRegistrant.notifyRegistrant(new AsyncResult(null, sms, null))中的mGsmSmsRegistrant是在這裡創建的 */ mGsmSmsRegistrant = new Registrant (h, what, obj); } ... ... }
封裝消息EVENT_NEW_SMS消息
frameworks/base/core/java/android/os/Registrant.java
public class Registrant { public Registrant(Handler h, int what, Object obj) /* 傳入需要處理消息為what的事件處理Handler h,obj為事件內容,參考phone.mCi.setOnNewGsmSms(getHandler(), EVENT_NEW_SMS, null); */ { refH = new WeakReference(h); this.what = what; userObj = obj; } ... ... /** * This makes a copy of @param ar */ public void notifyRegistrant(AsyncResult ar) /* 參考mGsmSmsRegistrant.notifyRegistrant(new AsyncResult(null, sms, null)) */ { internalNotifyRegistrant (ar.result, ar.exception); /* ar.result為sms */ } /*package*/ void internalNotifyRegistrant (Object result, Throwable exception) /* internalNotifyRegistrant (sms, Throwable exception) */ { Handler h = getHandler(); if (h == null) { clear(); } else { Message msg = Message.obtain(); /* 創建一個消息 */ msg.what = what; /* 消息類型EVENT_NEW_SMS */ msg.obj = new AsyncResult(userObj, result, exception); /* 消息內容sms */ h.sendMessage(msg); /* 發送消息到註冊了這個消息的Handler,參考phone.mCi.setOnNewGsmSms(getHandler(), EVENT_NEW_SMS, null);的getHandler() */ } } ... ... }
然而BaseCommands是一個抽象類,實現了CommandsInterface中的setOnNewGsmSms介面,這個介面由GsmInboundSmsHandler調用
(phone.mCi.setOnNewGsmSms(getHandler(), EVENT_NEW_SMS, null)),也就是說GsmInboundSmsHandler的getHandler()是EVENT_NEW_SMS
的監聽者,也就是說frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java中mGsmSmsRegistrant.notifyRegistrant(new AsyncResult(null, sms, null))
調用之後,會觸發GsmInboundSmsHandler中getHandler()的Handler對EVENT_NEW_SMS消息進行解析。這個Handler肯定是GsmInboundSmsHandler
實例化的對象中的,這個對象在什麼時候,在哪裡創建的,暫且不管。我們只管EVENT_NEW_SMS這個消息從哪裡來,然後到哪裡去
就行了。
./frameworks/opt/telephony/src/java/com/android/internal/telephony/ImsSMSDispatcher.java
public final class ImsSMSDispatcher extends SMSDispatcher { ... ... mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(phone.getContext(), /* 獲取mGsmInboundSmsHandler,並啟動狀態機 */ storageMonitor, phone); ... ... }
./frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
public class GsmInboundSmsHandler extends InboundSmsHandler { ... ... /** * Create a new GSM inbound SMS handler. */ private GsmInboundSmsHandler(Context context, SmsStorageMonitor storageMonitor, PhoneBase phone) { super("GsmInboundSmsHandler", context, storageMonitor, phone, /* 構造GsmInboundSmsHandler時,通過super()調用InboundSmsHandler的構造函數 */ GsmCellBroadcastHandler.makeGsmCellBroadcastHandler(context, phone)); phone.mCi.setOnNewGsmSms(getHandler(), EVENT_NEW_SMS, null); /* 註冊EVENT_NEW_SMS消息 */ mDataDownloadHandler = new UsimDataDownloadHandler(phone.mCi); } ... ... /** * Wait for state machine to enter startup state. We can't send any messages until then. */ public static GsmInboundSmsHandler makeInboundSmsHandler(Context context, SmsStorageMonitor storageMonitor, PhoneBase phone) { GsmInboundSmsHandler handler = new GsmInboundSmsHandler(context, storageMonitor, phone); /* 實例化GsmInboundSmsHandler */ handler.start(); /* 抽象類InboundSmsHandler繼承與StateMachine,而GsmInboundSmsHandler繼承於InboundSmsHandler, * GsmInboundSmsHandler調用啟動狀態機方法start() */ return handler; } ... ... }
./frameworks/opt/telephony/src/java/com/android/internal/telephony/InboundSmsHandler.java
public abstract class InboundSmsHandler extends StateMachine { ... ... protected InboundSmsHandler(String name, Context context, SmsStorageMonitor storageMonitor, PhoneBase phone, CellBroadcastHandler cellBroadcastHandler) { ... ... addState(mDefaultState); /* 構造InboundSmsHandler時,添加狀態機的狀態 */ addState(mStartupState, mDefaultState); addState(mIdleState, mDefaultState); addState(mDeliveringState, mDefaultState); addState(mWaitingState, mDeliveringState); setInitialState(mStartupState); /* 初始化狀態機 */ if (DBG) log("created InboundSmsHandler"); } ... ... 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); /* 轉到mDeliveringState */ return HANDLED; ... ... } } } ... ... class DeliveringState extends State { /* 轉到mDeliveringState狀態 */ @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); /* 處理新SMS */ sendMessage(EVENT_RETURN_TO_IDLE); /* 處理完回到空閑狀態 */ return HANDLED; ... ... } } ... ... } } void handleNewSms(AsyncResult ar) { ... ... SmsMessage sms = (SmsMessage) ar.result; result = dispatchMessage(sms.mWrappedSmsMessage); ... ... } public int dispatchMessage(SmsMessageBase smsb) { ... ... return dispatchMessageRadioSpecific(smsb); ... ... }
通過以上流程可以瞭解到,當狀態機接收到SMS後,對消息進行分發,針對type zero, SMS-PP data download,
和3GPP/CPHS MWI type SMS判斷,如果是Normal SMS messages,則調用dispatchNormalMessage(smsb),然後創建
一個InboundSmsTracker對象,把信息保存到raw table,然後在通過sendMessage(EVENT_BROADCAST_SMS, tracker)
把消息廣播出去。
./frameworks/opt/telephony/src/java/com/android/internal/telephony/InboundSmsHandler.java
class DeliveringState extends State { ... ... public boolean processMessage(Message msg) { switch (msg.what) { ... ... case EVENT_BROADCAST_SMS: /* 接收到EVENT_BROADCAST_SMS消息並處理 */ // if any broadcasts were sent, transition to waiting state if (processMessagePart((InboundSmsTracker) msg.obj)) { transitionTo(mWaitingState); } return HANDLED; ... ... } } ... ... } boolean processMessagePart(InboundSmsTracker tracker) { ... ... BroadcastReceiver resultReceiver = new SmsBroadcastReceiver(tracker); /* 創建一個廣播接收者,用來處理簡訊廣播的結果 */ ... ... intent = new Intent(Intents.SMS_DELIVER_ACTION); /* 設置當前intent的action為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); /* 這個action只會發送給carrier app,而且carrier app可以通過set result為RESULT_CANCELED來終止這個廣播 */ if (componentName != null) { // Deliver SMS message only to this receiver intent.setComponent(componentName); log("Delivering SMS to: " + componentName.getPackageName() + " " + componentName.getClassName()); } ... ... dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS, /* 廣播intent */ AppOpsManager.OP_RECEIVE_SMS, resultReceiver); ... ... } private final class SmsBroadcastReceiver extends BroadcastReceiver { ... ... public void onReceive(Context context, Intent intent) { ... ... // Now that the intents have been deleted we can clean up the PDU data. if (!Intents.DATA_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); /* 成功廣播 */ ... ... } ... ... }
到這裡,在應用層註冊具有Intents.SMS_RECEIVED_ACTION這樣action的廣播,就可以獲取到簡訊了。