How to Successfully Scan for Bluetooth Android Devices with Low Energy
The Use Case
Scan for all available Android devices supporting BLE, Use any Android device with Android OS starting from 4 to 10 to Scan.
The Problem
1. Successful scanning has inconsistent development requirements over different Android OS2. Official docs are outdated and don’t mention these inconsistencies explicitly, also samples accompanying the docs are using deprecated APIs
Article objectives
This is not a tutorial! this article covers the missing and confusing parts in the Android Developers overview article on Bluetooth Low Energy. It is highly recommended reading.
Technical Solution In Three Steps
1. The first problem I faced was that I needed to make sure that the device’s Bluetooth is not only On but also Visible. That was mentioned explicitly in the documentation but after thorough readings!
Findings: no need to request Bluetooth permission if discoverability is requested, also to stay discoverable infinitely through development set EXTRA_DISCOVERABLE_DURATION to 0, but that was not recommended in real use cases by docs
[pastacode lang="markup" manual="val%20discoverableIntent%20%3DIntent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE)%0AdiscoverableIntent%0A%20%20.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION%2C0%2F*set%20a%20duration%2C%200%20is%20not%20recommended*%2F)%0AstartActivity(discoverableIntent)" message="" highlight="" provider="manual"/]
2. If your app targets Android 6+, you must not only declare ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION in the Manifest, but also request Runtime Permissions in your app, or while in development mode, set your location permissions on for your app explicitly. If you didn’t, it would never be discoverable or even discover other devices.
A- In development mode give permission manually through settings.
[caption id="attachment_70995" align="aligncenter" width="250"]

App permissions settings[/caption]B- or addRuntime Permissionsto activate it momentarily once user asks for scanning[pastacode lang="markup" manual="private%20fun%20allowLocationDetectionPermissions()%20%7B%0A%20%20%20%20%20if%20(ContextCompat.checkSelfPermission(this%40MainActivity%2C%20Manifest.permission.ACCESS_FINE_LOCATION)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%3D%3D%20PackageManager.PERMISSION_DENIED)%20%7B%0A%20%20%20%20%20%20%20%20%20ActivityCompat.requestPermissions(this%40MainActivity%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)%2C%20FINE_LOCATION_PERMISSION_REQUEST)%0A%20%20%20%20%20%7D%0A%7D%0A%0Aoverride%20fun%20onRequestPermissionsResult(requestCode%3A%20Int%2C%20permissions%3A%20Array%3CString%3E%2C%20grantResults%3A%20IntArray)%20%7B%0A%20%20%20%20%20when%20(requestCode)%20%7B%0A%20%20%20%20%20%20%20%20%20FINE_LOCATION_PERMISSION_REQUEST%20-%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20if%20((grantResults.isNotEmpty()%20%26%26%20grantResults%5B0%5D%20%3D%3D%20PackageManager.PERMISSION_GRANTED))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20scanLeDevice(true)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2Fnotify%20the%20user%20to%20allow%20location%20detection%20otherwise%20the%20scaning%20won't%20work%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20return%0A%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%7D%0A%7D" message="" highlight="" provider="manual"/]
3. Please use latest BluetoothLeScanner API to start and end scanning instead of bluetoothAdapter instance, this was the outdated part in the docs
[pastacode lang="markup" manual="private%20fun%20scanLeDevice(enable%3A%20Boolean)%20%7B%0A%20%20%20%20%20%20%20%20when%20(enable)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20true%20-%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Stops%20scanning%20after%20a%20pre-defined%20scan%20period.%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Handler().postDelayed(%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mScanning%20%3D%20false%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20bluetoothAdapter%3F.bluetoothLeScanner%3F.stopScan(mLeScanCallback)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%205000)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mScanning%20%3D%20true%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20bluetoothAdapter%3F.bluetoothLeScanner%3F.startScan(mLeScanCallback)%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20else%20-%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mScanning%20%3D%20false%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20bluetoothAdapter%3F.bluetoothLeScanner%3F.stopScan(mLeScanCallback)%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%7D%0A%0A%20private%20var%20mLeScanCallback%3A%20ScanCallback%20%3D%0A%20%20%20%20%20%20%20%20%20%20%20%20object%20%3A%20ScanCallback()%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20override%20fun%20onScanResult(callbackType%3A%20Int%2C%20result%3A%20ScanResult%3F)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20super.onScanResult(callbackType%2C%20result)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2FDo%20your%20thing%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20override%20fun%20onBatchScanResults(results%3A%20List%3CScanResult%3F%3E%3F)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20super.onBatchScanResults(results)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2FDo%20your%20thing%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20override%20fun%20onScanFailed(errorCode%3A%20Int)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20super.onScanFailed(errorCode)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2FDo%20your%20thing%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D" message="" highlight="" provider="manual"/]
Then voila! All devices from Android 4 to 10 will be scannable in your app.
Please, check the full sample app code here
Related posts
The latest articles from Andela.

How to hire a Python developer: A guide to finding the right fit

Top takeaways from Gartner IT Symposium
.png)
Android ML face detection with Camera X
We have a 96%+
talent match success rate.
The Andela Talent Operating Platform provides transparency to talent profiles and assessment before hiring. AI-driven algorithms match the right talent for the job.
