Android - How to be the first to receive WAP PUSH (MMS)

android, broadcast, mms, push, wap

Solution

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.

Problem

I would like to intercept incomming MMS to enable mobile data. For that, I need to intercept them before any other app. I have setup my intent filter to receive `WAP_PUSH_RECEIVED_ACTION` broadcasts with the highest possible priority. But, in the Android documentation (https://developer.android.com/reference/android/provider/Telephony.Sms.Intents.html), there are the two following broadcasts: - `WAP_PUSH_DELIVER_ACTION` (Only sent to default sms app) - `WAP_PUSH_RECEIVED_ACTION` (Sent to all apps) Please, can you tell me which of these broadcasts is sent first (`WAP_PUSH_DELIVER_ACTION` or `WAP_PUSH_RECEIVED_ACTION`) and where did you find this information ? From where are them send in Android source code ? Does listening for `WAP_PUSH_RECEIVED_ACTION` with the highest possible priority let me be the first to receive WAP PUSH broadcasts ? Thanks

Original source