6

I'm developing an app which needs to run some code (Networking) whenever an SMS is received.
In API 25 and lower it's fine, I register an implicit receiver in Manifest file and start my service in the specified class which extended BroadcastReceiver. In API 26 however you cannot register android.provider.Telephony.SMS_RECEIVED in a receiver since it won't work.

From Android documentation:

Note: If your app targets API level 26 or higher, you cannot use the manifest to declare a receiver for implicit broadcasts (broadcasts that do not target your app specifically), except for a few implicit broadcasts that are exempted from that restriction. In most cases, you can use scheduled jobs instead.

I've read several articles like this one on medium. There are solutions like JobScheduler or Explicit Receiver, however the first one is used for changes in network state and I couldn't find a way to trigger the job on SMS_RECEIVED event and the second one is valid until your activity is up and running.

Because of the nature of my application I need to listen for incoming SMS whether the app is running or not. How to do that in API 26+?

Edit

Maybe the code in JobInfoBuilder doc on android website could help. It monitors the changes in the photos on a device and start the job on change. However I cannot find a proper Uri to do the same with the SMS (not even sure if it's possible to monitor SMS via ContentObserver)

szamani20
  • 650
  • 9
  • 26
  • I think job scheduler with periodic check method may be the correct solution. – VVB Oct 01 '17 at 12:11
  • @VVB I'm developing a TTS app that should play the sound when the SMS is received, however it seems that lowering the period time to detect the SMS received have a bad impact on performance and it's not a proper solution. – szamani20 Oct 01 '17 at 13:17
  • 3
    The `SMS_RECEIVED` action is exempt from the new implicit broadcast restrictions. https://developer.android.com/guide/components/broadcast-exceptions.html – Mike M. Oct 02 '17 at 19:20
  • @MikeM. Thanks. My terrible mistake here, I even searched for "SMS" in that exception list. However for those non-excepted receivers other than network related ones which could be triggered directly by `setRequiredNetworkType` [method](https://developer.android.com/reference/android/app/job/JobInfo.Builder.html) is there any way like `ContentObserver` (not sure about that) to do same job as implicit receivers? – szamani20 Oct 03 '17 at 07:47
  • 1
    I'm not sure what you're asking. The `setRequiredNetworkType()` method sets which network must be present to run a given job. It doesn't trigger any job itself. As far as `ContentObserver`s, using `JobScheduler` would be preferable, if you're running on API level 21+. In either case, the URI for SMS is `content://sms`, and you can't get any more specific than that with the SMS Provider, for some reason. That is, you cannot observe only `content://sms/inbox`, the received messages. – Mike M. Oct 03 '17 at 22:24
  • Could you provide an example of how to do the same job as implicit receiver, with `JobScheduler` and `ContentObserver` for receiving SMS as an answer to my question? I can't find a proper example of doing that and I'm still not sure whether it could be done as a replacement for implicit receiver. – szamani20 Oct 04 '17 at 06:45

2 Answers2

4

Since there are lots of ways to do the job in android O, I post this answer and mention my approach to solve the problem. Obviously by problem I mean the general problem not the SMS_RECEIVED receiver itself.

I start a foreground service and in there I register a dynamic or explicit receiver to listen to the incoming calls (for instance):

In MainActivity.java:

String action = "START"
final Intent intent = new Intent(this, CallMonitorService.class);
intent.setAction(action);
startService(intent);

In CallMonitorService.javas onCreate() method where I have BroadcastReceiver callExplicitReceiver as a field:

    final IntentFilter intentFilter = new IntentFilter();
    intentFilter.setPriority(2147483647);
    intentFilter.addAction("android.intent.action.PHONE_STATE");
    this.callExplicitReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
                // do the stuff here
            }
        }
    };
    registerReceiver(callExplicitReceiver, intentFilter);

and then in onStartCommand():

    if (intent.getAction().equals("START")) {
        Intent callServiceNotificationIntent = new Intent(this, MainActivity.class);
        callServiceNotificationIntent.setFlags(
            Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        PendingIntent pendingIntent = PendingIntent
            .getActivity(this, CALL_SERVICE_REQUEST_CODE,
                    callServiceNotificationIntent, CALL_SERVICE_FLAG);

        Notification notification = new NotificationCompat.Builder(this)
            .setContentTitle(CALL_NOTIFICATION_CONTENT_TITLE)
            .setTicker(CALL_NOTIFICATION_TICKER)
            .setContentText(CALL_NOTIFICATION_CONTENT_TEXT)
            .setSmallIcon(R.drawable.ic_info_outline_black_24dp)
            .setContentIntent(pendingIntent)
            .setOngoing(true)
            .build();
        startForeground(CALL_NOTIFICATION_ID, notification);
    }

and finally:

@Override
public void onDestroy() {
    super.onDestroy();
    unregisterReceiver(callExplicitReceiver);
}

I think of this as a good approach since the user is notified of the running service because of the undismissable notification and that's what android Oreo wants, however through a button in the app user could stop the service and the monitoring receiver as a direct result of destroying service (I cleared that part of code).

szamani20
  • 650
  • 9
  • 26
  • 1
    I've done the same thing in my app - some people were confused about having the icon in the notification bar (it's there on 8.0 by default, not on 8.1) as it's a foreground service. Personally, I'd be getting annoyed if there's like 10 of these notifications. So not sure if Google thought it through. I have had complaints that some phone numbers don't trigger the BroadcastReceiver *at all* on 8.0. Have you seen that ? – Someone Somewhere Jul 06 '18 at 08:18
  • I know the service is active at the time because I've added a bug report feature with a log and there's no messages that service is re-started. – Someone Somewhere Jul 06 '18 at 08:35
  • @szmani20 is it still only solution right now? – ShadeToD Jan 18 '21 at 17:03
3

I think for now you are safe as SMS_RECEIVED_ACTION is present in the current exempted implicit broadcast list. Also, upon receiving the system broadcast you can either start a foreground service or schedule a job (to perform network operation in your case). Moreover, I am also using the same action and upon testing it seems to work okay.

shailesh mota
  • 309
  • 2
  • 7
  • 1
    Thanks for your answer. I also needed to some other receivers which are not in that list. My approach was to start a foreground service and register a dynamic (explicit) receiver in there programmatically and also to restart the service in boot startup. I'll post my approach as an answer. – szamani20 Oct 20 '17 at 14:58