25

As I'm currently working on a little bluetooth library for Android, I'm trying to get all the service uuids of the devices I discovered in my surrounding.

When my broadcast receiver gets the BluetoothDevice.ACTION_FOUND intent, I'm extracting the device and call:

device.fetchUuidsWithSdp();

This will result BluetoothDevice.ACTION_UUID intents for each device found and I'm handling them with the same receiver:

BluetoothDevice d = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Parcelable[] uuidExtra = intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID);

if(uuidExtra ==  null) {
    Log.e(TAG, "UUID = null");
}

if(d != null && uuidExtra != null)
    Log.d(TAG, d.getName() + ": " + uuidExtra.toString());

The thing is, that uuidExtra is always null.

How can i get all the UUIDs of the surrounding devices?

EDIT:

Im working on a Nexus 7. I tried code i found on the internet and this also gives me a NullPointerException: http://digitalhacksblog.blogspot.de/2012/05/android-example-bluetooth-discover-and.html

Thank you.

tellob
  • 1,220
  • 3
  • 16
  • 32

6 Answers6

15

The documentation on this states...

Always contains the extra field BluetoothDevice.EXTRA_UUID

However, just like you, I have found this not to be true.

If you call fetchUuidsWithSdp() while device discovery is still taking place BluetoothDevice.EXTRA_UUID can be null.

You should wait until you receive BluetoothAdapter.ACTION_DISCOVERY_FINISHED before you make any calls to fetchUuidsWithSdp().

Eddie
  • 690
  • 10
  • 27
  • 4
    I get the same problem, even if i wait for device discovery to finish before calling `fetchUuidsWithSdp()`. – Kevin May 13 '13 at 01:15
  • This works for me in some cases. When discovering devices from my Samsung Galaxy Nexus 2, I can only receive the services on my HTC One (M7), not from my MacBook Pro (then the result is null). – mfb Dec 20 '14 at 00:43
  • EDIT: it seems completely random if `fetchUuidsWithSdp()` works or not, no matter at which point in the process I call it. – mfb Dec 20 '14 at 00:57
  • This works like charm. For the scenarios where we feel the behavior as random, see my answer below. – Arunkumar May 06 '16 at 10:32
  • ACTION_DISCOVERY_FINISHED is never called even if I added `filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);` – user924 Jun 23 '18 at 08:17
12

NOTE: This solution applies to CLASSIC bluetooth and not BLE. For BLE check how to send manufacturer specific Data in advertiser on the peripheral side

The problem with fetching Uuids is that you have only one bluetooth adapter, and we cannot have parallel api calls which uses adapter for its purpose.

As Eddie pointed out, wait for BluetoothAdapter.ACTION_DISCOVERY_FINISHED and then call fetchUuidsWithSdp().

Still this cannot guarantee uuids to be fetched for all devices. In addition to this one must wait for each subsequent call to fetchuuidsWithSdp() to complete, and then give a call to this method for another device.

See the code below --

ArrayList<BluetoothDevice> mDeviceList = new ArrayList<BluetoothDevice>();

private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
            BluetoothDevice device = (BluetoothDevice) intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            mDeviceList.add(device);
        } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
            // discovery has finished, give a call to fetchUuidsWithSdp on first device in list.
            if (!mDeviceList.isEmpty()) {
                BluetoothDevice device = mDeviceList.remove(0);
                boolean result = device.fetchUuidsWithSdp();
            }
        } else if (BluetoothDevice.ACTION_UUID.equals(action)) {
            // This is when we can be assured that fetchUuidsWithSdp has completed.
            // So get the uuids and call fetchUuidsWithSdp on another device in list

            BluetoothDevice deviceExtra = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            Parcelable[] uuidExtra = intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID);
            System.out.println("DeviceExtra address - " + deviceExtra.getAddress());
            if (uuidExtra != null) {
                for (Parcelable p : uuidExtra) {
                    System.out.println("uuidExtra - " + p);
                }
            } else {
                System.out.println("uuidExtra is still null");
            }
            if (!mDeviceList.isEmpty()) {
                BluetoothDevice device = mDeviceList.remove(0);
                boolean result = device.fetchUuidsWithSdp();
            }
        }
    }
}

