3

I'm working on an app that runs a background service to range bluetooth beacons in intervals.

I start a ForegroundService with a timer to start ranging beacons for 10 seconds every minute, with an interval of 200 millis, calculates the strongest beacon and submits it to the backend API.

This works neatly while the app is in foreground, and, when the screen is off, as long as i'm hooked up using adb logcat. As soon as I take it off, nothing gets submitted to the servers anymore, meaning that no beacons are being ranged anymore.

Here are the relevant code pieces, I hope I didn't simplify too much:

class BeaconService : Service(), BeaconConsumer {
    private var beaconManager: BeaconManager? = null
    private var rangingTimer = Timer("rangingTimer", true)
    private val region = Region("com.beacon.test", Identifier.parse("f7826da6-4fa2-4e98-8024-bc5b71e0893e"), null, null)

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        return Service.START_STICKY
    }

    override fun onCreate() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val notificationManager = MyNotificationManager.getInstance()
            val notification = notificationManager.buildBeaconServiceNotification(this, "iBeacon service", null)
            startForeground(NOTIFICATION_ID, notification)
        }

        initBeaconManager()
    }

    private fun initBeaconManager() {
        BeaconManager.setDebug(true)
        beaconManager = BeaconManager.getInstanceForApplication(this)
        beaconManager?.foregroundScanPeriod = 200L
        beaconManager?.beaconParsers?.add(BeaconParser().setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"))
        beaconManager?.bind(this)
    }

    override fun onBeaconServiceConnect() {
        beaconManager?.addRangeNotifier { beacons, _ ->
            if (beacons.isNotEmpty()) {
                //code add ranged beacons to list
            }
        }
        startRanging()
    }

    private fun startRanging() {

        //code to reset the list of ranged beacons

        beaconManager?.startRangingBeaconsInRegion(region)

        rangingTimer.schedule(10000L) {
            stopRanging(50000L)
        }

    }

    private fun stopRanging(restartRangingAfter: Long? = null) {
        beaconManager?.stopRangingBeaconsInRegion(region)

        //code calcuate the strongest beacon and submit to server

        if (restartRangingAfter != null) {
            rangingTimer.schedule(restartRangingAfter) {
                startRanging()
            }
        }
    }
}
kimyas78
  • 145
  • 10
  • 1
    What device model and operating system version are you using to get these results? – davidgyoung Mar 14 '19 at 12:48
  • I'm testing this on 3 phones: - Samsung Galaxy S9+ with Android 9 - Motorola one with Android 8.1 - Nokia 2.1, also Android 8.1 – kimyas78 Mar 14 '19 at 13:04

1 Answers1

2

On OS Versions 8+, Android limits background processing unless it is part of a foreground service or a job initiated by the JobScheduler. As a result of this limitation, the Android Beacon Library will by default use the JobScheduler on Android 8+. In the foreground, an "immediate" ScanJob will run constantly to do scans. In the background (meaning when no activities are visible with the screen unlocked), Android does not allow this. A job may be scheduled at most once every ~15 minutes. This is why you see scans stop.

It does not matter that you have your own foreground service. Android still enforces these limits on any background processing performed outside that foreground service.

Two alternatives:

  1. Live with the job limitations (scan once every 15 minutes). Use BackgroundPowerSaver to auto switch between foreground/background mode and set beaconManager.setBackgroundScanPeriod(5000) (for a 5 second scan every 15 minutes.) For clarity, you should also set beaconManager.setBackgroundBetweenScanPeriod(15*60*1000) (15 minutes), although you can set a lower value that will be disallowed by the OS on Android 8+.

  2. Set up the library to scan with its own Foreground Service (yes as second foreground service) as described here. You can then stop using your own foreground service, or keep it. If you keep it, you will see two notification icons about the two foreground services running. It is possible to combine those two notifications if you want to keep two foreground services and show just one notification.

davidgyoung
  • 63,876
  • 14
  • 121
  • 204
  • Thank you for your reply. I actually tried making it a Foreground Service, but still had issues with finding beacons. It seems that, at least my Samsung Galaxy S9+ stops passing on any bluetooth information as soon the screen is off. Trying to figure out if this is Samsung's doing or Android Pie being even more restrictive – kimyas78 Mar 15 '19 at 11:03
  • 1
    On Samsung devices with Android 8.1+, scans are blocked in the background unless a non-empty scan filter is used. The library is designed for this and will automatically use one on Samsung devices, but only if in background scanning mode. For this to work, you must put it the library in background mode by using BackgroundPowerSaver or setting `beaconManager.setBackground(true)` manually whenever the screen is off. All of this is setup up automatically with library defaults, but with your custom configuration you need to take extra care that these things happen. – davidgyoung Mar 15 '19 at 14:34