1

I developing an app which needs to receive sms message and does not let any other application receive sms.

App must work well on both Kitkat and older versions.(I make my app default sms app in kitkat)

here is what I tried in my manifast file(not all of it):

    <receiver android:name=".SmsReceiver"
            android:permission="android.permission.BROADCAST_SMS">
        <intent-filter android:priority="2147483647" >
            <action android:name="android.provider.Telephony.SMS_DELIVER" />
        </intent-filter>
    </receiver>



    <receiver android:name=".SmsReceiver" android:enabled="true">
        <intent-filter android:priority="2147483647">
            <action android:name="android.provider.Telephony.SMS_RECEIVED" />
        </intent-filter>
    </receiver>

First receiver is for kitkat to let my app be default sms app(when I remove this part kitkat don't let my app to be default sms app) and second one is for older versions

On kitkat , The problem is all of my codes run two times (As I have two reciver)

And on older versions , my App runs the onReceive method one time but I got new message notification from Go SMS Pro , but I need the sms be received only by my application

Here is my SmsReceiver class:

public class SmsReceiver extends BroadcastReceiver 
{

    @Override
    public void onReceive(Context context, Intent intent) 
    {
        Bundle bundle = intent.getExtras();
        if (bundle != null) 
        {
            Object[] pdus = (Object[]) bundle.get("pdus");
            if (pdus.length == 0) 
            {
                return;
            }
            SmsMessage[] messages = new SmsMessage[pdus.length];
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < pdus.length; i++) 
            {
                messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
                sb.append(messages[i].getMessageBody());
            }
            String sender = messages[0].getOriginatingAddress();
            String message = sb.toString();

            abortBroadcast();// prevent any other broadcast receivers from receiving broadcast

            // things I need to do on SMS

        }
    }// on Rec

}
Arashdn
  • 691
  • 3
  • 11
  • 25
  • Log the action you see which each invocation. – 323go Jul 25 '14 at 15:45
  • you can test if the broadcast is ordered. I would expect the new `SMS_RECEIVED_ACTION` not to be ordered anymore, since it is not abortable. – njzk2 Jul 25 '14 at 15:55

1 Answers1

1

To support SMS handling for both older and newer versions of Android, I'd recommend having two different BroadcastReceiver classes: one for the new SMS_DELIVER_ACTION, and one registered for the original SMS_RECEIVED action, which should be disabled on KitKat (API level 19) and above so that you don't receive the same message twice. Each Receiver can simply pass the retrieved messages to a common processing component – e.g., a background Service – so you're not repeating code.

We can effect the version enabling/disabling with a resource bool that's true by default, but false on versions starting with KitKat. For example:

res/values/booleans.xml:

<resources>
    <bool name="isPreKitKat">true</bool>
</resources>

res/values-v19/booleans.xml:

<resources>
    <bool name="isPreKitKat">false</bool>
</resources>
<receiver
    android:name=".SmsReceiver"
    android:permission="android.permission.BROADCAST_SMS">
    <intent-filter>
        <action android:name="android.provider.Telephony.SMS_DELIVER" />
    </intent-filter>
</receiver>

<receiver
    android:name=".OldSmsReceiver"
    android:enabled="@bool/isPreKitKat"
    android:permission="android.permission.BROADCAST_SMS">
    <intent-filter>
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
</receiver>

SMS_DELIVER_ACTION doesn't exist on pre-KitKat versions, so that Receiver just shouldn't ever run on those versions, though you may wish to similarly enable/disable SmsReceiver appropriately, if only for security reasons.


You won't be able to abort the SMS_RECEIVED_ACTION broadcast at all in newer versions, as that is now disallowed altogether starting with KitKat. (Source)

Note that—beginning with Android 4.4—any attempt by your app to abort the SMS_RECEIVED_ACTION broadcast will be ignored so all apps interested have the chance to receive it.

However, if other SMS/messaging apps are behaving as recommended, they like you should no longer be listening for the SMS_RECEIVED_ACTION broadcast anyway. And if they're not the default, they won't get the SMS_DELIVER_ACTION one.

So, beyond those apps, any others able to listen for SMS are hopefully doing it only when necessary, and only for valid purposes, as you can't really do anything to prevent it on KitKat and above.


As for your problem with GO SMS Pro on pre-KitKat versions, there may not be anything you can do about it. Among other sources, this post thread suggests that you might overcome the problem by ensuring that your app is installed before GO SMS Pro. However, you can see from the comments that this is not a guaranteed solution. You might advise your users to turn off GO SMS Pro's "Disable other message notification" option, so your app can at least receive the pertinent broadcasts, even if it can't abort them. Note that Hangouts often causes the same problem.