UPDATE: Latest android versions (mm & above) would result in triggering a pairing process with each device

Arunkumar
  • 3,812
  • 3
  • 22
  • 30
  • Interesting I still get null though :( – Sebastien FERRAND Apr 06 '17 at 12:25
  • 2
    Make sure to register the intents. – Eugene K May 02 '17 at 23:20
  • Which action triggers the pairing process? FetchUuidWithSdp()? If so I assume that is because a connection needs to be established? If that is true, why is the pairing process only triggered in MM and up? – Brian Reinhold May 28 '18 at 11:51
  • Yes. The reason is that security is being tightened going forward in newer versions of Android (mostly after MM). Plus a special care is being given to drop BT connection if there is no active data transfer. – Arunkumar May 30 '18 at 07:22
3

Here is a good example of how to get UUIDs of service characteristics from a service that I did for getting heart rate devices:

private class HeartRateBluetoothGattCallback extends BluetoothGattCallback {

    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {         
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            logMessage("CONNECTED TO " + gatt.getDevice().getName(), false, false);
            gatt.discoverServices();    
        } else if(newState == BluetoothProfile.STATE_DISCONNECTED) {
            logMessage("DISCONNECTED FROM " + gatt.getDevice().getName(), false, false);
            if(mIsTrackingHeartRate)
                handleHeartRateDeviceDisconnection(gatt);
        } 
    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            logMessage("DISCOVERING SERVICES FOR " + gatt.getDevice().getName(), false, false);

            if(mDesiredHeartRateDevice != null && 
                    gatt.getDevice().getAddress().equals(mDesiredHeartRateDevice.getBLEDeviceAddress())) {

                if(subscribeToHeartRateGattServices(gatt)) {

                    mIsTrackingHeartRate = true;
                    setDeviceScanned(getDiscoveredBLEDevice(gatt.getDevice().getAddress()), DiscoveredBLEDevice.CONNECTED);
                    broadcastHeartRateDeviceConnected(gatt.getDevice());

                } else
                    broadcastHeartRateDeviceFailedConnection(gatt.getDevice());

            } else {
                parseGattServices(gatt);
                disconnectGatt(getDiscoveredBLEDevice(gatt.getDevice().getAddress()));
            }
        }   
    }

    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
        if(characteristic.getUuid().equals(UUID.fromString(HEART_RATE_VALUE_CHAR_READ_ID))) {
            int flag = characteristic.getProperties();
            int format = -1;

            if ((flag & 0x01) != 0) 
                format = BluetoothGattCharacteristic.FORMAT_UINT16;
            else 
                format = BluetoothGattCharacteristic.FORMAT_UINT8;

            Integer heartRateValue = characteristic.getIntValue(format, 1);
            if(heartRateValue != null)
                broadcastHeartRateValue(heartRateValue);
            else
                Log.w(SERVICE_NAME, "UNABLE TO FORMAT HEART RATE DATA");
        }
    };

};

private void parseGattServices(BluetoothGatt gatt) {
    boolean isHeartRate = false;
    for(BluetoothGattService blueToothGattService : gatt.getServices()) {
        logMessage("GATT SERVICE: " + blueToothGattService.getUuid().toString(), false, false);
        if(blueToothGattService.getUuid().toString().contains(HEART_RATE_DEVICE_SERVICE_CHARACTERISTIC_PREFIX))
            isHeartRate = true;
    }   

    if(isHeartRate) {
        setDeviceScanned(getDiscoveredBLEDevice(gatt.getDevice().getAddress()), DiscoveredBLEDevice.IS_HEART_RATE);
        broadcastHeartRateDeviceFound(getDiscoveredBLEDevice(gatt.getDevice().getAddress()));
    } else 
        setDeviceScanned(getDiscoveredBLEDevice(gatt.getDevice().getAddress()), DiscoveredBLEDevice.NOT_HEART_RATE);
}

