I copy-pasted some code from the Find BLE devices documentation, but I am getting this error:
/home/heller/DeliveryBox/MobileApp/DeliveryBox_Android/app/src/main/java/com/deepsoft/deliverybox/BluetoothManager.kt:55: Error: Call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with checkPermission) or explicitly handle a potential SecurityException [MissingPermission]
bluetoothLeScanner.stopScan(leScanCallback)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/heller/DeliveryBox/MobileApp/DeliveryBox_Android/app/src/main/java/com/deepsoft/deliverybox/BluetoothManager.kt:69: Error: Call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with checkPermission) or explicitly handle a potential SecurityException [MissingPermission]
bluetoothLeScanner.stopScan(leScanCallback)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Here is the code file (BluetoothManager.kt):
/****************************************************************************
*
* Copyright (C) 2026 SEE FILE LICENSE.
*
* Synopsis: Bluetooth Manager code
* Authors: Robert Heller
*
****************************************************************************/
package com.deepsoft.deliverybox
import java.util.UUID
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothManager
import android.bluetooth.le.ScanFilter
import android.bluetooth.le.ScanSettings
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanResult
import android.content.Context
import android.content.IntentFilter
import android.os.Build
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import android.os.Handler
import android.os.ParcelUuid
class BluetoothConnectionManager(private val context: Context) {
private val ServiceUUID = UUID.fromString("3a6089ff-731f-4312-9761-6ecfa14b867e")
private val WIFICharacteristicUUID = UUID.fromString("00e19a26-da6e-4ad2-896c-b5f0cbe04e43")
private val MasterCodeCharacteristicUUID = UUID.fromString("fb90b3be-e21c-4aca-ae29-c05a0c3992e3")
private val OneTimeCodeCharacteristicUUID = UUID.fromString("3d32154a-50f1-4e7d-883e-2b95ad680d3f")
private val RestartCharacteristicUUID = UUID.fromString("2f1c6d70-9d97-47d7-8c9b-cb0b96bddea6")
private val bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
private val bluetoothAdapter: BluetoothAdapter? = bluetoothManager.adapter
private var scanning = false
private val bluetoothLeScanner = bluetoothAdapter?.getBluetoothLeScanner ()
private val handler = Handler()
// Stops scanning after 10 seconds.
private val SCAN_PERIOD: Long = 10000
private fun scanLeDevice() {
if (!isBluetoothSupported()) return
if (bluetoothLeScanner == null) {
return
}
if (!scanning) { // Stops scanning after a pre-defined scan period.
handler.postDelayed({
scanning = false
bluetoothLeScanner.stopScan(leScanCallback)
}, SCAN_PERIOD)
scanning = true
bluetoothLeScanner.startScan(listOf(ScanFilter.Builder()
.setServiceData(ParcelUuid(ServiceUUID),
null,
null).build()),
ScanSettings.Builder()
.setNumOfMatches(ScanSettings.
MATCH_NUM_ONE_ADVERTISEMENT)
.build(),
leScanCallback)
} else {
scanning = false
bluetoothLeScanner.stopScan(leScanCallback)
}
}
// Device scan callback.
private val leScanCallback: ScanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
super.onScanResult(callbackType, result)
//
}
}
fun isBluetoothSupported(): Boolean = bluetoothAdapter != null
fun isBluetoothEnabled(): Boolean = bluetoothAdapter?.isEnabled == true
fun initialize() {
if (isBluetoothSupported() && isBluetoothEnabled())
{
scanLeDevice()
}
}
fun writeWiFiCharacteristic(ssid: String, password: String)
{
}
fun writeMasterCodeCharacteristic(masterCode: String)
{
}
fun writeOneTimeCodeCharacteristic(oneTimeCode: String)
{
}
fun writeRebootCharacteristic()
{
}
}
sealed class ConnectionState {
object Disconnected : ConnectionState()
object Connecting : ConnectionState()
data class Connected(val device: BluetoothDevice) : ConnectionState()
data class Error(val message: String) : ConnectionState()
}
Note: I do have permission checking in the MainActivity.kt file.
Do I have to repeat the permission checks in the helper class? Is the Lint check really that stupid? Or is it that I need ALL of the code in one file or in one class?
I have added the full code on GitHub here.
OK, I wrapped the whole if (!scanning) { ... } else {...} block in a try {...} catch, and lint is now happy. I don't think there will ever by an exception because I have permission checking in the outer code.
Robert Heller