1. 程式人生 > 程式設計 >詳解Android4.4 RIL簡訊接收流程分析

詳解Android4.4 RIL簡訊接收流程分析

最近有客戶反饋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,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,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,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,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,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(),null);的getHandler() */
  }
 }
 ... ...
} 

然而BaseCommands是一個抽象類,實現了CommandsInterface中的setOnNewGsmSms介面,這個介面由GsmInboundSmsHandler呼叫
(phone.mCi.setOnNewGsmSms(getHandler(),null)),也就是說GsmInboundSmsHandler的getHandler()是EVENT_NEW_SMS
的監聽者,也就是說frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java中mGsmSmsRegistrant.notifyRegistrant(new AsyncResult(null,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(),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,PhoneBase phone) {
  GsmInboundSmsHandler handler = new GsmInboundSmsHandler(context,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,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的廣播,就可以獲取到簡訊了。

總結

以上所述是小編給大家介紹的Android4.4 RIL簡訊接收流程分析,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回覆大家的。在此也非常感謝大家對我們網站的支援!
如果你覺得本文對你有幫助,歡迎轉載,煩請註明出處,謝謝!