Detailed explanation of Android4.4 RIL short message receiving process analysis

  • 2021-11-13 18:13:28
  • OfStack

Recently, some customers reported that Android could not receive SMS, so one head plunged into RIL to find out the reason. Finally, it was found that it was not the problem of RIL, but the report of BC72
The format of short message is wrong, and AT+CNMA=1 has no effect, which is caused by several minor problems. Although the problem is not RIL, the RIL SMS receiving process is finally clarified.

log receiving new information:

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. SMS Reception

1. vendor ril receives the short message reported by 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,        /*  Report UNSOL_RESPONSE_NEW_SMS Message  */
   sms_pdu, strlen(sms_pdu)); 
 }
 ... ...
}

2. RILD Sends SMS to RILJ


hardware/ril/libril/ril.cpp
extern "C"
void RIL_onUnsolicitedResponse(int unsolResponse, void *data,
        size_t datalen)
{
 ... ...
 unsolResponseIndex = unsolResponse - RIL_UNSOL_RESPONSE_BASE; /*  Find out the message in s_unsolResponses[] Index of  */
 ... ...
 switch (s_unsolResponses[unsolResponseIndex].wakeType) {   /*  Do not enter hibernation  */
  case WAKE_PARTIAL:
   grabPartialWakeLock();
   shouldScheduleTimeout = true;
  break;
  ... ...
 }
 ... ...
 ret = s_unsolResponses[unsolResponseIndex]      /*  Call the message handler responseString() */
    .responseFunction(p, data, datalen);
 ... ...
 ret = sendResponse(p);           /*  Send Parcel To the server RILJ */
}
static UnsolResponseInfo s_unsolResponses[] = { 
... ...
/*  Message processing function corresponding to message, new information will wake up the system when it arrives  */
{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);     /*  Save information in string format to Parcel In a container  */
 return 0;
}

STEP 2 Parse SMS

1. RILJ Get SMS



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);      /*  Reference 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);       /*  Analyse PDU Short message in format  */
   if (mGsmSmsRegistrant != null) {
    mGsmSmsRegistrant
     .notifyRegistrant(new AsyncResult(null, sms, null));
   }
   break;
  }
  ... ...
 }
 ... ...
}
private Object
responseString(Parcel p) { 
 String response;
 response = p.readString();               /*  Information content is converted into Object */
 return response;
}

STEP 2 Parse SMS

SmsMessage. newFromCMT (a); According to import android. telephony. SmsMessage, the code path is:



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);  /*  It's for another 1 A newFromCMT Encapsulation of, because there is a gsm And cdma Two kinds of text messages, 
                     *  I.e. cdma There are also in newFromCMT , choose according to the situation and needs  
                     */
 return new SmsMessage(wrappedMessage);
}
  com.android.internal.telephony.gsm.SmsMessage.newFromCMT(lines) The implementation of the 
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]));    /*  Analyse PDU SMS  */
   return msg;
  } catch (RuntimeException ex) {
   Rlog.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
   return null;
  }
 }
 ... ...
}
  IccUtils.hexStringToBytes(lines[1]) Put 106 To convert a binary string to a byte array msg.parsePdu() Parse the contents of this array and finally get the contents of the short message 
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);   /*  Reference 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);         /*  For short message type is Deliver Analyze the short message of  */
    break;
   ... ...
  }
 ... ...
}
private void parseSmsDeliver(PduParser p, int firstByte) {
 ... ...
 mOriginatingAddress = p.getAddress();                            
 if (mOriginatingAddress != null) {
  if (VDBG) Rlog.v(LOG_TAG, "SMS originating address: "    /*  Reference 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);    /*  Reference log: SMS TP-PID:0 data coding scheme: 0 */
 }
 mScTimeMillis = p.getSCTimestampMillis();
 if (VDBG) Rlog.d(LOG_TAG, "SMS SC timestamp: " + mScTimeMillis);   /*  Reference log : SMS SC timestamp: 1571831129000 */
 boolean hasUserDataHeader = (firstByte & 0x40) == 0x40;
 parseUserData(p, hasUserDataHeader);          /*  Analyze the effective content of information  */
 ... ... 
}  
private void parseUserData(PduParser p, boolean hasUserDataHeader) {
 ... ...
 if (VDBG) Rlog.v(LOG_TAG, "SMS message body (raw): '" + mMessageBody + "'"); /*  SMS content, reference log: SMS message body (raw): 'jchfbfh' */
 ... ...
} 

STEP 3 Handle SMS

The text message content valid for the user is finally stored in the mMessageBody variable of type String, which belongs to the SmsMessageBase abstract class, and
SmsMessage inherits from SmsMessageBase.
Back to processUnsolicited () in frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL. java,
sms = SmsMessage. newFromCMT (a); After parsing the short message, return an SmsMessage and notify the upper application.


frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java
mGsmSmsRegistrant
 .notifyRegistrant(new AsyncResult(null, sms, null));        /*  Put sms Turn into Object Type  */
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;
 }
 ... ...
}

According to mGsmSmsRegistrant. notifyRegistrant (new AsyncResult (null, sms, null); Find the code for mGsmSmsRegistrant registration:


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)) In mGsmSmsRegistrant Is created here  */                      
  mGsmSmsRegistrant = new Registrant (h, what, obj);                         
 }
 ... ...
} 

