Codename One Bluetooth LE Scan Fix for Android
Fix Bluetooth LE scan issues in Codename One on Android with proper permissions configuration and implementation. Works for iOS too.
How to fix Codename One Bluetooth LE extension scan not starting on Android? evt1.getSource() not executing and missing hasPermissionBtScan method
I have installed the Codename One Bluetooth extension and am following the demo example to scan for Bluetooth devices, but the “start scan” button does not work.
I’m building for Android using IntelliJ IDEA (also need iOS support). Here’s the problematic code:
hi.add(new Button(new Command("start scan") {
@Override
public void actionPerformed(ActionEvent evt) {
updateAction("start scan");
try {
updateAction("Permessi: " + bt.hasPermission());
updateAction("Location Service: " + bt.isLocationEnabled());
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
bt.startScan((ActionListener) evt1 -> {
try {
JSONObject res = (JSONObject) evt1.getSource();
System.out.println("response " + res);
updateAction("response " + res);
if (res.getString("status").equals("scanResult")) {
if (!devices.containsKey(res.getString("address"))) {
devices.put(res.getString("address"), res);
updateAction("Dispositivi: " + devices.size());
updateUI();
}
}
} catch (JSONException ex) {
Log.e(ex);
}
}, null, true, Bluetooth.SCAN_MODE_LOW_POWER, Bluetooth.MATCH_MODE_STICKY,
Bluetooth.MATCH_NUM_MAX_ADVERTISEMENT, Bluetooth.CALLBACK_TYPE_ALL_MATCHES);
} catch (IOException ex) {
Log.e(ex);
}
}
}));
The code fails at evt1.getSource() and does not execute the scan callback.
For Android API >= 31, documentation mentions needing BLUETOOTH_SCAN permission and methods like hasPermissionBtScan() or requestPermissionBtScan(), but these methods do not exist in the API.
Goal: Retrieve MAC addresses or UUIDs of nearby Bluetooth LE devices. Likely missing correct permissions or configuration. How to resolve this for Android and iOS?
Codename One Bluetooth LE scan issues on Android are typically caused by missing permissions for API 31+ and improper permission checking before initiating scans. The hasPermissionBtScan() method you mentioned doesn’t exist in the API - instead, you should use the existing hasPermission() method combined with proper Android manifest configuration for Bluetooth permissions.
Contents
- Understanding the Issue
- Android Permissions Configuration
- Proper Permission Checking
- Scan Implementation
- iOS Considerations
- Troubleshooting
Understanding the Issue
The Bluetooth LE scan not starting on Android is primarily a permissions problem, especially for devices running Android 12 (API level 31) or higher. When you call bt.startScan(), the system needs proper permissions to access Bluetooth Low Energy devices. The callback evt1.getSource() not executing indicates that the scan is not being initiated successfully due to missing or denied permissions.
Codename One’s Bluetooth LE extension relies on the underlying Android Bluetooth stack, which has become more restrictive with newer Android versions. For Android API 31+, Google introduced new granular Bluetooth permissions that must be explicitly requested by your application.
Android Permissions Configuration
To fix the Bluetooth LE scan issue, you need to add the appropriate permissions to your Android manifest. These permissions should be added as build hints in your Codename One project:
android.xapplication = android:usesPermission android:name="android.permission.BLUETOOTH_SCAN"
android.xapplication += android:usesPermission android:name="android.permission.BLUETOOTH_CONNECT"
android.xapplication += android:usesPermission android:name="android.permission.ACCESS_FINE_LOCATION"
android.xapplication += android:usesPermission android:name="android.permission.BLUETOOTH"
android.xapplication += android:usesPermission android:name="android.permission.BLUETOOTH_ADMIN"
For Android 12 and above, the BLUETOOTH_SCAN permission is particularly important. According to the official Android documentation, this permission is required for apps that look for Bluetooth devices, including BLE peripherals.
Proper Permission Checking
Instead of calling hasPermissionBtScan() (which doesn’t exist), you should use the existing bt.hasPermission() method to check if permissions are granted. Here’s the correct approach:
// First, check if Bluetooth is enabled
if (!bt.isEnabled()) {
bt.enable(new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
if (evt.getType() == Bluetooth.EVENT_STATE_CHANGED &&
bt.getState() == Bluetooth.STATE_ON) {
// Bluetooth is now enabled, proceed with permission check
checkAndRequestPermissions();
}
}
});
} else {
// Bluetooth is already enabled, check permissions
checkAndRequestPermissions();
}
private void checkAndRequestPermissions() {
try {
if (!bt.hasPermission()) {
updateAction("Requesting Bluetooth permissions...");
bt.requestPermission(new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
if (evt.getType() == Bluetooth.EVENT_PERMISSIONS_GRANTED) {
updateAction("Permissions granted, starting scan...");
startBluetoothScan();
} else {
updateAction("Permissions denied, cannot start scan");
}
}
});
} else {
updateAction("Permissions already granted, starting scan...");
startBluetoothScan();
}
} catch (IOException e) {
Log.e(e);
updateAction("Error checking permissions: " + e.getMessage());
}
}
private void startBluetoothScan() {
bt.startScan((ActionListener) evt1 -> {
try {
JSONObject res = (JSONObject) evt1.getSource();
System.out.println("response " + res);
updateAction("response " + res);
if (res.getString("status").equals("scanResult")) {
if (!devices.containsKey(res.getString("address"))) {
devices.put(res.getString("address"), res);
updateAction("Dispositivi: " + devices.size());
updateUI();
}
}
} catch (JSONException ex) {
Log.e(ex);
}
}, null, true, Bluetooth.SCAN_MODE_LOW_POWER, Bluetooth.MATCH_MODE_STICKY,
Bluetooth.MATCH_NUM_MAX_ADVERTISEMENT, Bluetooth.CALLBACK_TYPE_ALL_MATCHES);
}
The Codename One Bluetooth LE library documentation shows that for Android API ≥ 31, bt.requestPermission() will automatically request the new BLE permissions (BLUETOOTH_SCAN, BLUETOOTH_CONNECT, BLUETOOTH_ADVERTISE).
Scan Implementation
When implementing the scan functionality, ensure you follow these best practices:
- Check Bluetooth state: Always verify that Bluetooth is enabled before attempting to scan
- Handle permissions properly: Request permissions if they’re not already granted
- Provide user feedback: Update the UI to inform users about the scanning process
- Clean up properly: Stop the scan when it’s no longer needed
Here’s a complete implementation pattern:
private void initializeBluetoothScan() {
Button scanButton = new Button(new Command("start scan") {
@Override
public void actionPerformed(ActionEvent evt) {
if (isScanning) {
stopBluetoothScan();
} else {
startBluetoothScan();
}
}
});
hi.add(scanButton);
}
private void startBluetoothScan() {
try {
if (!bt.isEnabled()) {
updateAction("Please enable Bluetooth first");
return;
}
if (!bt.hasPermission()) {
updateAction("Requesting Bluetooth permissions...");
bt.requestPermission(new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
if (evt.getType() == Bluetooth.EVENT_PERMISSIONS_GRANTED) {
updateAction("Permissions granted, starting scan...");
proceedWithScan();
} else {
updateAction("Permissions denied, cannot start scan");
}
}
});
} else {
proceedWithScan();
}
} catch (IOException e) {
Log.e(e);
updateAction("Error starting scan: " + e.getMessage());
}
}
private void proceedWithScan() {
updateAction("Starting Bluetooth LE scan...");
isScanning = true;
bt.startScan((ActionListener) evt1 -> {
try {
JSONObject res = (JSONObject) evt1.getSource();
System.out.println("Scan result: " + res);
updateAction("Found device: " + res.getString("name") + " - " + res.getString("address"));
if (res.getString("status").equals("scanResult")) {
String address = res.getString("address");
if (!devices.containsKey(address)) {
devices.put(address, res);
updateAction("Dispositivi: " + devices.size());
updateUI();
}
}
} catch (JSONException ex) {
Log.e(ex);
}
}, null, true, Bluetooth.SCAN_MODE_LOW_POWER, Bluetooth.MATCH_MODE_STICKY,
Bluetooth.MATCH_NUM_MAX_ADVERTISEMENT, Bluetooth.CALLBACK_TYPE_ALL_MATCHES);
}
private void stopBluetoothScan() {
try {
bt.stopScan();
isScanning = false;
updateAction("Scan stopped");
} catch (IOException e) {
Log.e(e);
updateAction("Error stopping scan: " + e.getMessage());
}
}
iOS Considerations
When developing for iOS, keep in mind that iOS has its own set of Bluetooth requirements:
- Info.plist configuration: Add Bluetooth permissions to your iOS build hints
- Always authorization: iOS requires “Always” location permission for Bluetooth scanning
- Background modes: Enable “Uses Bluetooth LE accessories” if scanning in background
iOS build hints example:
ios.info.plist = NSBluetoothAlwaysUsageDescription = "This app needs Bluetooth access to find nearby devices"
ios.info.plist += NSLocationWhenInUseUsageDescription = "This app needs location access to find nearby Bluetooth devices"
ios.info.plist += NSLocationAlwaysAndWhenInUseUsageDescription = "This app needs location access to find nearby Bluetooth devices"
As noted in community discussions, there were some issues with the builds of both the Bluetooth cn1lib and the CN1JSON cn1lib that have been fixed. You may need to update the library via Codename One preferences for these fixes to take effect.
Troubleshooting
If you’re still experiencing issues with Bluetooth LE scanning on Android, consider these troubleshooting steps:
- Update libraries: Ensure you have the latest version of the Codename One Bluetooth LE extension
- Check Android version: Verify your app’s target and minimum Android API levels
- Test on real device: Bluetooth scanning may not work on emulators
- Check location services: Ensure location services are enabled on the device
- Review manifest permissions: Double-check that all required permissions are properly declared
For iOS-specific issues, remember that the original code from which the BluetoothLE was forked shows different parameters for iOS than Android. According to community discussions, you may need to adjust scan parameters for iOS compatibility.
Sources
- Android Bluetooth Permissions Documentation
- Codename One Bluetooth LE Library
- Community-maintained Bluetooth LE Library
- iOS Bluetooth Scanning Discussion
- iOS Bluetooth LE Parameters
Conclusion
To resolve Codename One Bluetooth LE scan issues on Android, you need to properly configure Android permissions for API 31+ and implement proper permission checking before starting scans. The hasPermissionBtScan() method doesn’t exist in the API - use bt.hasPermission() instead. Always verify Bluetooth is enabled, request runtime permissions if needed, and provide clear user feedback throughout the process. With the correct permissions configuration and implementation, your Bluetooth LE scanning should work reliably on both Android and iOS platforms.