private void handleHeartRateDeviceDisconnection(BluetoothGatt gatt) {
    broadcastHeartRateDeviceDisconnected(gatt.getDevice());
    gatt.close();

    clearoutHeartRateData();
    scanForHeartRateDevices();
}

private void disconnectGatt(DiscoveredBLEDevice device) {
    logMessage("CLOSING GATT FOR " + device.getBLEDeviceName(), false, false);
    device.getBlueToothGatt().close();
    device.setBlueToothGatt(null);
    mInDiscoveryMode = false;
}

private boolean subscribeToHeartRateGattServices(BluetoothGatt gatt) {
    for(BluetoothGattService blueToothGattService : gatt.getServices()) {
        if(blueToothGattService.getUuid().toString().contains(HEART_RATE_DEVICE_SERVICE_CHARACTERISTIC_PREFIX)) {
            mHeartRateGattService = blueToothGattService;

            for(BluetoothGattCharacteristic characteristic : mHeartRateGattService.getCharacteristics()) {
                logMessage("CHARACTERISTIC UUID = " + characteristic.getUuid().toString(), false, false);

                for(BluetoothGattDescriptor descriptor :characteristic.getDescriptors()) {
                    logMessage("DESCRIPTOR UUID = " + descriptor.getUuid().toString(), false, false);
                }

                if(characteristic.getUuid().equals(UUID.fromString(HEART_RATE_VALUE_CHAR_READ_ID))) {
                    gatt.setCharacteristicNotification(characteristic, true);
                    BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(HEART_RATE_VALUE_CHAR_DESC_ID));
                    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                    return gatt.writeDescriptor(descriptor);
                }
            }

            break; //break out of master for-loop
        }
    }

    return false;
}
Droid Chris
  • 3,455
  • 28
  • 31
  • 5
    I believe the question is geared for Bluetooth Classic, not Bluetooth LE... as is the case with yours mentioning "services," "characteristics," "gatt," and heart-rate devices. – jschlepp Jan 12 '16 at 06:23
3

device.getUuids() use this to get all the uuid of that paired device in the form of ParcelUuid; Example code below:-

private void get_uuid_from_paired_devices(){
        Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
        for (BluetoothDevice device: pairedDevices){

            for (ParcelUuid uuid: device.getUuids()){
                String uuid_string = uuid.toString();
                Log.d(TAG, "uuid : "+uuid_string);
            }
        }
    }
Biplob Das
  • 2,818
  • 21
  • 13
2

I suppose you need to be paired with the device in order to receive the uuids. At least, this is what happened to me.

The Good Giant
  • 1,740
  • 2
  • 19
  • 35
0

Below worked for me to fetch the records from the remote device

-0-
registerReceiver(..,
                new IntentFilter(BluetoothDevice.ACTION_UUID));

-1- device.fetchUuidsWithSdp();

-2-from within the broadcase receiver

   if (BluetoothDevice.ACTION_UUID.equals(action)) {
                        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                        Parcelable[] uuids = intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID);
                        for (Parcelable ep : uuids) {
                            Utilities.print("UUID records : "+ ep.toString());
                        }
                    }

You can also fetch the offline cached UUID records with

 BluetoothDevice.getUuids();
  • 1
    I tried `getUuids()` with three different BLE devices and all returned a null array. – Robert Lewis Nov 10 '17 at 01:14
  • The getUuids() is for classic only as I understand it. Its completely different for BTLE! – Brian Reinhold May 28 '18 at 11:54
  • This helped me figure it out. I had to use 'GetParcelableArrayExtra' instead of 'GetParcelableExtra'. The fact that that's not mentioned in any of the official documentation I looked at is frustrating. – MondQ Dec 07 '20 at 20:29