Encapsulation message EVENT_NEW_SMS message


frameworks/base/core/java/android/os/Registrant.java
public class Registrant                                  
{ 
 public
 Registrant(Handler h, int what, Object obj)      /*  The incoming message to be processed is what Event handling of Handler h , obj For event content, reference 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)         /*  Reference mGsmSmsRegistrant.notifyRegistrant(new AsyncResult(null, sms, null)) */
 {
  internalNotifyRegistrant (ar.result, ar.exception);   /* ar.result For sms */
 }
 /*package*/ void
 internalNotifyRegistrant (Object result, Throwable exception)  /* internalNotifyRegistrant (sms, Throwable exception) */
 {
  Handler h = getHandler();
  if (h == null) {
   clear();
  } else {
   Message msg = Message.obtain();       /*  Create 1 Messages  */
   msg.what = what;           /*  Message type EVENT_NEW_SMS */
   msg.obj = new AsyncResult(userObj, result, exception); /*  Message content sms */
   h.sendMessage(msg);          /*  Send a message to the Handler , reference phone.mCi.setOnNewGsmSms(getHandler(), EVENT_NEW_SMS, null); Adj. getHandler() */
  }
 }
 ... ...
} 

However, BaseCommands is an abstract class that implements the setOnNewGsmSms interface in CommandsInterface, which is called by GsmInboundSmsHandler
(phone. mCi. setOnNewGsmSms (getHandler (), EVENT_NEW_SMS, null)), which means that getHandler () of GsmInboundSmsHandler is EVENT_NEW_SMS
frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL. java mGsmSmsRegistrant. notifyRegistrant (new AsyncResult (null, sms, null)
When called, Handler of getHandler () in GsmInboundSmsHandler is triggered to parse the EVENT_NEW_SMS message. This Handler must be GsmInboundSmsHandler
Instantiated object, when and where this object was created, regardless. We just care where the message EVENT_NEW_SMS comes from and where it goes
Just do it.


./frameworks/opt/telephony/src/java/com/android/internal/telephony/ImsSMSDispatcher.java
public final class ImsSMSDispatcher extends SMSDispatcher {
 ... ...
 mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(phone.getContext(),   /*  Get mGsmInboundSmsHandler And start the state machine  */
   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,        /*  Structure GsmInboundSmsHandler When, pass super() Call InboundSmsHandler Constructor of  */
    GsmCellBroadcastHandler.makeGsmCellBroadcastHandler(context, phone));
  phone.mCi.setOnNewGsmSms(getHandler(), EVENT_NEW_SMS, null);        /*  Registration EVENT_NEW_SMS Message  */
  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); /*  Instantiation GsmInboundSmsHandler */
  handler.start();                   /*  Abstract class InboundSmsHandler Inheritance and StateMachine , and GsmInboundSmsHandler Inherit from InboundSmsHandler , 
                         * GsmInboundSmsHandler Call the startup state machine method 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);                 /*  Structure InboundSmsHandler Add the state of the state machine when  */
  addState(mStartupState, mDefaultState);
  addState(mIdleState, mDefaultState);
  addState(mDeliveringState, mDefaultState);
  addState(mWaitingState, mDeliveringState);
  setInitialState(mStartupState);               /*  Initialize state machine  */
  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:                /*  When you are idle, you receive short messages  */
    case EVENT_BROADCAST_SMS:
     deferMessage(msg);
     transitionTo(mDeliveringState);            /*  Go to mDeliveringState */
     return HANDLED;
    ... ...
   }
  }
 } 
  ... ...
 class DeliveringState extends State {               /*  Go to mDeliveringState Status  */
  @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);           /*  Deal with new SMS */
     sendMessage(EVENT_RETURN_TO_IDLE);            /*  Go back to idle state after processing  */
     return HANDLED;
    ... ...
   }
  } 
   ... ...
 }
}
void handleNewSms(AsyncResult ar) {
 ... ...
 SmsMessage sms = (SmsMessage) ar.result;
 result = dispatchMessage(sms.mWrappedSmsMessage);
 ... ...
}
public int dispatchMessage(SmsMessageBase smsb) {
 ... ...
 return dispatchMessageRadioSpecific(smsb);
 ... ...
}

According to the above process, when the state machine receives SMS, it distributes messages, aiming at type zero, SMS-PP data download,
And 3GPP/CPHS MWI type SMS, if it is Normal SMS messages, call dispatchNormalMessage (smsb), and then create
An InboundSmsTracker object that saves the information to raw table and broadcasts the message through sendMessage (EVEN
OADCAST_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:               /*  Received EVENT_BROADCAST_SMS Message and process  */
    // 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);     /*  Create 1 Broadcast recipients, which are used to process the results of SMS broadcasting  */
 ... ...
 intent = new Intent(Intents.SMS_DELIVER_ACTION);           /*  Sets the current intent Adj. action For 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); /*  This action Will only be sent to carrier app , and carrier app It can be passed through set result For RESULT_CANCELED To terminate this broadcast  */
 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,       /*  Broadcast 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);            /*  Successful broadcast  */

  ... ...
 }
 ... ...
}

At this point, you can get the short message by registering the broadcast with Intents.SMS_RECEIVED_ACTION in the application layer.

Summarize


Related articles: