AltBeacon / android-beacon-library

Allows Android apps to interact with BLE beacons
Apache License 2.0
2.83k stars 834 forks source link

Background scan stops working after the app is killed #1094

Closed JulianModlinski closed 2 years ago

JulianModlinski commented 2 years ago

Expected behavior

The iBeacon scan continues in the background

Actual behavior

The background scan stops suddenly Logcat:

2022-06-16 11:56:04.176 4020-4020/? I/ActivityManager: Killing 18682:com.example.beaconbackgroundscan/u0a360 (adj 1001): remove task
2022-06-16 11:56:04.177 4020-6040/? W/NotificationService: Toast already killed. pkg=com.example.beaconbackgroundscan token=android.os.BinderProxy@91a7c6e
2022-06-16 11:56:04.218 4382-4399/? D/LeAppInfo: removeLeacReportedServerApp, appName: com.example.beaconbackgroundscan
2022-06-16 11:56:04.222 4382-18751/? D/LeAppInfo: removeLeacReportedServerApp, appName: com.example.beaconbackgroundscan
2022-06-16 11:56:04.226 4382-4871/? E/BtGatt.GattService: [GSIM LOG]: gsimLogHandler, msg: MESSAGE_SCAN_STOP, appName: com.example.beaconbackgroundscan, scannerId: 7, reportDelayMillis=0

Steps to reproduce this behavior

  1. Open app
  2. Force close the app

Mobile device model and OS version

Samsung Galaxy A10 Android version 11

Android Beacon Library version

2.19

IMPORTANT: This forum is reserved for feature requests or reproducible bugs with the library itself. If you need help with using the library with your project, please open a new question on StackOverflow.com.

Application code:

class MainActivity : AppCompatActivity(), MonitorNotifier {

    val TAG = "BeaconBackgroundScan"

    private fun bringAppToFront(){
        val intentToReopenApp = Intent(applicationContext, MainActivity::class.java)
        intentToReopenApp.flags = Intent.FLAG_ACTIVITY_NEW_TASK /*+ Intent.FLAG_ACTIVITY_CLEAR_TOP + Intent.FLAG_ACTIVITY_REORDER_TO_FRONT*/
        applicationContext.startActivity(intentToReopenApp)
    }

    override fun onStop() {
        BeaconManager.setDebug(true)
        val beaconManager =  BeaconManager.getInstanceForApplication(this)
        beaconManager.beaconParsers.clear()
        beaconManager.beaconParsers.add(
            BeaconParser().
            setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"))

        beaconManager.isRegionStatePersistenceEnabled = false
        beaconManager.setBackgroundModeInternal(true)
        beaconManager.setEnableScheduledScanJobs(false);
        beaconManager.backgroundBetweenScanPeriod = 100;
        beaconManager.backgroundScanPeriod = 1100;
        Toast.makeText(this@MainActivity, "Scanning for beacon...", Toast.LENGTH_SHORT).show()

        val region = Region("all-beacons-region", Identifier.parse("a32237e7-3ec0-c584-864b-b999f98203f7"), null, null)

        beaconManager.startMonitoring(region)
        beaconManager.addMonitorNotifier(this@MainActivity)
        Log.d(TAG, "Scan started")
        super.onStop()
    }

    override fun onCreate(savedInstanceState: Bundle?)
    {
        super.onCreate(savedInstanceState)

        Toast.makeText(this@MainActivity, "App started", Toast.LENGTH_SHORT).show();

        if (ContextCompat.checkSelfPermission(this@MainActivity,
                Manifest.permission.ACCESS_FINE_LOCATION) !==
            PackageManager.PERMISSION_GRANTED) {
            if (ActivityCompat.shouldShowRequestPermissionRationale(this@MainActivity,
                    Manifest.permission.ACCESS_FINE_LOCATION)) {
                ActivityCompat.requestPermissions(this@MainActivity,
                    arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 1)
            } else {
                ActivityCompat.requestPermissions(this@MainActivity,
                    arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 1)
            }
        }

        if (ContextCompat.checkSelfPermission(this@MainActivity,
                Manifest.permission.ACCESS_BACKGROUND_LOCATION) !==
            PackageManager.PERMISSION_GRANTED) {
            if (ActivityCompat.shouldShowRequestPermissionRationale(this@MainActivity,
                    Manifest.permission.ACCESS_BACKGROUND_LOCATION)) {
                ActivityCompat.requestPermissions(this@MainActivity,
                    arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION), 1)
            } else {
                ActivityCompat.requestPermissions(this@MainActivity,
                    arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION), 1)
            }
        }

    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>,
                                            grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        when (requestCode) {
            1 -> {
                if (grantResults.isNotEmpty() && grantResults[0] ==
                    PackageManager.PERMISSION_GRANTED) {
                    if ((ContextCompat.checkSelfPermission(this@MainActivity,
                            Manifest.permission.ACCESS_FINE_LOCATION) ===
                                PackageManager.PERMISSION_GRANTED) && (ContextCompat.checkSelfPermission(this@MainActivity,
                            Manifest.permission.ACCESS_BACKGROUND_LOCATION) ===
                                PackageManager.PERMISSION_GRANTED)) {
                        Toast.makeText(this, "Permissions Granted", Toast.LENGTH_SHORT).show()
                    }
                } else {
                    Toast.makeText(this, "Permission Denied", Toast.LENGTH_SHORT).show()
                }
                return
            }
        }
    }

    override fun didEnterRegion(region: Region?) {

        this.runOnUiThread {
            Log.d(TAG, "did enter region.")
            bringAppToFront()
            Toast.makeText(this, "Beacon found", Toast.LENGTH_SHORT).show()
        }
    }

    override fun didExitRegion(region: Region?) {
        Log.d(TAG, "did exit region.")
    }

    override fun didDetermineStateForRegion(state: Int, region: Region?) {
        if (state == MonitorNotifier.INSIDE) {
            Log.d(TAG, "Beacon in range")
            val beaconManager =  BeaconManager.getInstanceForApplication(this)
            if (region != null) {
                beaconManager.stopMonitoring(region)
                beaconManager.stopRangingBeacons(region)
            }
        }
        else {
            Log.d(TAG, "Beacon outside of range")
        }
    }
}
davidgyoung commented 2 years ago

@JulianModlinski what you report is expected behavior. Android 8+ forbids background running for more than ~10 minutes. The alternative is to use a foreground service as described here.