Search code examples
androidkotlinandroid-jetpack-composebluetooth-lowenergyandroid-jetpack

Why BLE scanning not working for me on Android 13?


I'm using Jetpack Compose to build an app that scans for BLE devices. Here's the composable that simply scans for BLE devices when it enters compostions and stops scanning when exiting. I have already obtained permissions of BLUETOOTH_CONNECT and BLUETOOTH_SCAN. The problem is, when the scan starts, nothing happened. The program didn't crash, but I can't find any BLE devices either. I noticed an error in logcat: "Bad call made by uid 1002. Package "top.frankyang.gc" does not belong to uid 1002.", is this related to the failure? How would I solve it or is there anything wrong in my code? I'm using Android 13 and my program targets Android 12.

// when this Composable enters composition, it's certain that we have the permission
@SuppressLint("MissingPermission")
@Composable
fun BleScanner(adapter: BluetoothAdapter) {
    val scanner = adapter.bluetoothLeScanner
    val devices = remember { mutableStateListOf<BluetoothDevice>() }
    val callback = remember {
        object : ScanCallback() {
            override fun onScanResult(callbackType: Int, result: ScanResult) {
                super.onScanResult(callbackType, result)
                Log.i(TAG, "onScanResult: new device found: ${result.device}")
                devices.add(result.device)
            }

            override fun onBatchScanResults(results: MutableList<ScanResult>) {
                super.onBatchScanResults(results)
                Log.i(TAG, "onBatchScanResults: new devices found: $results")
                results.mapTo(devices) { it.device }
            }

            override fun onScanFailed(errorCode: Int) {
                Log.w(TAG, "onScanFailed: scan failure: errorCode=$errorCode")
            }
        }
    }

    for (device in devices) {
        Text(device.name)
    }
    if (devices.isEmpty()) {
        Text(stringResource(R.string.no_device))
    }

    DisposableEffect(scanner) {
        Log.i(TAG, "BleScanner: start scanning for BLE devices")
        scanner.startScan(callback)
        onDispose {
            Log.i(TAG, "BleScanner: scanning stopped or adapter changed")
            scanner.stopScan(callback)
        }
    }
}
2023-12-29 20:58:42.902 25173-25173 Bt                      top.frankyang.gc                     I  BleScanner: scanning stopped or adapter changed
2023-12-29 20:58:42.903 25173-25173 BluetoothAdapter        top.frankyang.gc                     D  isLeEnabled(): ON
2023-12-29 20:58:42.906 25173-25173 BluetoothAdapter        top.frankyang.gc                     D  isLeEnabled(): ON
2023-12-29 20:58:43.862 25173-25173 Bt                      top.frankyang.gc                     I  BleScanner: start scanning for BLE devices
2023-12-29 20:58:43.862 25173-25173 BluetoothAdapter        top.frankyang.gc                     D  isLeEnabled(): ON
2023-12-29 20:58:43.866 25173-4472  BluetoothLeScanner      top.frankyang.gc                     D  onScannerRegistered() - status=0 scannerId=8 mScannerId=0
2023-12-29 20:58:43.870  2489-4327  AppOps                  system_server                        E  Bad call made by uid 1002. Package "top.frankyang.gc" does not belong to uid 1002.

Solution

  • Add android:usesPermissionFlags="neverForLocation" inside BLUETOOTH_SCAN permission declaration like this

    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />
    

    Otherwise, the scanner will not give the result (or else you can declare location permissions in the Manifest.xml and turn on the device Location).

    Doing so will give the scan result.

    NB:

    1. The text needs to be wrapped with a Column Composable
    2. device.name may lead to NPE