Search code examples
androidandroid-serviceandroid-contentprovider

Observe SMS Content Provider in Oreo and beyond


Objective Observe the SMS Content Provider to keep track of Sent SMS and notify the user when they reach a certain limit.

What works:

  1. Targeting API 22, I started a (START_STICKY) background service which initialized a ContentObserver that listens to SMS URI changes.
  2. Since Oreo doesn't allow background services, I changed this to a foregroundService that shows a persistent notification that a service is running but works as expected. (Running a normal background service, it gets stopped after around 1 minute when the parent activity is left idle)

Running on Oreo

While reading through many articles, I could find out stuff like JobIntentService which can perform a task in the background and then finish. Since I need the ContentObserver to run always or at least when the device is woken up, is there any preferred way to accomplish this.

The persistent notification has made even me to stop the service as it looks annoying.

References


Solution

  • On Android 7+ we can use JobScheduler and addTriggerContentUri() to solve this problem and its working.

    Sample code for this and the methods to achieve it are documented in the below blog post.

    http://midhunhk.github.io/dev/2018/08/05/content-observer-service/

    Create a JobService

    class MyJobService : JobService() {  
     override fun onStartJob(params: JobParameters?): Boolean {  
    
        doAsync{
          // Do your content observer changes here
    
          jobFinished(params, true)
        }
    
        return true
    }
    
    override fun onStopJob(params: JobParameters?): Boolean {
        return false
    }  
    

    }

    Scheduling a JobService

    val component = ComponentName(context, MyJobService::class.java)
            val contentUri = JobInfo.TriggerContentUri(Uri.parse("content://sms"),
                    JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS)
    
            val jobInfo = JobInfo.Builder(JOB_ID, component)
                    .addTriggerContentUri(contentUri)
                    .setTriggerContentUpdateDelay(DELAY_MIN)
                    .setTriggerContentMaxDelay(DELAY_MAX)
                    .build()
    
            // Schedule a Job if not already done so
            val scheduler: JobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
    
            val result = scheduler.schedule(jobInfo)
            return (result == JobScheduler.RESULT_SUCCESS)
        }
    

    More reading: https://medium.com/google-developers/scheduling-jobs-like-a-pro-with-jobscheduler-286ef8510129