I'm trying to detect if a bonded Bluetooth Device supports GATT or not.
When scanning, calling BluetoothDevice.getType() will recognized my device as type 3 (Dual Mode - BR/EDR/LE). However, after the device is bonded and a call to BluetoothAdapter.getBondedDevices(), the same method returns my device as type 1 (Classic).
#Override
public void onScanResult(int callbackType, android.bluetooth.le.ScanResult result) {
result.getDevice().getType();// value is 3
}
...
// this will show pairing request to user
device.connectGatt(context, false, callback);
...
// once the device is paired, I query for the new set of bonded devices.
Set<BluetoothDevice> set = BluetoothAdapter.getDefaultAdapter().getBondedDevices();
for (BluetoothDevice device : set)
{
device.getType();// value is 1
}
How can I reliably detect if a bonded device support GATT (type 3 or 2)?
I have also tried cross-checking bonded devices with:
int[] ALL_STATES = { BluetoohtProfile.STATE_DISCONNECTED, BluetoohtProfile.STATE_CONNECTING, BluetoohtProfile.STATE_CONNECTED, BluetoohtProfile.STATE_DISCONNECTING };
List<BluetoothDevice> list = BluetoothManager.getDevicesMatchingConnectionState(BluetoothProfile.GATT, ALL_STATES);
But the result is always an empty list.
Appreciate any help!
You can double check whether the device in your scan result and bonded device is the same device.
I guess the bonded device is other BT device bonded with your Android device.
Related
I'm trying to build an app which gets battery level of currently connected Bluetooth headset. This app can be used on phones which don't have this functionality built-in.
While searching on stackoverflow, I found How to get Bluetooth Headset battery status in android this question. I got the currently connected Bluetooth headset using BluetoothProfile.HEADSET profile.
But in the device object of type BluetoothDevice I don't see any method or property to get battery level of Bluetooth Headset.
I can get the device name and isAudioConnected.
If question is about Bluetooth HFP feature: HF indicators feature is optional for the both sides. If the both sides support it, BluetoothHeadset will broadcast BluetoothHeadset.ACTION_HF_INDICATORS_VALUE_CHANGED with BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID equal 2 (Battery Level) and BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE with scope 0..100. Do not remember Android version were it was implemented, you should check it.
Also battery level can be implemented in device using vendor specific HFP AT commands (especially for old handsfree devices) and maybe BLE.
I found a solution, but it only works on android 8 and above
I took this code from here
Kotlin
fun getBatteryLevel(pairedDevice: BluetoothDevice?): Int {
return pairedDevice?.let { bluetoothDevice ->
(bluetoothDevice.javaClass.getMethod("getBatteryLevel"))
.invoke(pairedDevice) as Int
} ?: -1
}
The first thing to register BroadcastReciver by "android.bluetooth.device.action.BATTERY_LEVEL_CHANGED"
and you can receive this action by the broadcast receiver then get extra data by "android.bluetooth.device.extra.BATTERY_LEVEL"
and if you want to trigger this action, you need to reconnect your Bluetooth device or Bluetooth device battery level happened to change.
Good luck for you.
Connected AirPods Pro to OnePlus 5T with Android 9.
None of those registered events happen:
"android.bluetooth.device.action.BATTERY_LEVEL_CHANGED"
"android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED"
"android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED"
I am Able to achieve the handset battery Level in Java
try {
BluetoothDevice device = bluetoothAdapter.getRemoteDevice("Connected device ID");
java.lang.reflect.Method method;
method = device.getClass().getMethod("getBatteryLevel");
int value = (int) method.invoke(device);
result.success(value);
} catch (Exception ex) {
result.error("invalid_argument", "'deviceId' argument is required to be string", null);
break;
}
This is #Kirill Martyuk answer as an Extension variable
val BluetoothDevice.batteryLevel
get() = this.let { device ->
val method = device.javaClass.getMethod("getBatteryLevel")
method.invoke(device) as Int?
} ?: -1
Usage would be something like
val manager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager?
val adapter = manager?.adapter
val devices = adapter?.bondedDevices.orEmpty()
devices.forEach { device ->
Log.d("DEVICE_NAME", device.name)
Log.d("CHARGE_LEVEL", device.batteryLevel.toString())
}
I am working with BLE device.I perform the code using android GATT and it scan device and also I send immediate alert in BLE device from mobile using writeCharacteristic and BLE device is beep.But now I want to Alert in android Mobile when I press BLE device button. That exactly I don't know how to do.
Thank You.
For this you have to listen for characteristics change. When you will press the BLE button, a characteristics must be change inside its hardware (set any flag value depends on hardware's firmware coding). When characteristics changed, characteristics method will called. You can perform required functionality there.
#Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic
characteristic)
{
Here we received ble button pressed event callback. Do stuff here.
}
For receiving characteristics change callback first you have to enable the notification like this.
BluetoothGattCharacteristic characteristic = gatt.getService(mServiceUuuid).getCharacteristic(mCharOneUuuid);
gatt.setCharacteristicNotification(characteristic, true);
List<BluetoothGattDescriptor> list = characteristic.getDescriptors();
for (int i = 0; i < list.size(); i++) {
BluetoothGattDescriptor desc = list.get(i);
desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(desc);
}
I need to "rename" bluetooth paired device of Android phone with programming. But searching results are most discuss about local bluetooth rename method. And it could use setname() to complete. Does there have any method to rename "paired device" of Android phone ?
I know the question is old, but I just needed this and found out how to do it. It uses reflexion so I'm not sure this is the best way to go but it works.
public void renamePairDevice(BluetoothDevice device, String name)
throws IllegalAccessException, InvocationTargetException, NoSuchMethodException
{
Method m = device.getClass().getMethod("setAlias", String.class);
m.invoke(device, name);
}
I don't think you can rename the name of the paired device. You can only change the name from the paired device's settings.
Think of it like a wifi router, you cannot change the name of the router, but you can only connect to it.
However if you want,you could assign this way
Set<BluetoothDevice> devices = btAdapter.getBondedDevices();
if (devices.size() > 0) {
for(int i=0;i<device.size();i++) {
mDevice[i] = device;
bondedDevices.add(mDevice.getName());
}
}
That way you could get the name of the paired Devices as a mDevice array. Hope it solved your issue
Following a lot of answers here, I am able to build the list of connected bluetooth devices with the help of a BroadcastReceiver. Now my question is how do I know which device supports which profile. I want to be able to pick the devices based on the profile, for example, get a list of currently connected devices and their profile, and pick one of them. I don't see how I can get such info if I have the instance of BluetoothDevice.
On this page there are some codes illustrating how to work with a bluetooth headset profile: http://developer.android.com/guide/topics/connectivity/bluetooth.html#Profiles. But it doesn't solve my problem. If you think I am missing anything, please help me and point it out.
Thanks a lot in advance.
I've run into the same problem. It doesn't appear that you can get the available profiles from the BluetoothDevice class.
But there is a long way around by getting a List of BluetoothDevices from the getDevicesMatchingConnectionStates method in the BluetoothProfile class.
For example if you want to find which BluetoothDevices support A2DP, first create a custom BluetoothProfile.ServiceListener
public class cServiceListener implements BluetoothProfile.ServiceListener {
private static final int[] states={ BluetoothProfile.STATE_DISCONNECTING,
BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_CONNECTING};
#Override
public void onServiceConnected(int profile, BluetoothProfile bluetoothProfile) {
List<BluetoothDevice> Devices=bluetoothProfile.getDevicesMatchingConnectionStates(states);
for (BluetoothDevice loop:Devices){
Log.i("myTag",loop.getName());
}
}
#Override
public void onServiceDisconnected(int profile) {
}
}
Then attach it to the profile you want to check, in this example A2DP
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
cServiceListener mServiceListener=new cServiceListener();
mBluetoothAdapter.getProfileProxy(thisContext,mServiceListener, BluetoothProfile.A2DP);
This will logcat all the bluetooth devices that support A2DP which are in the requested states. In this example it includes all devices which are currently connected and previously paired devices which are disconnected.
Looking at the Android source code, you can guess which profiles are available for a device by looking at its UUIDs, and then connect each profile one by one.
Step 0 : Copy the _PROFILE_UUIDS constants from there : https://android.googlesource.com/platform/packages/apps/Settings/+/9ad703cdb9a8d0972c123b041d18aa7bbeb391a4/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java
Step 1 : get your BluetoothDevice, via scanning for instance. Assure that it's properly bonded.
Step 2 : register a BroadcastReceiver for the android.bluetooth.BluetoothDevice.ACTION_UUID action intent
Step 3 : on your device, call the fetchUuidsWithSdp method
Step 4 : you will recieve a ACTION_UUID broadcast : in the onReceive method you can unregister the receiver, and get the list of profiles like so :
ArrayList<Integer> profiles = new ArrayList<>();
ParcelUuid[] uuids = device.getUuids();
if (BluetoothUuid.containsAnyUuid(uuids, HEADSET_PROFILE_UUIDS))
{
profiles.add(BluetoothProfile.HEADSET);
}
if (BluetoothUuid.containsAnyUuid(uuids, A2DP_PROFILE_UUIDS))
{
profiles.add(BluetoothProfile.A2DP);
}
if (BluetoothUuid.containsAnyUuid(uuids, OPP_PROFILE_UUIDS))
{
//OPP doesn't have any BluetoothProfile value
}
if (BluetoothUuid.containsAnyUuid(uuids, HID_PROFILE_UUIDS))
{
//You will need system privileges in order to use this one
profiles.add(BluetoothProfile.INPUT_DEVICE);
}
if (BluetoothUuid.containsAnyUuid(uuids, PANU_PROFILE_UUIDS))
{
profiles.add(BluetoothProfile.PAN);
}
Step 5 : get the proxies for the profiles, one by one :
for (int profile : profiles)
{
if (!adapter.getProfileProxy(context, listener, profile))
{
//Do something
}
}
Step 6 : do anything with each proxy retrieved in the onServiceConnected method of your listener. You can access the connect method using relfection.
In my code, I'm trying to unpair a bluetooth device by calling the function.
import android.bluetooth.BluetoothDevice;
.....
BluetoothDevice Device = mBluetoothAdapter.getRemoteDevice(address);
.......
public void unpair() {
int state = getBondState();
if (state == BluetoothDevice.BOND_BONDING || state == BluetoothDevice.BOND_BONDED) {
Device.cancelBondProcess(); //Error in this line
}
Seems like you are using a non-public API cancelBondProcess(),
In addition this APIs sounds like it will only cancel the process that is ongoing , i.e when the state is BOND_BONDING, if the device is already bonded it will fail / return error. You will need to removeBond() to remove the bonded device - again this is also non-public API and not recommended if you plan on keeping your application compatible with various versions of android releases.
There are no public APIs currently to accomplish canceling or removing of bond.