Skip to content

IntentScanStrategyCoordinator initialization concurrency #1242

@matgar

Description

@matgar

We are developing an SDK that depends on AltBeacon to detect our trackers (similar to smart tags) nearby. It is expected to run in the background most of the time, so in order to achieve better scan results, I’m testing the intent scan strategy settings:

// By disabling the scan jobs
setEnableScheduledScanJobs(false)

//And enabling intent scan strategy
setIntentScanningStrategyEnabled(true)

This works great: since the OS delivers the results, we don’t need to worry about keeping the process running in background to start a scan and listen for it.

However, we are receiving crash reports related to concurrency issues during the initialization of IntentScanStrategyCoordinator. I suspect this happens because we are calling BeaconManager.startRangingBeacons(region) off the main thread. Some sample stack traces:

Exception hl.e0: lateinit property scanState has not been initialized
  at org.altbeacon.beacon.service.IntentScanStrategyCoordinator.start (IntentScanStrategyCoordinator.kt:118)
  at org.altbeacon.beacon.BeaconManager.bindInternal (BeaconManager.java:457)
  at org.altbeacon.beacon.BeaconManager.autoBind (BeaconManager.java:1986)
  at org.altbeacon.beacon.BeaconManager.startRangingBeacons (BeaconManager.java:1063)
  at com.technopartner.technosdk.execution.scan.InternalAltBeaconReceiver.startRanging (AltBeaconReceiver.kt)
  at com.technopartner.technosdk.execution.scan.InternalAltBeaconReceiver.subscribe (AltBeaconReceiver.kt)
  at com.technopartner.technosdk.execution.scan.InternalAltBeaconReceiver.access$subscribe (AltBeaconReceiver.kt)
  at com.technopartner.technosdk.execution.scan.InternalAltBeaconReceiver$receive$1.invokeSuspend (AltBeaconReceiver.kt)
  at com.technopartner.technosdk.execution.scan.InternalAltBeaconReceiver$receive$1.invoke (AltBeaconReceiver.kt)
  at com.technopartner.technosdk.execution.scan.InternalAltBeaconReceiver$receive$1.invoke (AltBeaconReceiver.kt)
  at kotlinx.coroutines.flow.SafeFlow.collectSafely (Builders.kt:61)
  at kotlinx.coroutines.flow.AbstractFlow.collect (Flow.kt:230)
  at com.technopartner.technosdk.scan.TrackerScanner$startScan$1.invokeSuspend (TrackerScanner.kt)
  at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33)
  at kotlinx.coroutines.DispatchedTask.run (DispatchedTask.kt:108)
  at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely (CoroutineScheduler.kt:584)
  at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask (CoroutineScheduler.kt:793)
  at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker (CoroutineScheduler.kt:697)
  at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run (CoroutineScheduler.kt:684)
Exception hl.e0: lateinit property scanHelper has not been initialized
  at org.altbeacon.beacon.service.IntentScanStrategyCoordinator.processScanResults (IntentScanStrategyCoordinator.kt:176)
  at org.altbeacon.beacon.service.IntentScanStrategyCoordinator.performPeriodicProcessing (IntentScanStrategyCoordinator.kt:181)
  at org.altbeacon.beacon.service.ScanJob$1.run (ScanJob.java:84)
  at java.lang.Thread.run (Thread.java:1012)
Exception java.lang.RuntimeException: Unable to start receiver org.altbeacon.beacon.startup.StartupBroadcastReceiver: java.lang.NullPointerException: Attempt to invoke virtual method 'void ln.g.y()' on a null object reference
  at android.app.ActivityThread.handleReceiver (ActivityThread.java:3612)
  at android.app.ActivityThread.access$1300 (ActivityThread.java:237)
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1796)
  at android.os.Handler.dispatchMessage (Handler.java:106)
  at android.os.Looper.loop (Looper.java:214)
  at android.app.ActivityThread.main (ActivityThread.java:7050)
  at java.lang.reflect.Method.invoke
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:494)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:965)
Caused by java.lang.NullPointerException: Attempt to invoke virtual method 'void ln.g.y()' on a null object reference
  at org.altbeacon.beacon.service.ScanHelper$1.onCycleEnd (ScanHelper.java:313)
  at org.altbeacon.beacon.service.IntentScanStrategyCoordinator.processScanResults (IntentScanStrategyCoordinator.kt:176)
  at org.altbeacon.beacon.startup.StartupBroadcastReceiver.onReceive (StartupBroadcastReceiver.java:59)
  at android.app.ActivityThread.handleReceiver (ActivityThread.java:3603)

Our SDK is initialized in MainApplication.onCreate and starts the BeaconManager on a coroutine context that does not run on the main thread. The concurrency issue seems to occur in IntentScanStrategyCoordinator.ensureInitialized and IntentScanStrategyCoordinator.reinitialize, since they only rely on a boolean flag to verify whether the object is initialized:

fun ensureInitialized() {
    if (!initialized) {
        initialized = true
        scanHelper = ScanHelper(context)
        reinitialize()
    }
}

fun reinitialize() {
  if (!initialized) {
      ensureInitialized() // this will call reinitialize
      return
  }
  // code continues...
}

If two concurrent threads attempt to initialize the coordinator, one sets the flag, while the other skips the entire initialization block (because the flag is already set).

That said, I have some questions regarding this scenario:

  1. Regarding the settings: is it correct to disable scan jobs (setEnableScheduledScanJobs(false)), or can they remain enabled alongside the intent scan strategy?

  2. Do you agree with my analysis, or could the issue be happening elsewhere in the code?

  3. What would you recommend? Should I migrate the BeaconManager initialization back to the main thread (straightforward and maybe the “right” way to initialize AltBeacon), or should I modify the library so that IntentScanStrategyCoordinator initialization is wrapped in a synchronized block (or some other concurrency protection mechanism)?

Current library version: 2.20.6

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions