詳解Android4.4 RIL短信接收流程分析
最近有客戶反饋Android接收不到短信,于是一頭扎進(jìn)RIL里面找原因。最后發(fā)現(xiàn)不是RIL的問題,而是BC72上報
短信的格式不對,AT+CNMA=1無作用等幾個小問題導(dǎo)致的。盡管問題不在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把短信息發(fā)送到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) { /* 禁止進(jìn)入休眠 */
case WAKE_PARTIAL:
grabPartialWakeLock();
shouldScheduleTimeout = true;
break;
... ...
}
... ...
ret = s_unsolResponses[unsolResponseIndex] /* 調(diào)用消息處理函數(shù)responseString() */
.responseFunction(p, data, datalen);
... ...
ret = sendResponse(p); /* 發(fā)送Parcel中的信息內(nèi)容到服務(wù)端RILJ */
}
static UnsolResponseInfo s_unsolResponses[] = {
... ...
/* 消息對應(yīng)的消息處理函數(shù),新信息到來會喚醒系統(tǒng) */
{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(); /* 信息內(nèi)容轉(zhuǎn)換成Object */
return response;
}
2. 解析短信息
SmsMessage.newFromCMT(a);根據(jù)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的封裝,因為有g(shù)sm和cdma兩種短信,
* 即cdma中也有newFromCMT,根據(jù)情況按需選擇
*/
return new SmsMessage(wrappedMessage);
}
com.android.internal.telephony.gsm.SmsMessage.newFromCMT(lines)的實現(xiàn)在
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])把十六進(jìn)制的字符串轉(zhuǎn)換成字節(jié)數(shù)組msg.parsePdu()解析這個數(shù)組的內(nèi)容,最后獲得短信內(nèi)容
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的短信進(jìn)行解析 */
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); /* 解析信息有效內(nèi)容 */
... ...
}
private void parseUserData(PduParser p, boolean hasUserDataHeader) {
... ...
if (VDBG) Rlog.v(LOG_TAG, "SMS message body (raw): '" + mMessageBody + "'"); /* 短信內(nèi)容,參考log: SMS message body (raw): 'jchfbfh' */
... ...
}
三、處理短信息
對用戶有效的短信內(nèi)容,最終保存在類型為String的mMessageBody變量中,該變量屬于SmsMessageBase抽象類,而
SmsMessage繼承于SmsMessageBase。
回到前面frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java中processUnsolicited(),
sms = SmsMessage.newFromCMT(a);解析完短信息后,返回一個SmsMessage并通知上層應(yīng)用。
frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java
mGsmSmsRegistrant
.notifyRegistrant(new AsyncResult(null, sms, null)); /* 把sms轉(zhuǎn)成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;
}
... ...
}
根據(jù)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是在這里創(chuàng)建的 */
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為事件內(nèi)容,參考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(); /* 創(chuàng)建一個消息 */
msg.what = what; /* 消息類型EVENT_NEW_SMS */
msg.obj = new AsyncResult(userObj, result, exception); /* 消息內(nèi)容sms */
h.sendMessage(msg); /* 發(fā)送消息到注冊了這個消息的Handler,參考phone.mCi.setOnNewGsmSms(getHandler(), EVENT_NEW_SMS, null);的getHandler() */
}
}
... ...
}
然而BaseCommands是一個抽象類,實現(xiàn)了CommandsInterface中的setOnNewGsmSms接口,這個接口由GsmInboundSmsHandler調(diào)用
(phone.mCi.setOnNewGsmSms(getHandler(), EVENT_NEW_SMS, null)),也就是說GsmInboundSmsHandler的getHandler()是EVENT_NEW_SMS
的監(jiān)聽者,也就是說frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java中mGsmSmsRegistrant.notifyRegistrant(new AsyncResult(null, sms, null))
調(diào)用之后,會觸發(fā)GsmInboundSmsHandler中g(shù)etHandler()的Handler對EVENT_NEW_SMS消息進(jìn)行解析。這個Handler肯定是GsmInboundSmsHandler
實例化的對象中的,這個對象在什么時候,在哪里創(chuàng)建的,暫且不管。我們只管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,并啟動狀態(tài)機 */
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, /* 構(gòu)造GsmInboundSmsHandler時,通過super()調(diào)用InboundSmsHandler的構(gòu)造函數(shù) */
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調(diào)用啟動狀態(tài)機方法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); /* 構(gòu)造InboundSmsHandler時,添加狀態(tài)機的狀態(tài) */
addState(mStartupState, mDefaultState);
addState(mIdleState, mDefaultState);
addState(mDeliveringState, mDefaultState);
addState(mWaitingState, mDeliveringState);
setInitialState(mStartupState); /* 初始化狀態(tài)機 */
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); /* 轉(zhuǎn)到mDeliveringState */
return HANDLED;
... ...
}
}
}
... ...
class DeliveringState extends State { /* 轉(zhuǎn)到mDeliveringState狀態(tài) */
@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); /* 處理完回到空閑狀態(tài) */
return HANDLED;
... ...
}
}
... ...
}
}
void handleNewSms(AsyncResult ar) {
... ...
SmsMessage sms = (SmsMessage) ar.result;
result = dispatchMessage(sms.mWrappedSmsMessage);
... ...
}
public int dispatchMessage(SmsMessageBase smsb) {
... ...
return dispatchMessageRadioSpecific(smsb);
... ...
}
通過以上流程可以了解到,當(dāng)狀態(tài)機接收到SMS后,對消息進(jìn)行分發(fā),針對type zero, SMS-PP data download,
和3GPP/CPHS MWI type SMS判斷,如果是Normal SMS messages,則調(diào)用dispatchNormalMessage(smsb),然后創(chuàng)建
一個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); /* 創(chuàng)建一個廣播接收者,用來處理短信廣播的結(jié)果 */
... ...
intent = new Intent(Intents.SMS_DELIVER_ACTION); /* 設(shè)置當(dāng)前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只會發(fā)送給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); /* 成功廣播 */
... ...
}
... ...
}
到這里,在應(yīng)用層注冊具有Intents.SMS_RECEIVED_ACTION這樣action的廣播,就可以獲取到短信了。
總結(jié)
以上所述是小編給大家介紹的Android4.4 RIL短信接收流程分析,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
如果你覺得本文對你有幫助,歡迎轉(zhuǎn)載,煩請注明出處,謝謝!
相關(guān)文章
Android利用Document實現(xiàn)xml讀取和寫入操作
這篇文章主要為大家詳細(xì)介紹了Android利用Document實現(xiàn)xml讀取和寫入操作,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-12-12
Android使用Pull解析器解析xml文件的實現(xiàn)代碼
Android使用Pull解析器解析xml文件的實現(xiàn)代碼,需要的朋友可以參考一下2013-02-02
android 電話狀態(tài)監(jiān)聽(來電和去電)實現(xiàn)代碼
從事android開發(fā)的朋友們可能電話狀態(tài)監(jiān)聽不是很擅長,接下來將詳細(xì)介紹電話狀態(tài)監(jiān)聽功能的實現(xiàn)步驟,需要了解的朋友可以參考下2012-12-12
Android 中ListView的Item點擊事件失效的快速解決方法
這篇文章主要介紹了Android 中ListView的Item點擊事件失效的快速解決方法的相關(guān)資料,需要的朋友可以參考下2016-09-09
Android中自定義PopupWindow實現(xiàn)彈出框并帶有動畫效果
這篇文章主要介紹了Android中自定義PopupWindow實現(xiàn)彈出框并帶有動畫效果的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-09-09
解決Error:All flavors must now belong to a named flavor dimens
這篇文章主要介紹了解決Error:All flavors must now belong to a named flavor dimension. Learn more at https://d.android.com,需要的朋友可以參考下2017-11-11
Android Studio preview 不固定及常見問題的解決辦法
preview 可以幫助您預(yù)覽您的布局文件將如何在用戶的設(shè)備上呈現(xiàn)。這篇文章主要介紹了Android Studio preview 不固定及常見問題的解決辦法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-05-05
讓Android應(yīng)用不被殺死(killer)的方法
這篇文章主要介紹了讓Android應(yīng)用不被殺死(killer)的方法,本文講解了實現(xiàn)方法和原理分析,需要的朋友可以參考下2015-04-04

