Alvaro Vargas
Lurker
So, another issue came up when doing more tests on an app project I have been working on for a while now. I stumbled upon this issue while making an app that gathers data from surrounding Bluetooth devices to later on connect to the correct one (a custom embedded system using Bluetooth Classic, not Low Energy).
I tried the same app on two different phones (with different android versions), one has Android 5.1.1 and the other has Android 9; taking into consideration their respective permissions and restrictions.
This time, I was specifically trying to collect the UUIDs from my surrounding devices. To be clear, getting the devices' names and their respective MAC addresses was no sweat, and everything shows up perfectly on different phones. But UUIDs are these little beasts annoying me a lot, and it seems like Android Studio's API for fetching UUIDs is flawed and acts randomly and here is why:
To simplify things, I made another very simple app that gathers the UUIDs from a known device, meaning that I hard coded the MAC address I want to fetch UUIDs from.
I created a Button to start fetching by calling this piece of code:
Now, there is a broadcast receiver that shows me when an ACL connection is made, since a low level connection is done in order to get the UUIDs from the wanted device:
After the low level connection is made, the UUIDs are fetched and caught on another broadcast receiver:
Now, the supposed order in which things are supposed to happen is:
I can confirm this is not a device specific issue since it happens on different phones and when fetching UUIDs from other devices (Bluetooth keyboards, other phones, headsets, etc), I can also confirm that it is not just a lag on the Logs because on the more complete app I am working on, flags related to ACL connections and disconnections don't get triggered at all or don't get triggered in the order they are supposed to happen.
So... after this long post... what is going on in here?
I tried the same app on two different phones (with different android versions), one has Android 5.1.1 and the other has Android 9; taking into consideration their respective permissions and restrictions.
This time, I was specifically trying to collect the UUIDs from my surrounding devices. To be clear, getting the devices' names and their respective MAC addresses was no sweat, and everything shows up perfectly on different phones. But UUIDs are these little beasts annoying me a lot, and it seems like Android Studio's API for fetching UUIDs is flawed and acts randomly and here is why:
To simplify things, I made another very simple app that gathers the UUIDs from a known device, meaning that I hard coded the MAC address I want to fetch UUIDs from.
I created a Button to start fetching by calling this piece of code:
Java:
isFetchUUIDSButtonPressed = true;
bluetoothDiscoveredUUID.clear();
String UUID = "00:00:00:00:00:00"; // MAC ADDRESS IS OBVIOUSLY SOMETHING DIFFERENT, BUT I AM NOT SHOWING IT HERE.
BluetoothManager bluetoothManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothDevice mBluetoothDevice = bluetoothManager.getAdapter().getRemoteDevice(UUID);
Log.w(TAG, "BEFORE Check if fetch succeeded: " + checkIfUUIDFetchSucceeded);
checkIfUUIDFetchSucceeded = mBluetoothDevice.fetchUuidsWithSdp();
Log.w(TAG, "AFTER Check if fetch succeeded: " + checkIfUUIDFetchSucceeded);
checkIfUUIDFetchSucceeded = false;
Now, there is a broadcast receiver that shows me when an ACL connection is made, since a low level connection is done in order to get the UUIDs from the wanted device:
Java:
////////////////////////////// BLUETOOTH IN GENERAL BROADCAST RECEIVER //////////////////////////////
BroadcastReceiver bluetoothBroadcastReceiver = new BroadcastReceiver(){
@Override
public void onReceive(Context context, Intent intent){
String action = intent.getAction();
if(BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)){
//STATE_OFF = 10;
//STATE_TURNING_ON = 11;
//STATE_ON = 12;
//STATE_TURNING_OFF = 13;
int previousBluetoothState = intent.getIntExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, BluetoothAdapter.ERROR);
int currentBluetoothState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
Log.w(TAG,"Previous Bluetooth Adapter State: " + previousBluetoothState);
Log.w(TAG,"Current Bluetooth Adapter State: " + currentBluetoothState);
if( previousBluetoothState == BluetoothAdapter.STATE_ON ){
//TODO: CANCEL CURRENT OPERATIONS, RESET ALL VARIABLES, LISTVIEWS, BUTTONS, etc, TO THEIR INITIAL STATES.
// this is to guarantee a fresh restart of operations after the Bluetooth adapter is turned ON again.
Log.w(TAG, "Bluetooth Turning Off: RESETTING ALL FIELDS AND VIEWS TO THEIR INITIAL STATES");
resetFields();
}
}
////////////////////////////// CONNECTION ESTABLISHED //////////////////////////////
else if(BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)){
Log.w(TAG, "Provisional Connection to Retrieve UUID");
}
////////////////////////////// DISCONNECTION VERIFIED //////////////////////////////
else if(BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)){
Log.w(TAG,"ACTION_ACL_DISCONNECTED: Provisional connection finished!");
Log.w(TAG,"ACTION_ACL_DISCONNECTED: Disconnected from device");
}
}
};
After the low level connection is made, the UUIDs are fetched and caught on another broadcast receiver:
Java:
////////////////////////////// UUIDs BROADCAST RECEIVER //////////////////////////////
private final BroadcastReceiver UUIDBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if( BluetoothDevice.ACTION_UUID.equals(action) && isFetchUUIDSButtonPressed){
Parcelable[] uuids = intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID);
Log.e(TAG,"FROM ACTION_UUID");
bluetoothDiscoveredUUID.add(new ArrayList<UUID>());
if (uuids != null) {
for (Parcelable ep : uuids) {
Log.e(TAG, "UUID = " + ep.toString());
bluetoothDiscoveredUUID.get((bluetoothDiscoveredUUID.size()) - 1).add(UUID.fromString(ep.toString()));
}
}
else{
bluetoothDiscoveredUUID.get((bluetoothDiscoveredUUID.size()) - 1).add(nullUUID);
Log.e(TAG, "Null UUID");
}
Log.w(TAG, "UUID data acquired");
isFetchUUIDSButtonPressed = false;
}
}
};
Now, the supposed order in which things are supposed to happen is:
- A fetching command is sent
- An ACL connection is made to retrieve the UUIDs from the device
- UUIDs are retrieved
- ACL connection is terminated.
- UUID broadcast receiver gets the UUIDs stored in cache if the device was previously discovered during the same session. After the Bluetooth adapter is turned off, all devices discovered during that session that were stored in cache are now erased, so, when turning on the Bluetooth adapter and trying to get the UUID from another device that is now turned off, the value fetched inside the broadcast receiver is now null.
- When fetching UUIDs from an already searched device, the order in which things are supposed to happen, happen randomly or don't happen at all.
- There is not always a provisional ACL connection to retrieve the UUIDs from the device.
- An ACL connection and disconnection is performed even after cached UUIDs are fetched.
Java:
D/ViewRootImpl: ViewPostImeInputStage ACTION_DOWN
W/MainActivity: BEFORE Check if fetch succeeded: false
W/MainActivity: AFTER Check if fetch succeeded: true
E/MainActivity: FROM ACTION_UUID
Null UUID -------------------------------------->>>> //Other device is off and no UUIDs stored in cache
W/MainActivity: UUID data acquired
D/ViewRootImpl: ViewPostImeInputStage ACTION_DOWN
W/MainActivity: BEFORE Check if fetch succeeded: false
W/MainActivity: AFTER Check if fetch succeeded: true
W/MainActivity: Provisional Connection to Retrieve UUID!
E/MainActivity: FROM ACTION_UUID
UUID = 00001101-0000-1000-8000-00805f9b34fb
UUID = 00000000-0000-1000-8000-00805f9b34fb
W/MainActivity: UUID data acquired
W/MainActivity: ACTION_ACL_DISCONNECTED: Provisional connection finished!
ACTION_ACL_DISCONNECTED: Disconnected from device
D/ViewRootImpl: ViewPostImeInputStage ACTION_DOWN
W/MainActivity: BEFORE Check if fetch succeeded: false
W/MainActivity: AFTER Check if fetch succeeded: true
E/MainActivity: FROM ACTION_UUID
UUID = 00001101-0000-1000-8000-00805f9b34fb
UUID = 00000000-0000-1000-8000-00805f9b34fb
W/MainActivity: UUID data acquired
D/ViewRootImpl: ViewPostImeInputStage ACTION_DOWN
W/MainActivity: BEFORE Check if fetch succeeded: false
W/MainActivity: AFTER Check if fetch succeeded: true
E/MainActivity: FROM ACTION_UUID
UUID = 00001101-0000-1000-8000-00805f9b34fb
UUID = 00000000-0000-1000-8000-00805f9b34fb
W/MainActivity: UUID data acquired
W/MainActivity: Provisional Connection to Retrieve UUID!
W/MainActivity: ACTION_ACL_DISCONNECTED: Provisional connection finished!
ACTION_ACL_DISCONNECTED: Disconnected from device
I can confirm this is not a device specific issue since it happens on different phones and when fetching UUIDs from other devices (Bluetooth keyboards, other phones, headsets, etc), I can also confirm that it is not just a lag on the Logs because on the more complete app I am working on, flags related to ACL connections and disconnections don't get triggered at all or don't get triggered in the order they are supposed to happen.
So... after this long post... what is going on in here?