This topic seems to be not so popular!
I tried to answer the question myself and I found something interesting.
Analysis
SMS and MMS reception are mainly managed in the file InboundSmsHandler.java
.
This file starts with a comment block that explains the SMS/MMS receiving state machine.
Here is an extract of this comment with explanations:
- The state machine starts in
InboundSmsHandler.IdleState
state.
- When the
SMSDispatcher
receives a new SMS from the radio, it calls dispatchNormalMessage(com.android.internal.telephony.SmsMessageBase)
, which transitions to InboundSmsHandler.DeliveringState
state.
- From the
InboundSmsHandler.DeliveringState
state, processMessagePart(InboundSmsTracker tracker)
is called. Within this method, if the destination port number of the SMS is SmsHeader.PORT_WAP_PUSH
(in other words if the SMS is an MMS), the WapPushOverSms.dispatchWapPdu(byte[] pdu, BroadcastReceiver receiver, InboundSmsHandler handler)
method is called.
- Inside the
dispatchWapPdu
method, they call InboundSmsHandler.dispatchIntent(Intent intent, String permission, int appOp, BroadcastReceiver resultReceiver, UserHandle user)
. They check if there is a default MMS app and if it's the case, configure the intent to only be delivered to this app.
Code:
// Direct the intent to only the default MMS app. If we can't find a default MMS app
// then sent it to all broadcast receivers.
ComponentName componentName = SmsApplication.getDefaultMmsApplication(mContext, true);
if (componentName != null) {
// Deliver MMS message only to this receiver
intent.setComponent(componentName);
if (DBG) Rlog.v(TAG, "Delivering MMS to: " + componentName.getPackageName() +
" " + componentName.getClassName());
}
handler.dispatchIntent(intent, permission, appOp, receiver, UserHandle.OWNER);
- Inside the
dispatchIntent
we have what we are looking for, the call to Context.sendOrderedBroadcastAsUser(...)
. So, it is this method that sends the WAP_PUSH_DELIVER_ACTION
broadcast as an ordered broadcast.
- This broadcast is also handled (default app and
SmsBroadcastReceiver
) by the SmsBroadcastReceiver.onReceive(Context context, Intent intent)
handler located in InboundSmsHandler.java
. Inside this handler, the WAP_PUSH_DELIVER_ACTION
case is processed. The intent is changed to WAP_PUSH_RECEIVED_ACTION
and broadcasted again through the InboundSmsHandler.dispatchIntent(Intent intent, String permission, int appOp, BroadcastReceiver resultReceiver, UserHandle user)
method. This time, not only the default app is concerned, but all interested apps.
Code:
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intents.SMS_FILTER_ACTION)) {
// ...
} else if (action.equals(Intents.SMS_DELIVER_ACTION)) {
// ...
} 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.
dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
AppOpsManager.OP_RECEIVE_SMS, this, UserHandle.OWNER);
} else {
// ...
}
}
Conclusion (Quick answer to the original question)
When an MMS is received, WAP_PUSH_DELIVER_ACTION
is broadcasted first to the default app followed by the WAP_PUSH_RECEIVED_ACTION
.
Both broadcasts are ordered broadcasts that means that priorities can be used.
Well, it's a bad news for me because it also means that I cannot be the first to be notified for an incoming MMS and turn on the modile data before the MMS app is notified.
Ahh Google, with Lollipop, you make the things harder for us : Android Issue 78084 - setMobileDataEnabled removed
So, I have to look for another way in order to do that.