Mike M.
  • 38,532
  • 8
  • 99
  • 95
  • My main problem is , on kitkat , the onReceive method will be executed twice in my app (Because I have added another reciver for older versions and It also will execute on kitkat) , I just want to execute it only one time – Arashdn Jul 25 '14 at 15:42
  • I don't understand why they added a new broadcast that replace the previous one and modify the previous broadcast behavior, instead of simply adding a new broadcast that would not be abortable. It seems to me it would have made the transition easier. – njzk2 Jul 25 '14 at 15:53
  • @MikeM. I am a beginner , how can I do that? – Arashdn Jul 25 '14 at 16:07
  • @MikeM. Thanks for answer , I tried Adding two action into one receiver , The app still get sms twice , I also tried to check name by intent.getAction() and limit the app , bit I couldn't – Arashdn Jul 26 '14 at 05:05
  • @MikeM. Well , How can I find if first action is happened or not? – Arashdn Jul 26 '14 at 05:44
  • @Mike M. It's been few years since KitKat was released, is your above approach to use 2 receivers and then disable the old receiver if Build.VERSION.SDK is >= KITKAT still the best way to handle all versions? – AJW Jul 14 '20 at 05:05
  • @AJW Are you creating a default SMS app? If so, then yeah, this is still a viable method, though there's a cleaner way to do it, that I apparently was not aware of back then. I can update the answer a little later, when I get a chance, if it'll help. – Mike M. Jul 14 '20 at 05:16
  • @Mike M. I am not creating a default SMS app. I am using default SMS to deliver user data (text) with an Intent, from my app to another user using my app. setPackage is the same for both users. – AJW Jul 14 '20 at 05:21
  • @AJW Sorry, but I don't really understand what you mean. The `SMS_DELIVER` broadcast is sent to only the current default SMS app, and only the system can send it (on standard Android). I'm not sure why you'd have a Receiver for `SMS_DELIVER`, or why you'd have a Receiver at all, really, if you're just using an external app to send messages. – Mike M. Jul 14 '20 at 05:26
  • @Mike M. The SMS message is to deliver the user's data in my app to another user of my app. For example, the user can send a small grocery list as a JSONObject that gets attached to the Intent that is received by the other user's BroadcastReceiver. If the other user accepts the text message by clicking on it then the BroadcastReceiver sends a message to a JobIntentService that then saves the grocery list data in the other user's SQLite database and the data is displayed for the other user with a CardView in a RecyclerView list. – AJW Jul 14 '20 at 05:39
  • 1
    @AJW Oh, OK, I think I get it. Your app is just monitoring incoming SMS for certain ones? In any case, on KitKat+, if your app is not the default SMS app, it will never get the `SMS_DELIVER` broadcast, so you don't need separate Receivers. You just need one for `SMS_RECEIVED`, which still works pretty much like it always did (except it's unordered, and can't be aborted anymore). – Mike M. Jul 14 '20 at 05:45
  • 1
    @Mike M. Yes, monitoring for SMS messages that have a secret code from another user of the app that then delivers the data. Understood on the SMS_RECEIVED. I appreciate the reply and your expertise, cheers. – AJW Jul 14 '20 at 05:49
  • @Mike M. One follow-up, for SMS_RECEIVED my app would need RECEIVE_SMS permission which Google Play considers dangerous and therefore developer has to submit a form to Google Play administrators for approval. Other developers have mentioned the process is awful with feedback taking weeks and often outright rejections are received with no explanations. Any ideas on how to avoid? – AJW Jul 15 '20 at 12:14
  • @AJW Not really. Not with direct SMS access, anyway. There are roundabout ways that are really shaky, unless you're certain of the user's setup; e.g., listening for `Notification`s from the default SMS app (which could be turned off, or not display the message at all), using an `AccessibilityService` in some fashion (which they'd have to manually enable), etc. – Mike M. Jul 15 '20 at 12:21
  • @Mike M. I hope they make an exception and allow RECEIVE_SMS as I have set up the app using setPackage() in the Intent to share data only between app users. If a user knows and trusts the other user they will be happy to accept the SMS data payload but Google Play would reject the app which then requires the approval request. Any thoughts on this as I don't have any experience with Google Play? – AJW Jul 15 '20 at 12:32
  • @AJW Sorry, no. I've never published an SMS app on the store, nor any other that needed special permission, like a dialer. I've only done messaging apps in-house. – Mike M. Jul 15 '20 at 12:35