I want to pair the device and connect it ,but I have a problem ,I just can pair device but I am not able to connect them. I want to know how to solve this problem. I'm afraid, I have not explained my problem very well, I can not connect means, connect your phone to a Bluetooth headset and I can only pair, here is the code
if (btAdapt.isEnabled()) {
tbtnSwitch.setChecked(false);
} else {
tbtnSwitch.setChecked(true);
}
// ============================================================
IntentFilter intent = new IntentFilter();
intent.addAction(BluetoothDevice.ACTION_FOUND);
intent.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
intent.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
intent.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
registerReceiver(searchDevices, intent);
}
private BroadcastReceiver searchDevices = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Bundle b = intent.getExtras();
Object[] lstName = b.keySet().toArray();
for (int i = 0; i < lstName.length; i++) {
String keyName = lstName[i].toString();
Log.e(keyName, String.valueOf(b.get(keyName)));
}
BluetoothDevice device = null;
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
device = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device.getBondState() == BluetoothDevice.BOND_NONE) {
String str = "no pair|" + device.getName() + "|"
+ device.getAddress();
if (lstDevices.indexOf(str) == -1)
lstDevices.add(str);
adtDevices.notifyDataSetChanged();
}
}else if(BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)){
device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
switch (device.getBondState()) {
case BluetoothDevice.BOND_BONDING:
Log.d("BlueToothTestActivity", "it is pairing");
break;
case BluetoothDevice.BOND_BONDED:
Log.d("BlueToothTestActivity", "finish");
connect(device);
break;
case BluetoothDevice.BOND_NONE:
Log.d("BlueToothTestActivity", "cancel");
default:
break;
}
}
}
};
#Override
protected void onDestroy() {
this.unregisterReceiver(searchDevices);
super.onDestroy();
android.os.Process.killProcess(android.os.Process.myPid());
}
class ItemClickEvent implements AdapterView.OnItemClickListener {
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3)
{
if(btAdapt.isDiscovering())btAdapt.cancelDiscovery();
String str = lstDevices.get(arg2);
String[] values = str.split("\\|");
String address = values[2];
Log.e("address", values[2]);
BluetoothDevice btDev = btAdapt.getRemoteDevice(address);
try {
Boolean returnValue = false;
if (btDev.getBondState() == BluetoothDevice.BOND_NONE) {
BluetoothDevice.createBond(BluetoothDevice remoteDevice);
Method createBondMethod = BluetoothDevice.class
.getMethod("createBond");
Log.d("BlueToothTestActivity", "start");
returnValue = (Boolean) createBondMethod.invoke(btDev);
}else if(btDev.getBondState() == BluetoothDevice.BOND_BONDED){
connect(btDev);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void connect(BluetoothDevice btDev) {
UUID uuid = UUID.fromString(SPP_UUID);
try {
btSocket = btDev.createInsecureRfcommSocketToServiceRecord(uuid);
Log.d("BlueToothTestActivity", "connecting...");
btSocket.connect();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
class ClickEvent implements View.OnClickListener {
public void onClick(View v) {
if (v == btnSearch)
{
if (btAdapt.getState() == BluetoothAdapter.STATE_OFF) {
Toast.makeText(BlueToothTestActivity.this, "please open", 1000)
.show();
return;
}
if (btAdapt.isDiscovering())
btAdapt.cancelDiscovery();
lstDevices.clear();
Object[] lstDevice = btAdapt.getBondedDevices().toArray();
for (int i = 0; i < lstDevice.length; i++) {
BluetoothDevice device = (BluetoothDevice) lstDevice[i];
String str = "pair|" + device.getName() + "|"
+ device.getAddress();
lstDevices.add(str);
adtDevices.notifyDataSetChanged();
}
setTitle("address:" + btAdapt.getAddress());
btAdapt.startDiscovery();
} else if (v == tbtnSwitch) {
if (tbtnSwitch.isChecked() == false)
btAdapt.enable();
else if (tbtnSwitch.isChecked() == true)
btAdapt.disable();
} else if (v == btnDis)
{
Intent discoverableIntent = new Intent(
BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(
BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);
} else if (v == btnExit) {
try {
if (btSocket != null)
btSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
BlueToothTestActivity.this.finish();
}
}
}
}
You have not specified, but I assume your problem is in the socket creation part rather than the actual connect() call. This is where things usually go wrong.
How to fix?
Your code assumes the headset supports insecure BT communications. In many cases this is true, but not in all. Try calling createRfcommSocketToServiceRecord() instead
Your code uses the default SPP UUID for the RFCOMM channel lookup. For API versions >= 15 this is the wrong thing to do. Instead try calling device.getUuids() and use the first returned UUID as the creation parameter.
From my own experience, even for pre-15 API versions you can still call getUuids() and get good results, but you will need to do it by reflection. Only if the above fails should you attempt socket creation with the default SPP UUID
If the above fails, you may try, as a last resort, to activate the "createRfcommSocket" hidden API . That had worked for me several occasions, and on multiple Android versions. Use java reflection to activate this call and, since inherently not safe, protect it with try catch.
Remember to place your logic in an AsyncTask or something. You do not want the UI thread to block on such tasks!
Finally feel free to use https://github.com/giladHaimov/BTWiz for a much simpler handling of the Bluetooth connect and for simple async IO interface.
Quoting your code:
btSocket = btDev.createInsecureRfcommSocketToServiceRecord(uuid);
Log.d("BlueToothTestActivity", "connecting...");
btSocket.connect();
You can find the code on the official Android documentation on connecting as a client, here.
I can see 4 things that might cause problems:
You should connect in a separate thread! .connect() is a blocking call - see link above
Not all devices accept insecure connections
For Android devices below 2.3.3 this method does not work. You have to call a private method via reflection - see this. Also I think you will find it on SO.
Surround .create.... with a try/catch and post errors on Logcat.
Can you post your Logcat logs?
Related
Inside the BluetoothGattCallback method onConnectionStateChange I am checking for the successful connection with the BLE device and calling the discoverServices method afterwards. The BLE device needs a pin entry (prompt by Android) for an successful pairing. I want to discover all available services immediately after connecting to the device because when switching to the main activity of the application there should be data from the available characteristics already displayed.
I tried to analyze the functionality of the onConnectionStateChange method and the behavior of the BLE device with pin. Unfortunately the method is called once you initialize the connection and again after successfully entering the pin. There isn't a difference within the receiving state codes. Status and newState are exactly the same when initializing and after successful pin entry and connection. Therefore i added this workaround with the Thread.sleep method to wait for the user entry of the pin and call afterwards the discoverServices method. But this workaround is not very practical because the user need to enter the pin within these 10 seconds. If not the connection is successful but the services won't be discovered.
How can i check or differ between these two states? Initializing the connection and successful enter of the pin?
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
Log.d("KOPPLUNG", "In onConnectionStateChange with status: " + status + " and newState: " + newState);
if (newState == BluetoothProfile.STATE_CONNECTED) {
broadcastUpdate(ACTION_GATT_CONNECTED, mCallbackBleAddress);
Log.i(TAG, "Connected to GATT server." + mCallbackBleAddress);
// Attempts to discover services after successful connection.
try {
Thread.sleep(10000);
} catch (Exception e) {
}
for(int i = 0; i<5; i++){
if(mBoltDeviceHandler.getBoltDevice(mCallbackBleAddress).getBluetoothGatt().discoverServices()){
Log.i(TAG, "Attempting to start service discovery:" + true);
break;
}
}
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
switch (status){
case 0: Log.i(TAG, "Sucessfully Disconnected from GATT server.");
break;
case 133: // Handle internal Android Bug
Log.i(TAG, "Connection aborted, Android Error 133");
broadcastUpdate(ACTION_GATT_CONNECTION_NOT_SUCCESSFUL, mCallbackBleAddress);
break;
default: Log.i(TAG, "Unexpected Disconnection from GATT server. Errorcode: "+status);
broadcastUpdate(ACTION_GATT_DISCONNECTED, mCallbackBleAddress);
autoconnect(gatt.getDevice());
}
Addition
The code for building up the connection is divided up in three different classes. The application starts with an scan activity where available devices are being searched and listed. By clicking on one list item (device) the connection process will be started.
onListItemClick
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position);
if (device == null) return;
Context mContext = getApplicationContext();
BoltDevice boltDevice = new BoltDevice(device,mContext);
int i = mBoltDeviceHandler.addBoltDevice(boltDevice);
Intent resultIntent = new Intent();
resultIntent.putExtra("IN_CONNECTION", boltDevice.getBleAddress());
resultIntent.putExtra("POSITION",i);
if(i != -1){
setResult(Activity.RESULT_OK, resultIntent);
}
else {
setResult(Activity.RESULT_CANCELED, resultIntent);
}
finish();
}
By running the above code an object from type BoltDevice is being created and added to the BoltDeviceHandler.
Relevant code from BoltDevice class
public BoltDevice(BluetoothDevice device, Context context) {
mContext = context;
this.mBluetoothDevice = device;
this.mBleAddress = device.getAddress();
this.mDeviceName = device.getName();
mBatteryLevel = 0;
mSpecialBolt = false;
//Workarround for 133 error taken from: https://github.com/googlesamples/android-BluetoothLeGatt/issues/44
mStartGattHandler.postDelayed(mStartGattRunnable, START_GATT_DELAY);
}
public void closeGatt() {
if (mBluetoothGatt != null) {
mBluetoothGatt.disconnect();
mBluetoothGatt.close();
mBluetoothGatt = null;
}
}
public boolean connect() {
// Previously connected device. Try to reconnect.
if (mBluetoothGatt != null) {
Log.d(TAG, "Trying to connect with existing bluetoothGATT to: " + mBleAddress);
if (mBluetoothGatt.connect()) {
return true;
} else {
Log.d(TAG, "Could not connect to existing bluetoothGATT: " + mBleAddress);
return false;
}
}
// We want to directly connect to the device, so we are setting the autoConnect
// parameter to false.
mBluetoothLeService.connect(mBluetoothDevice);
Log.d(TAG, "Trying to create a new connection to: " + mBluetoothDevice.getAddress());
return true;
}
public boolean disconnect() {
if (mBluetoothGatt != null) {
mBluetoothGatt.disconnect();
return true;
} else {
return false;
}
}
public void bindService(){
Intent gattServiceIntent = new Intent(mContext, BluetoothLeService.class);
}
Relevant code from the MainActivity (active after the ScanActivity is finished)
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode) {
case (5) : {
if (resultCode == Activity.RESULT_OK) {
int i = data.getIntExtra("POSITION",-1);
mReconnect[i] = false;
runOnUiThread(new Runnable() {
#Override
public void run() {
if(!isFinishing()) {
updateText(i,true);
ToastCompat.makeText(MainActivity.this, "In Connection!", ToastCompat.LENGTH_SHORT).show();
}
}
});
}
if (resultCode == Activity.RESULT_CANCELED) {
if(data!=null) {
int i = mBoltDeviceHandler.getBoltDevicePositionInList(data.getStringExtra("IN_CONNECTION"));
mReconnect[i] = true;
runOnUiThread(new Runnable() {
#Override
public void run() {
if(!isFinishing()) {
ToastCompat.makeText(MainActivity.this, "In Connection!", ToastCompat.LENGTH_SHORT).show();
}
}
});
}
else{
ToastCompat.makeText(this,"No device selected!", ToastCompat.LENGTH_SHORT).show();
}
}
break;
}
}
}
(...)
private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
final String address = intent.getStringExtra(BluetoothLeService.EXTRA_ADDRESS);
if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
ToastCompat.makeText(MainActivity.this, mBoltDeviceHandler.getBoltDevice(address).getDeviceName() + " " + getResources().getString(R.string.BLE_Connected), ToastCompat.LENGTH_SHORT).show();
updateText(mBoltDeviceHandler.getBoltDevicePositionInList(address), false);
ToastCompat.makeText(MainActivity.this, mBoltDeviceHandler.getBoltDevice(address).getDeviceName()+ ": "+ getResources().getString(R.string.BLE_ServicesDiscovered), ToastCompat.LENGTH_SHORT).show();
} else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
(...)
First of all, I would recommend using the Nordic library. That saved me a lot of headhacke when working with BLE on android and allow to keep a clean architecture.
You should have something of the state machine like :
connect to device
request GATT services / characteristic
request for the user code on Android
send user code
retrieve data over BLE
Also, after receiving a onConnection status change notifcation, you should xwait a few ms before starting discovering GATT
The onConnectionStateChange event is triggered just after the Android connects to a device.Moreover, when the device has Service Changed indication enabled, and the list of services has changed (e.g. using the DFU), the indication is received few hundred milliseconds later, depending on the connection interval. When received, Android will start performing a service discovery operation on its own, internally, and will NOT notify the app that services has changed.
public final void onConnectionStateChange(#NonNull final BluetoothGatt gatt,
final int status, final int newState) {
if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED) {
// Sometimes, when a notification/indication is received after the device got
// disconnected, the Android calls onConnectionStateChanged again, with state
// STATE_CONNECTED.
// See: https://github.com/NordicSemiconductor/Android-BLE-Library/issues/43
if (bluetoothDevice == null) {
Log.e(TAG, "Device received notification after disconnection.");
log(Log.DEBUG, "gatt.close()");
try {
gatt.close();
} catch (final Throwable t) {
// ignore
}
return;
}
// Notify the parent activity/service
Log("Connected to " + gatt.Device.Address);
isConnected = true;
connectionState = State.Connected;
OnDeviceConnected(gatt.Device);
/*
* TODO: Please calculate the proper delay that will work in your solution.
* If your device does not use Service Change indication (for example does not have DFU) the delay may be 0.
*/
var delay = 1600; // around 1600 ms is required when connection interval is ~45ms.
postDelayed(() -> gatt.DiscoverServices(), delay);
} else {
if (newState == ProfileState.Disconnected)
{
var wasConnected = IsConnected;
if (gatt != null)
{
NotifyDeviceDisconnected(gatt.Device); // This sets the mConnected flag to false
if (_initialConnection) ConnectDevice(gatt.Device);
if (wasConnected || status == GattStatus.Success)
return;
}
}
/*
* Connection attempt did fail! Retry possible ?
*/
onError(gatt.Device, Error.LinkLost, status);
}
}
I managed to create a ListView, search for devices and show them, on click connect to the selected device, but now I want to know how to send from one phone to the other one just an int or a boolean or something like this.
It's a little game, there is a winner and a loser - so I want to compare two variables and check who won, then display it.
My code so far:
Searching
bluetooth.startDiscovery();
textview.setText("Searching, Make sure other device is available");
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter);
Displaying in a ListView
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
device = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
mDeviceList.add(device.getName() + "\n" + device.getAddress());
Log.i("BT", device.getName() + "\n" + device.getAddress());
listView.setAdapter(new ArrayAdapter<String>(context,
android.R.layout.simple_list_item_1, mDeviceList));
}
}
};
#Override
protected void onDestroy() {
unregisterReceiver(mReceiver);
super.onDestroy();
}
Pairing the devices
private void pairDevice(BluetoothDevice device) {
try {
Log.d("pairDevice()", "Start Pairing...");
Method m = device.getClass().getMethod("createBond", (Class[]) null);
m.invoke(device, (Object[]) null);
Log.d("pairDevice()", "Pairing finished.");
} catch (Exception e) {
Log.e("pairDevice()", e.getMessage());
}
}
It works pretty well, both devices are paired.
Do I need a client and a server to transfer ints?
Here is some sample code from the android SDK: BluetoothChat
The key is to use a Service on both sides of the connection that both listens for incoming messages and sends outgoing messages. Then all your Activity has to worry about is interacting with the user, and telling the service to send the appropriate messages.
//METHOD FROM ACTIVITY
private void sendMessage(String message) {
// Check that we're actually connected before trying anything
if (mChatService.getState() != BluetoothChatService.STATE_CONNECTED) {
Toast.makeText(this, R.string.not_connected, Toast.LENGTH_SHORT).show();
return;
}
// Check that there's actually something to send
if (message.length() > 0) {
// Get the message bytes and tell the BluetoothChatService to write
byte[] send = message.getBytes();
mChatService.write(send);
// Reset out string buffer to zero and clear the edit text field
mOutStringBuffer.setLength(0);
mOutEditText.setText(mOutStringBuffer);
}
}
//METHOD FROM SERVICE
public void write(byte[] out) {
// Create temporary object
ConnectedThread r;
// Synchronize a copy of the ConnectedThread
synchronized (this) {
if (mState != STATE_CONNECTED) return;
r = mConnectedThread;
}
// Perform the write unsynchronized
r.write(out);
}
I am using Bluetooth API of android. I am here creating client-server connection using BluetoothServerSocket & BluetoothSocket but my program stuck at the certain point.
// Create a BroadcastReceiver for ACTION_FOUND
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// When discovery find a device
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// get the BluetoothDevice object from the Intent
BluetoothDevice mBluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.i("MainActivity", "Device Name: " + mBluetoothDevice.getName() + " Address: " + mBluetoothDevice.getAddress());
new AcceptThread().start();
}
}
};
private class AcceptThread extends Thread {
private BluetoothServerSocket mBluetoothServerSocket ;
public AcceptThread() {
try {
mBluetoothServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord("BT_SERVER", UUID.fromString("a60f35f0-b93a-11de-8a39-08002009c666"));
} catch (IOException e) {
Log.e("MainActivity", e.getMessage());
}
}
#Override
public void run() {
BluetoothSocket mBluetoothSocket;
// Keep listening until exception occurs or a socket is returned
while(true) {
try {
mBluetoothSocket = mBluetoothServerSocket.accept();
} catch (IOException e) {
break;
}
// If a connection was accepted
if(mBluetoothSocket != null) {
// transfer the data here
Toast.makeText(MainActivity.this, "Socket is created", Toast.LENGTH_LONG).show();;
try {
// close the connection to stop to listen any connection now
mBluetoothSocket.close();
} catch(IOException e) { }
}
}
}
}
Here my program stuck
mBluetoothServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord("BT_SERVER", UUID.fromString("a60f35f0-b93a-11de-8a39-08002009c666"));
I could not catch why it getting stuck at this point, Any idea to you for this ?
From your question it is unclear whether your application is a client or server or both. For writing bluetooth client-server applications, android phone at any instance plays a single role of server or a client. If your phone is server, then you need to listen for connections from other bluetooth devices using method listenUsingRfcommWithServiceRecord(). Then use accept() to complete the connection.
In case android phone acts as client, it will initiate a bluetooth connection to other devices. For such scenario, your broadcast receiver is needed. We need to scan for available bluetooth devices with startDiscovery() method. Your broadcast receiver's onReceive() is called when a new bluetooth device is found. To connect to this found device, call createRfcommSocketToServiceRecord() with desired UUID.
Hope this helps.
This may be obvious but did you instantiate your BluetoothAdapter? Accept Thread uses the adapter without intializing it.
myBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
While listening, set the discovery name to a specific value then used listenUsingRfcommWithServiceRecord method in broadcast receiver.
private class AcceptTask extends AsyncTask<UUID,Void,BluetoothSocket> {
#Override
protected BluetoothSocket doInBackground(UUID... params) {
String name = mBtAdapter.getName();
try {
//While listening, set the discovery name to a specific value
mBtAdapter.setName(SEARCH_NAME);
BluetoothServerSocket socket = mBtAdapter.listenUsingRfcommWithServiceRecord("BluetoothRecipe", params[0]);
BluetoothSocket connected = socket.accept();
//Reset the BT adapter name
mBtAdapter.setName(name);
return connected;
} catch (IOException e) {
e.printStackTrace();
mBtAdapter.setName(name);
return null;
}
}
#Override
protected void onPostExecute(BluetoothSocket socket) {
if(socket == null) {
return;
}
mBtSocket = socket;
ConnectedTask task = new ConnectedTask();
task.execute(mBtSocket);
}
}
// End
I have written an application that regularly tries to connect to paired devices and, if the connection succeeds, performs an action. I have a registered bluetooth broadcastReceiver that handles ACTION_ACL_CONNECTED and ACTION_ACL_DISCONNECTED. In a service, in the run loop, I get the list of paired devices and attempt a connection to each. When the connection succeeds, I get the ACTION_ACL_CONNECTED notification and when the device disconnects (or goes out of range) I get the ACTION_ACL_DISCONNECTED. All works well.
For headsets, the BluetoothHeadset class raises ACTION_ACL_CONNECTED and ACTION_ACL_DISCONNECTED so I don't need to connect to a device whose primary UUID is 00001108.
I have a bluetooth speaker that has a single UUID of 0000110B. It can go in and out of range all day long and I get the ACTION_ACL_CONNECTED and ACTION_ACL_DISCONNECTED signals each time I connect to it and each time the disconnect happens because it goes out of range.
If I try to connect to another phone, using 00001105, the first connect succeeds and ACTION_ACL_CONNECTED is raised. If I take the phone out of range (or just turn off its bluetooth), I get ACTION_ACL_DISCONNECTED. However, any further connect attempt results in "Service discovery failed" even if the phone is in range and its bluetooth is on. This continues until i toggle the bluetooth adapter on my phone either manually or in code. Then I can do one connect/disconnect and then the Service discovery failure happens again. obviously, toggling the adapter is not a satisfactory solution.
So, my question is, what makes 00001105 different than 00001108 in that I cannot do multiple connect/disconnects with 00001105?
The code is relatively complex, but here it is (please excuse the indentation. Cut and paste didn't work very well):
public class BluetoothListenerService extends Service {
// The BroadcastReceiver that listens for Connected and disconnected blue tooth devices (HSP,HFP and SPP)
public final BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String devices[]=null;
Intent service_intent;
String action = intent.getAction();
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
// Get the BluetoothDevice object from the Intent
String testUUID=null;
try {
Method method = device.getClass().getMethod("getUuids", (Class<?>[]) null);
ParcelUuid[] phoneUuids = (ParcelUuid[]) method.invoke(device, (Object[]) null);
UUID myUUID=phoneUuids[0].getUuid();
testUUID=myUUID.toString().toLowerCase();
Log.i(TAG,"connected to "+device.getName()+ "using "+ testUUID);
} catch (Exception e) {}
return;
} // end ACTION_ACL_CONNECTED
if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
Log.i(TAG, "disconnected from "+device.getName());
return;
} // end ACTION_ACL_DISCONNECTED
} // end OnReceive
}; // end broadcast receiver definition
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
#Override
public void onStart(final Intent intent, final int startId) {
super.onStart(intent, startId);
if (mBluetoothAdapter == null) {
Toast.makeText(this," Bluetooth is not available on this device", Toast.LENGTH_LONG).show();
return;
}
Thread t = new Thread("MyService(" + startId + ")") {
#Override
public void run() {
_onStart(intent, startId);
stopSelf();
}
};
t.start();
return;
}
public void _onStart(Intent intent, int startId) {
String devices[]=null;
Log.i(TAG,"Bluetooth Listener Service started on thread "+ Integer.toString(android.os.Process.myTid()));
// Register the Bluetooth BroadcastReceiver
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED);
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
try {
this.unregisterReceiver(mBluetoothReceiver);
} catch(IllegalArgumentException ignorable) {}
registerReceiver(mBluetoothReceiver, filter);
// the loop
bRun=true;
while(bRun) {
Set <BluetoothDevice> mySet=mBluetoothAdapter.getBondedDevices();
Iterator <BluetoothDevice> iterator=mySet.iterator();
while(iterator.hasNext()) {
BluetoothDevice setElement=iterator.next();
String testUUID=null;
try { // Get UUID for device
Method method = setElement.getClass().getMethod("getUuids", (Class<?>[]) null);
ParcelUuid[] phoneUuids = (ParcelUuid[]) method.invoke(setElement, (Object[]) null);
UUID myUUID=phoneUuids[0].getUuid();
testUUID=myUUID.toString().toUpperCase();
Log.i(TAG, "device UUID is "+testUUID);
if (testUUID.compareTo(headsetUUID)!=0 && testUUID.compareTo(a2dpUUID)!=0) {
try { // create a socket
BluetoothSocket tmp=null;
tmp=setElement.createInsecureRfcommSocketToServiceRecord(UUID.fromString(testUUID));
mmSocket=tmp;
Log.i(TAG,"connecting using socket "+mmSocket.toString());
Log.i(TAG,"connecting to "+setElement.getName());
try {
if (mBluetoothAdapter.isDiscovering()==true) mBluetoothAdapter.cancelDiscovery();
mmSocket.connect();
Log.i(TAG,"connection to "+setElement.getName()+" was sucessful");
// the connection will be handled by the receiver in ACL_CONNECTED
} catch (Exception e) { // connection failed
String sTest=e.getMessage();
Log.e(TAG,"connection to "+setElement.getName()+" returned "+sTest);
if (sTest!=null) {
try { // closing the socket
Log.i(TAG,"closing socket due to error: "+sTest);
Log.i(TAG,"closing socket "+mmSocket.toString());
if (!mmSocket.equals(null)) {
mmSocket.close();
mmSocket=null;
tmp=null;
}
else {
Log.e(TAG, "socket is null");
}
} catch (Exception e1) { // socket close failed
Log.e(TAG,"closing socket failed");
break;
}
if (sTest.compareTo("Unable to start Service Discovery")==0) {
mBluetoothAdapter.disable();
SystemClock.sleep(3000);
mBluetoothAdapter.enable();
SystemClock.sleep(1000);
}
}
} // end catch connection failed
} catch (Exception e1) {
Log.e(TAG, "socket create failed.");
}
} // end test if headset or not
} catch (Exception e1) {
Log.e(TAG, "find UUID failed.");
} // end find UUID
} // end while iterator.hasnext()
} // end of run loop
}
}
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.
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().
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
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;
}
I suppose you need to be paired with the device in order to receive the uuids.
At least, this is what happened to me.
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);
}
}
}
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();