expo-beacon 0.6.7 → 0.6.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -76,29 +76,31 @@ class BeaconForegroundService : Service(), BeaconConsumer {
76
76
  // Increase AltBeacon's region exit period so didExitRegion doesn't fire
77
77
  // prematurely during brief BLE scan gaps.
78
78
  BeaconManager.setRegionExitPeriod(REGION_EXIT_PERIOD_MS)
79
- // Let AltBeacon's internal BeaconService run as a foreground service so
80
- // Samsung/OEM battery enforcement doesn't kill BLE scanning after ~4 min.
81
- // Reset first to clear any stale state from a previous session.
82
- try { beaconManager.disableForegroundServiceScanning() } catch (_: Exception) {}
83
- try {
84
- beaconManager.enableForegroundServiceScanning(
85
- buildForegroundNotification(), FOREGROUND_NOTIF_ID
86
- )
87
- } catch (e: IllegalStateException) {
88
- // Already bound by another consumer (e.g. a scan in progress) — non-fatal.
89
- Log.w(TAG, "enableForegroundServiceScanning skipped (already bound)", e)
90
- }
79
+ // NOTE: We intentionally do NOT call enableForegroundServiceScanning().
80
+ // Our BeaconForegroundService is already a foreground service (startForeground
81
+ // in onStartCommand). AltBeacon's internal BeaconService runs in the same
82
+ // process and inherits the elevated priority. Calling enable/disable on the
83
+ // shared singleton causes crashes when the ExpoBeaconModule has an active
84
+ // scan bound to the same BeaconManager.
91
85
  }
92
86
 
93
87
  override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
94
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
95
- startForeground(
96
- FOREGROUND_NOTIF_ID,
97
- buildForegroundNotification(),
98
- ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE
99
- )
100
- } else {
101
- startForeground(FOREGROUND_NOTIF_ID, buildForegroundNotification())
88
+ try {
89
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
90
+ startForeground(
91
+ FOREGROUND_NOTIF_ID,
92
+ buildForegroundNotification(),
93
+ ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE
94
+ )
95
+ } else {
96
+ startForeground(FOREGROUND_NOTIF_ID, buildForegroundNotification())
97
+ }
98
+ } catch (e: Exception) {
99
+ // SecurityException on Android 14+ if BT permissions missing,
100
+ // or other platform-specific issues. Stop gracefully instead of crashing.
101
+ Log.e(TAG, "startForeground failed — stopping service", e)
102
+ stopSelf()
103
+ return START_NOT_STICKY
102
104
  }
103
105
  if (serviceConnected) {
104
106
  // Already bound from a prior onStartCommand — reload regions directly
@@ -590,6 +592,7 @@ class BeaconForegroundService : Service(), BeaconConsumer {
590
592
  fun start(context: Context) {
591
593
  context.getSharedPreferences(PREF_IS_MONITORING, Context.MODE_PRIVATE)
592
594
  .edit().putBoolean("active", true).apply()
595
+ ensureNotificationChannel(context)
593
596
  val intent = Intent(context, BeaconForegroundService::class.java)
594
597
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
595
598
  context.startForegroundService(intent)
@@ -700,7 +703,6 @@ class BeaconForegroundService : Service(), BeaconConsumer {
700
703
  monitoredRegions.forEach {
701
704
  try { beaconManager.stopMonitoringBeaconsInRegion(it) } catch (_: RemoteException) {}
702
705
  }
703
- try { beaconManager.disableForegroundServiceScanning() } catch (_: Exception) {}
704
706
  beaconManager.unbind(this)
705
707
  super.onDestroy()
706
708
  }
@@ -327,9 +327,25 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
327
327
  promise.reject("PERMISSION_DENIED", "Location permissions required for background monitoring. Call requestPermissionsAsync() first.", null)
328
328
  return@AsyncFunction
329
329
  }
330
+ // Android 12+ requires BLUETOOTH_SCAN for BLE operations;
331
+ // Android 14+ additionally requires it for connectedDevice foreground services.
332
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
333
+ val hasBtScan = ContextCompat.checkSelfPermission(ctx, Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED
334
+ val hasBtConnect = ContextCompat.checkSelfPermission(ctx, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED
335
+ if (!hasBtScan || !hasBtConnect) {
336
+ promise.reject("PERMISSION_DENIED", "Bluetooth permissions required for beacon monitoring. Call requestPermissionsAsync() first.", null)
337
+ return@AsyncFunction
338
+ }
339
+ }
330
340
 
331
341
  registerEventReceiver()
332
- BeaconForegroundService.start(ctx)
342
+ try {
343
+ BeaconForegroundService.start(ctx)
344
+ } catch (e: Exception) {
345
+ unregisterEventReceiver()
346
+ promise.reject("SERVICE_START_FAILED", "Failed to start monitoring service: ${e.message}", e)
347
+ return@AsyncFunction
348
+ }
333
349
  promise.resolve(null)
334
350
  }
335
351
 
@@ -345,7 +361,6 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
345
361
  return@AsyncFunction
346
362
  }
347
363
  BeaconForegroundService.stop(ctx)
348
- try { beaconManager.disableForegroundServiceScanning() } catch (_: Exception) {}
349
364
  unregisterEventReceiver()
350
365
  promise.resolve(null)
351
366
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-beacon",
3
- "version": "0.6.7",
3
+ "version": "0.6.8",
4
4
  "description": "Expo module for scanning, pairing, and monitoring iBeacons on Android and iOS",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",