本篇文章将简单介绍 Android Mashmallow MT(Mobile Terminated) SMS/MMS 的基本流程
一、短信的接收流程
1. RILJ 收到主动上报的消息 UNSOL_RESPONSE_NEW_SMS
在RIL的处理主动上报的消息的方法processUnsolicited,会通过Registrant的方式发送通知给监听者。
mGsmSmsRegistrant.notifyRegistrant(new AsyncResult(null, sms, null));
通过搜代码可以看到GsmInboundSmsHandler在构造方法里注册监听了这条消息,phone.mCi.setOnNewGsmSms(getHandler(), EVENT_NEW_SMS, null);
来看一下GsmInboundSmsHandler这个类的注释
/**
* This class broadcasts incoming SMS messages to interested apps after storing them in
* the SmsProvider "raw" table and ACKing them to the SMSC. After each message has been broadcast
*/
大概意思是这个类是用来,在把接收到的短信存到raw表中之后,会发广播给感兴趣的app。并且,在短信广播之后会给短信中心SMSC发送确信消息。
额,介绍就这么多~,还是要具体看看都干啥了。咦
public class GsmInboundSmsHandler extends InboundSmsHandler
public abstract class InboundSmsHandler extends StateMachine
GsmInboundSmsHandler 是状态机的子类,对状态机不熟的可以简单参考另一篇文章哟~http://blog.csdn.net/djialin0418/article/details/51030331
InboundSmsHandler 对这个类的介绍的注释比较多啊,一起看一下。
The state machine starts in {@link IdleState} state. When the {@link SMSDispatcher} receives a
* new SMS from the radio, it calls {@link #dispatchNormalMessage},
* which sends a message to the state machine, causing the wakelock to be acquired in
* {@link #haltedProcessMessage}, which transitions to {@link DeliveringState} state, where the message
* is saved to the raw table, then acknowledged via the {@link SMSDispatcher} which called us.
*
*
After saving the SMS, if the message is complete (either single-part or the final segment
* of a multi-part SMS), we broadcast the completed PDUs as an ordered broadcast, then transition to
* {@link WaitingState} state to wait for the broadcast to complete. When the local
* {@link BroadcastReceiver} is called with the result, it sends {@link #EVENT_BROADCAST_COMPLETE}
* to the state machine, causing us to either broadcast the next pending message (if one has
* arrived while waiting for the broadcast to complete), or to transition back to the halted state
* after all messages are processed. Then the wakelock is released and we wait for the next SMS.
*/
主要说了啥?1.状态机的起始状态是IdleState 2. SMS会在DeliveringState存到raw表中? 3.保存完信息之后会检查信息是否完整(如果是长信息,看看各个部分是否完整?)然后把完整的PDU嵌到有序广播中发送出去。4.然后状态机会进如WaitingState等广播完成,当本地的BroadcastReceiver发送EVENT_BROADCAST_COMPLETE给状态机后,会使 广播一条短信(如果这个时候是有一条短信已经接受但是在等当前的这个广播结束),或者处理所有消息后,状态机回到会haltedState。然后wakelock会被释放,并等待下一条短信。
注释文档介绍的还是挺清晰的,下面还是看代码吧~
1. 把短信写到数据库
GsmInboundSmsHandler 处理消息 EVENT_NEW_SMS
IdleState - processMessage -
case EVENT_NEW_SMS:
deferMessage(msg);
transitionTo(mDeliveringState);
状态机要在DeliveringState 处理这条消息 class DeliveringState extends State
/**
* In the delivering state, the inbound SMS is processed and stored in the raw table.
* The message is acknowledged before we exit this state. If there is a message to broadcast,
* transition to {@link WaitingState} state to send the ordered broadcast and wait for the
* results. When all messages have been processed, the halting state will release the wakelock.
*/
case EVENT_NEW_SMS:
handleNewSms((AsyncResult) msg.obj);
sendMessage(EVENT_RETURN_TO_IDLE);
在这个handleNewSms方法中会处理sms,并且处理后会调用RIL的接口来给SMSC短信中心发送确认消息
result = dispatchMessage(sms.mWrappedSmsMessage); //处理短信
notifyAndAcknowledgeLastIncomingSms(handled, result, null); //向SMSC短信中心发送确认消息
主要看一下短信是怎么被处理的
dispatchMessage -> dispatchMessageRadioSpecific(smsb) -> dispatchNormalMessage(smsb)//这里之后便是处理的重点了
InboundSmsTracker tracker;
tracker = new InboundSmsTracker(sms.getPdu(), sms.getTimestampMillis(), destPort,
is3gpp2(), false);
参数里面的3gpp2,3gpp是针对GSM手机的,3gpp2是针对CDMA手机的。
-> addTrackerToRawTableAndSendMessage(tracker) -> addTrackerToRawTable(tracker) //重点之一
看下注释
/**
* Insert a message PDU into the raw table so we can acknowledge it immediately.
* If the device crashes before the broadcast to listeners completes, it will be delivered
* from the raw table on the next device boot. For single-part messages, the deleteWhere
* and deleteWhereArgs fields of the tracker will be set to delete the correct row after
* the ordered broadcast completes.
*
* @param tracker the tracker to add to the raw table
* @return true on success; false on failure to write to database
*/
ContentValues values = tracker.getContentValues();
Uri newUri = mResolver.insert(sRawUri, values);//主要就是做了这么个事情~
这里还是要看一下getContentValues() 这个方法,看起来有利于debug
InboundSmsTracker
ContentValues getContentValues() {
ContentValues values = new ContentValues();
values.put("pdu", HexDump.toHexString(mPdu));
values.put("date", mTimestamp);
// Always set the destination port, since it now contains message format flags.
// Port is a 16-bit value, or -1, so clear the upper bits before setting flags.
int destPort;
if (mDestPort == -1) {
destPort = DEST_PORT_FLAG_NO_PORT;
} else {
destPort = mDestPort & DEST_PORT_MASK;
}
if (mIs3gpp2) {
destPort |= DEST_PORT_FLAG_3GPP2;
} else {
destPort |= DEST_PORT_FLAG_3GPP;
}
if (mIs3gpp2WapPdu) {
destPort |= DEST_PORT_FLAG_3GPP2_WAP_PDU;
}
values.put("destination_port", destPort);
if (mAddress != null) {
values.put("address", mAddress);
values.put("reference_number", mReferenceNumber);
values.put("sequence", mSequenceNumber);
values.put("count", mMessageCount);
}
return values;
}
pdu是怎样解析的啊,3gpp还是3gpp2啊,address电话号码啊什么的。
2.写完数据库那么该发广播了吧?
短信存好后return 个RESULT_SMS_HANDLED (= 1),然后发送个消息给状态机sendMessage(EVENT_BROADCAST_SMS, tracker),之后addTrackerToRawTableAndSendMessage返回Intents.RESULT_SMS_HANDLED,那一旦返回这个,岂不是要向SMSC发送确认了,好像也没有在发送短信广播之后啊?
注释貌似在这个地方说反了哟,是先向SMSC发送确认,后发送广播。
看一下在DeliveringState中的处理
case EVENT_BROADCAST_SMS:
// if any broadcasts were sent, transition to waiting state
InboundSmsTracker inboundSmsTracker = (InboundSmsTracker) msg.obj;
if (processMessagePart(inboundSmsTracker)) {
transitionTo(mWaitingState);
} else {
// if event is sent from SmsBroadcastUndelivered.broadcastSms(), and
// processMessagePart() returns false, the state machine will be stuck in
// DeliveringState until next message is received. Send message to
// transition to idle to avoid that so that wakelock can be released
log("No broadcast sent on processing EVENT_BROADCAST_SMS in Delivering " +
"state. Return to Idle state");
sendMessage(EVENT_RETURN_TO_IDLE);
}
return HANDLED;
字面上还是比较清晰的,如果在这里processMessagePart(inboundSmsTracker)发出广播了,那这个方法应该会return ture,然后状态机会进入WaitingState,在看一下WaitingState的注释
/** Broadcasting state. Waits for current broadcast to complete before delivering next. */
final WaitingState mWaitingState = new WaitingState();
应该是要等待这次广播完成,才能回到DeliveringState,之后才可以处理下一条信息。还是贴一下WaitingState的代码
case EVENT_BROADCAST_COMPLETE:
// return to idle after handling all deferred messages
sendMessage(EVENT_RETURN_TO_IDLE);
transitionTo(mDeliveringState);
return HANDLED;
还是看一下processMessagePart(inboundSmsTracker)这个方法,从注释上看貌似只有这个SmsBroadcastUndelivered.broadcastSms()方法过来的猜会return false,然后状态机会回到
IdleState。那processMessagePart(inboundSmsTracker)到底做了啥事情呢?
/**
* Process the inbound SMS segment. If the message is complete, send it as an ordered
* broadcast to interested receivers and return true. If the message is a segment of an
* incomplete multi-part SMS, return false.
* @param tracker the tracker containing the message segment to process
* @return true if an ordered broadcast was sent; false if waiting for more message segments
*/
boolean processMessagePart(InboundSmsTracker tracker) {
看来是要发广播,这里主要说了一个长短信的问题吧?就是如果一条长短信有好多部分,必须要等所有的部分都收到才能发广播,如果发了广播就会return true,否则为false。
代码比较长,不全贴了,主要说一下逻辑。首先是获得pdu,如果是短-短信,就是收到只有一条,那么pdu可以直接从tracker里面拿
// single-part message
pdus = new byte[][]{tracker.getPdu()};
如果是长-短信,只为先到的部分没有发广播,后到的部分跑到这里时,tracker里又没有携带先到的部分的内容,所以只能去数据库里取,为啥呢,因为刚刚存过了啊(是因为先存的啊)~
if (cursorCount < messageCount) {
// Wait for the other message parts to arrive. It's also possible for the last
// segment to arrive before processing the EVENT_BROADCAST_SMS for one of the
// earlier segments. In that case, the broadcast will be sent as soon as all
// segments are in the table, and any later EVENT_BROADCAST_SMS messages will
// get a row count of 0 and return.
return false;
}
上面这几句话没看懂,看懂的可以解释一下~难道可以给一个segment来发广播?说好了等segment都到齐了一起发呢~???但是从代码上是可以看出还是要等一起的。
然后new了个receiver,SmsBroadcastReceiver resultReceiver = new SmsBroadcastReceiver(tracker); 这个是用来接收广播的
在向下就是if (destPort == SmsHeader.PORT_WAP_PUSH),这个应该是处理彩信的。 那么彩信咋处理?
##通过wap push来下载,mWapPush.dispatchWapPdu(output.toByteArray(), resultReceiver, this);先不看这个,先把短信的看完。##
carrierPackages = card.getCarrierPackageNamesForIntent(
mContext.getPackageManager(),
new Intent(CarrierMessagingService.SERVICE_INTERFACE));
systemPackages =
getSystemAppForIntent(new Intent(CarrierMessagingService.SERVICE_INTERFACE));
这两个carrierPackages和systemPackages应该是可以处理接收短信的应用路径,
可能分别对应运营商的app(通过uiccCard来获得的)和系统app(好像是可以设置默认sms app的,不清楚是不是和这个是对应的)。
优先级如下,
if (carrierPackages != null && carrierPackages.size() == 1) {// 运营商的最高,但是数量只能是1
log("Found carrier package.");
CarrierSmsFilter smsFilter = new CarrierSmsFilter(pdus, destPort,
tracker.getFormat(), resultReceiver);
CarrierSmsFilterCallback smsFilterCallback = new CarrierSmsFilterCallback(smsFilter);
smsFilter.filterSms(carrierPackages.get(0), smsFilterCallback);
} else if (systemPackages != null && systemPackages.size() == 1) {// 系统其次,数量也必须是1
log("Found system package.");
CarrierSmsFilter smsFilter = new CarrierSmsFilter(pdus, destPort,
tracker.getFormat(), resultReceiver);
CarrierSmsFilterCallback smsFilterCallback = new CarrierSmsFilterCallback(smsFilter);
smsFilter.filterSms(systemPackages.get(0), smsFilterCallback);
} else {//否则就是默认的处理???
logv("Unable to find carrier package: " + carrierPackages
+ ", nor systemPackages: " + systemPackages);
dispatchSmsDeliveryIntent(pdus, tracker.getFormat(), destPort, resultReceiver);
}
这上面其实就是两种处理方式,一种是找到可用的app来接收sms,另外一种是默认的处理。先简单看一下如果有可用的app的那种。
smsFilter.filterSms(systemPackages.get(0), smsFilterCallback);
-> bindToCarrierMessagingService(mContext, carrierPackageName)
Intent intent = new Intent(CarrierMessagingService.SERVICE_INTERFACE);
-> context.bindService(intent, mCarrierMessagingServiceConnection, Context.BIND_AUTO_CREATE);
-> smsFilter是这个类的实例CarrierSmsFilter
onServiceReady(ICarrierMessagingService.Stub.asInterface(service)); //从这行代码可以看出bind的service是ICarrierMessagingService
/**
* Invokes the {@code carrierMessagingService} to filter messages. The filtering result is
* delivered to {@code smsFilterCallback}.
*/
@Override
protected void onServiceReady(ICarrierMessagingService carrierMessagingService) {
try {
carrierMessagingService.filterSms(
new MessagePdu(Arrays.asList(mPdus)), mSmsFormat, mDestPort,
mPhone.getSubId(), mSmsFilterCallback);
} catch (RemoteException e) {
loge("Exception filtering the SMS: " + e);
mSmsFilterCallback.onFilterComplete(true);
}
}
}
跨进程调用了啊,现在要找一下刚刚被bind的那个service的代码了。
代码在 frameworks/base/core/java/android/service/carrier/CarrierMessagingService.java
public void filterSms(MessagePdu pdu, String format, int destPort,
int subId, final ICarrierMessagingCallback callback) {
onFilterSms(pdu, format, destPort, subId, new ResultCallback
@Override
public void onReceiveResult(final Boolean result) throws RemoteException {
callback.onFilterComplete(result);
}
});
}
-> onFilterSms -> callback.onReceiveResult(true) //那也没干啥啊?bind的这个service之后啥都没干啊~难道是为了验证service有没有bind成功?可以不可以通信?
这个callback是上面的smsFilterCallback,回去看下,CarrierSmsFilterCallback smsFilterCallback = new CarrierSmsFilterCallback(smsFilter);
/**
* This method should be called only once.
*/
@Override
public void onFilterComplete(boolean keepMessage) {
mSmsFilter.disposeConnection(mContext);
logv("onFilterComplete: keepMessage is "+ keepMessage);
if (keepMessage) {
dispatchSmsDeliveryIntent(mSmsFilter.mPdus, mSmsFilter.mSmsFormat,
mSmsFilter.mDestPort, mSmsFilter.mSmsBroadcastReceiver);
} else {
// Drop this SMS.
final long token = Binder.clearCallingIdentity();
try {
// Needs phone package permissions.
deleteFromRawTable(mSmsFilter.mSmsBroadcastReceiver.mDeleteWhere,
mSmsFilter.mSmsBroadcastReceiver.mDeleteWhereArgs);
} finally {
Binder.restoreCallingIdentity(token);
}
sendMessage(EVENT_BROADCAST_COMPLETE);
}
}
这短代码除了有点长,还是很好理解的。bind成功就保留这条msg,没成功就丢掉即删除在raw表中的数据。看一下bind成功的处理,和默认的差不多啊~
1.dispatchSmsDeliveryIntent(mSmsFilter.mPdus, mSmsFilter.mSmsFormat,
mSmsFilter.mDestPort, mSmsFilter.mSmsBroadcastReceiver);
2.dispatchSmsDeliveryIntent(pdus, tracker.getFormat(), destPort, resultReceiver);
这个方法有很多有用的信息,
/**
* Creates and dispatches the intent to the default SMS app or the appropriate port.
*
* @param pdus message pdus
* @param format the message format, typically "3gpp" or "3gpp2"
* @param destPort the destination port
* @param resultReceiver the receiver handling the delivery result
*/
a.
destPort == -1代表啥意思还不确定,就假设当做现在没有接收app的意思吧,然后会获取用户设置默认收发短信的app的CompnentName,然后放在intent里
intent.setAction(Intents.SMS_DELIVER_ACTION);
ComponentName componentName = SmsApplication.getDefaultSmsApplication(mContext, true);
intent.setComponent(componentName);//用户没设置default sms app, 那就set null。
然后要做sms 的持久化了,SmsManager.getDefault().getAutoPersisting(),看看它要干啥,看下注释,如果返回true的话,就自动(有条件的)保存
/**
* Get the value of the flag to automatically write sent/received SMS/MMS messages into system
*
* When this flag is on, all SMS/MMS sent/received are stored by system automatically
* When this flag is off, only SMS/MMS sent by non-default SMS apps are stored by system
* automatically
*
* @return the current value of the auto persist flag
* {@hide}
*/
-> IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
return iMms.getAutoPersisting();//又来了,看看这个IMms.Stub在哪,有两个地方有具体实现~~~,原来是一个调用另一个,我去了,绕晕了。
-> 这个iMms应该是MmsServiceBroker的内部类,private final class BinderService extends IMms.Stub
-> public boolean getAutoPersisting() throws RemoteException {
return getServiceGuarded().getAutoPersisting();
}
getServiceGuarded() 这个方法要返回MmsService instance, 如果没有连接service, 那需要连一下(bindService)。现在代码还跑在Framework
-> 然后到了MmsService, 到了其他进程了, 代码目录 /packages/services/Mms/src/com/android/mms/service/MmsService.java
MmsService extends Service
private IMms.Stub mStub = new IMms.Stub(
-> getAutoPersisting()
-> getAutoPersistingPref() // 还是写在sharedPreference里的~~~,private static final String PREF_AUTO_PERSISTING = "autopersisting";
public boolean getAutoPersistingPref() {
final SharedPreferences preferences = getSharedPreferences(
SHARED_PREFERENCES_NAME, MODE_PRIVATE);
return preferences.getBoolean(PREF_AUTO_PERSISTING, false);
}
好长啊~要回到梦开始的地方了,如果自动持久化为true,那么会call writeInboxMessage(intent); //Store a received SMS into Telephony provider
final SmsMessage[] messages = Telephony.Sms.Intents.getMessagesFromIntent(intent);
mContext.getContentResolver().insert(Telephony.Sms.Inbox.CONTENT_URI, values);
很明显,拿出message,保存到Inbox 这个表里。
谁能帮忙解释一下下面这两句啥意思,干啥用滴?看不懂~
final long identity = Binder.clearCallingIdentity();
Binder.restoreCallingIdentity(identity);
b.
destPort == -1 为false,下面的我没看懂,要之后看看这个Uri有啥用
intent.setAction(Intents.DATA_SMS_RECEIVED_ACTION);
Uri uri = Uri.parse("sms://localhost:" + destPort);
intent.setData(uri);
最后调用dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
AppOpsManager.OP_RECEIVE_SMS, options, resultReceiver, UserHandle.OWNER);
if (user.equals(UserHandle.ALL)) {
mContext.sendOrderedBroadcastAsUser(intent, targetUser, permission, appOp, opts,
users[i] == UserHandle.USER_OWNER ? resultReceiver : null,
getHandler(), Activity.RESULT_OK, null, null)
} else {
mContext.sendOrderedBroadcastAsUser(intent, user, permission, appOp, opts,
resultReceiver, getHandler(), Activity.RESULT_OK, null, null);
}
dispatchIntent方法截取了部分代码,从调用时带入的参数UserHandle.OWNER 看,那么一直会走else这部分。好了,到现在广播也发了。下一步该干啥?看看谁收?
这个UserHandle.OWNER 和 UserHandle.ALL 能不能有人来解释一下~不懂~
好像是这个resultReceiver来接收者个广播~。虽然很长,还是全贴了吧,因为我可能说不明白~
/**
* Handler for an {@link InboundSmsTracker} broadcast. Deletes PDUs from the raw table and
* logs the broadcast duration (as an error if the other receivers were especially slow).
*/
private final class SmsBroadcastReceiver extends BroadcastReceiver {
private final String mDeleteWhere;
private final String[] mDeleteWhereArgs;
private long mBroadcastTimeNano;
SmsBroadcastReceiver(InboundSmsTracker tracker) {
mDeleteWhere = tracker.getDeleteWhere();
mDeleteWhereArgs = tracker.getDeleteWhereArgs();
mBroadcastTimeNano = System.nanoTime();
}
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intents.SMS_DELIVER_ACTION)) {//短信
// Now dispatch the notification only intent
intent.setAction(Intents.SMS_RECEIVED_ACTION);//变了
intent.setComponent(null);
// All running users will be notified of the received sms.
Bundle options = handleSmsWhitelisting(null);
dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
AppOpsManager.OP_RECEIVE_SMS, options, this, UserHandle.ALL);//变成ALL了哟~
} else if (action.equals(Intents.WAP_PUSH_DELIVER_ACTION)) {//彩信
// Now dispatch the notification only intent
intent.setAction(Intents.WAP_PUSH_RECEIVED_ACTION);
intent.setComponent(null);
// Only the primary user will receive notification of incoming mms.
// That app will do the actual downloading of the mms.
Bundle options = null;
try {
long duration = mDeviceIdleController.addPowerSaveTempWhitelistAppForMms(
mContext.getPackageName(), 0, "mms-broadcast");
BroadcastOptions bopts = BroadcastOptions.makeBasic();
bopts.setTemporaryAppWhitelistDuration(duration);
options = bopts.toBundle();
} catch (RemoteException e) {
}
dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
AppOpsManager.OP_RECEIVE_SMS, options, this, UserHandle.OWNER);
} else {
// Now that the intents have been deleted we can clean up the PDU data.
if (!Intents.DATA_SMS_RECEIVED_ACTION.equals(action)
&& !Intents.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);// 要把raw表的数据删除
sendMessage(EVENT_BROADCAST_COMPLETE);
int durationMillis = (int) ((System.nanoTime() - mBroadcastTimeNano) / 1000000);
if (durationMillis >= 5000) {
loge("Slow ordered broadcast completion time: " + durationMillis + " ms");
} else if (DBG) {
log("ordered broadcast completed in: " + durationMillis + " ms");
}
}
}
}
EVENT_BROADCAST_COMPLETE,现在是啥状态?哦,发了广播要在WaitingState的。
case EVENT_BROADCAST_COMPLETE:
// return to idle after handling all deferred messages
sendMessage(EVENT_RETURN_TO_IDLE);
transitionTo(mDeliveringState);
return HANDLED;
case EVENT_RETURN_TO_IDLE:
// not ready to return to idle; ignore
return HANDLED;
额~~~~搞毛线啊~,不管了,那现在貌似没啥事了。
sms app可能收到广播后,弹出个通知,然后用户点进去的时候会查数据库,就是Inbox那个,然后就会显示出来,并且有已读未读的状态显示什么的吧,我猜的。
二、还有彩信~~~代码超长,不贴了。大部分代码看不懂,但是感觉用处不大~
if (destPort == SmsHeader.PORT_WAP_PUSH)
mWapPush.dispatchWapPdu(output.toByteArray(), resultReceiver, this);
在流程上,关注的应该就是下面这几行了吧~
Intent intent = new Intent(Intents.WAP_PUSH_DELIVER_ACTION);//过滤处理的应用
ComponentName componentName = SmsApplication.getDefaultMmsApplication(mContext, true);//找到默认app来处理, 没有就拉倒,谁处理都行
handler.dispatchIntent(intent, permission, appOp, options, receiver, UserHandle.OWNER);//发广播~