Android Bluetooth headset connection - android

I am new to Android Platform. I am working with an application requires integration of Bluetooth. The requirement is instead of manually connecting and disconnecting a Bluetooth headset(HSP profile),Connection and disconnection should be possible within the application.Is it possible to connect and disconnect the device in Android devices running OS 4.2 ,4.3 and 4.4.If any one has a solution for this issue,Please advise me for the same.

It is possible, but sometimes not that simple.
To connect, start by checking whether or not the device you are running on has BT support at all:
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter==null) {
// device not support BT
}
If not - gracefully disable the BT portion of your app and move on.
If supported, check whether or not it is currently enabled (remember, the user can
turn BT on & off as with other communication channels):
boolean isEnabled = bluetoothAdapter.isEnabled(); // Equivalent to: getBluetoothState() == STATE_ON
And, if not enabled, allow the user to turn it on by firing an ACTION_REQUEST_ENABLE intent:
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, ENABLE_BT_CODE);
Once you are clear in terms of availability, perform lookup for the specific device you aim for.
It is always a good idea to start with the bonded device list maintained by Android:
Set<BluetoothDevice> bondedDevices = bluetoothAdapter.getBondedDevices();
for (BluetoothDevice device: pairedDevices) {
if (device is the one we look for) {
return device;
}
}
If not, you will need to issue a BT discovery command.
Discovery must never be performed on the UI thread, so please spawn a thread (use AsyncTask, Executer, etc. to do the work).
Discovery should not be performed when a BT connection operation is still taking place. The
impact on the device resources will be too high.
Start by setting your discovery receiver:
discoveryReceiver = new BroadcastReceiver() {
private boolean wasFound = false;
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
System.out.println(action);
if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
discoveryStatus = STARTED;
}
else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
discoveryStatus = FINISHED;
}
else if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device is what we look for) {
stopDiscovery(context);
}
}
}
};
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
filter.addAction(BluetoothDevice.ACTION_FOUND);
context.registerReceiver(discoveryReceiver, filter);
And follow with a start off command:
boolean started = bluetoothAdapter.startDiscovery(); //async call!
if (!started) {
// log error
}
Once you find your device, you will then need to create a dedicated BT socket:
BluetoothSocket clientSocket = null;
try {
if (secureMode == SECURE) {
clientSocket = device.createRfcommSocketToServiceRecord(serviceUuid);
}
else { // INSECURE
clientSocket = device.createInsecureRfcommSocketToServiceRecord(serviceUuid);
}
if (clientSocket == null) {
throw new IOException();
}
} catch (IOException e) {
// log error
}
Followed by connect command:
clientSocket.connect(context);
Once connect returns, you can transmit data back & forth the way you do with sockets and when done:
clientSocket.close(context);
The above depicts the general flow. In many cases your work will be harder:
You will use different socket generation methods for secure vs. insecure BT modes. You will use different
methods to interrogate the device for supported UUIDs. You may also sometimes have to resort to reflection to activate hidden services e.g. getUuids() for Android < ver 15. And the list goes on.
It makes sense, especially for a beginner, to use a tool for this job.
My favorite (I am biased, I wrote it..) is BTWiz which will encapsulate the above
flow from you and will also provide you with a simple interface for async IO. Feel free to try it out.

Related

Bluetooth devices found in an Android app, but not in a platform specific code in Flutter

I'm trying to scan, connect and receive data from a Bluetooth module. Everything works fine if I just use an android application. I can scan and find all nearby devices, connect to anyone (I'm only interested in my Bluetooth module) and I am able to read the test data that's being sent from the Bluetooth module.
The problem is that the application is being developed using Flutter. I used the same code from my Android application and linked it with Dart though the EventsChannel, but now I can only see fewer Bluetooth devices in the Flutter app and none of them is the Bluetooth Module I'm interested in. I'm new to Flutter and the platform specific coding, I can't understand why the same code behaves differently in different apps on same the hardware.
I've tested my code on Samsung S4 and S8 phones and the result is the same.
This is the code for the EventChannel for the discovery part:
new EventChannel(flutterEngine.getDartExecutor(), DISCOVER_CHANNEL).setStreamHandler(
new EventChannel.StreamHandler() {
#Override
public void onListen(Object args, EventChannel.EventSink events) {
Log.w(TAG, "Registering receiver");
discoverReceiver = DiscoverReceiver(events);
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(discoverReceiver, filter);
}
#Override
public void onCancel(Object args) {
Log.w(TAG, "Unregistering receiver");
unregisterReceiver(discoverReceiver);
discoverReceiver = null;
}
}
);
For now my discoverReceiver is a global BroadcastReceiver.
Below is the code for the Broadcastreceiver:
private BroadcastReceiver DiscoverReceiver(final EventSink events) {
return new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Discovery has found a device. Get the BluetoothDevice
// object and its info from the Intent.
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
String deviceName = device.getName();
if (deviceName == null){
deviceName = "No Device Name";
}
events.success(deviceName);
Log.w(TAG, "Sending " + deviceName);
}
}
};
}
**I used the (Log.w(TAG, "Sending " + deviceName);) statement to see if events were being lost/dropped.
And below is how I receive it in Dart:
#override
void initState() {
super.initState();
devices.add(selectDevice);
discoverChannel.receiveBroadcastStream().listen(onEvent);
}
void onEvent(Object event) {
setState(() {
devices.add(event);
});
}
Below is the code in my Android app that can scan and find all devices in case you want to compare with the above:
private BroadcastReceiver DiscoverReceiver() {
return new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Discovery has found a device. Get the BluetoothDevice
// object and its info from the Intent.
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
String deviceName = device.getName();
if (deviceName == null){
deviceName = "No Device Name";
}
devicelist.add(device);
devNames.add(deviceName);
arrayAdapter.add(deviceName);
arrayAdapter.notifyDataSetChanged();
Log.w(TAG, "Sending " + deviceName);
}
}
};
}
I'm not concerned with the last snippet but I just thought I'd show the complete flow. Snippet 2 is a copy of what I have in a stand-alone Android App and it scans and finds all devices, but once I use it in a Flutter App as native code for Android it stops finding the same number of devices, still finds some though and is very unreliable.
I have tried most of the Flutter bluetooth packages but none of them was what I was looking for and so I ended up going with Platform specific code, which worked fine until it was plugged to Flutter. I've read the documentation for Android development and the code above is mostly modified code from Android sample. I just can't figure out why the same code can find more devices as a stand-alone app versus using it as a native code for a flutter application if at the end it's being tested on the same hardware.
Any input will be appreciated!
Ok so I've finally found the solution. It is something to do with the Bluetooth's startDiscovery() running and doing its job properly but the events are captured a little later, something that the debugger would not be able to show.
So in my case, all devices were "discovered" but the flutter app starts capturing the events later so it only shows the last 1 or 2 devices that were discovered during the discovery.
I moved:
discoverChannel.receiveBroadcastStream().listen(onEvent);
From the initState() and into a function that is called after a button press, which makes sure everything has loaded before registering the broadcast receiver on the native side and the broadcast stream receiver on Dart's.
I'm still not sure exactly how to express this, but it's about the timing of registering the BroadcastReceiver on the native side and the receiveBroadcastStream on Dart's side.
Now it starts the discovery and captures the events properly, which shows the same number of devices found in an Android stand-alone app.
Hope this helps anyone who might face this odd issue in the future.

Bluetooth pairing randomly removed

I'm mantaining an app that runs on an Android 6.0 tablet that uses a Zebra iMZ220 bluetooth printer. The app requires the printer to be paired to the tablet, of course.
From time to time, the printer appears to receive a new pairing request from the tablet to which it is already paired, with the end result of breaking the bond.
I've been unable to determine the exact cause of it, for there are no errors in the log and it appears to happen randomly.
I've found these lines which I think is supposed to pair the printer:
Method method = device.getClass().getMethod("createBond", (Class[]) null);
method.invoke(device, (Object[]) null);
in the sequence
String printerMac = settings.getString("printerMac","");
if (!stampante.isEmpty()) {
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(stampante);
try {
Method method = device.getClass().getMethod("createBond", (Class[]) null);
method.invoke(device, (Object[]) null);
} catch (Exception e) {
e.printStackTrace();
}
}
Could it, for some reason, remove the bond on the printer even though it is supposed to do the opposite?
I couldn't reproduce this error on a Android 8 device.
From my experience (since i am also developing and maintaining an app that connects to a bluetooth printer) the pairing between the device and the bluetooth device can sometimes be lost although it is really rare. It is a bug that spreads amongst various devices and Android versions.
What i ended up doing is accept the possibility that it might sometime happen and implement a broadcast receiver to be notified that the pairing has been lost. At that time i create the bond again programmatically and also connect the printer.
When trying to create the bond, a default dialog will appear for the user to input the PIN the printer uses.However, since i know the PIN, i input this PIN progrmmatically so the dialog just appears for a split second and then dissapears.
After months battling with this issue this ended up to be the most elegant solution i could find.Hope it helps you as well.
In onCreate create the filter and register the receiver:
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECTED);
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
filter.addAction(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothDevice.ACTION_PAIRING_REQUEST);
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
registerReceiver(mReceiver, filter);
Method to call when you want to pair with the device:
private void pairDevice(BluetoothDevice device) {
try {
//Log.d("AutoPairing", "Start Pairing... with: " + device.getName());
device.createBond();
Log.d("AutoPairing", "Pairing finished.");
} catch (Exception e) {
Log.e("AutoPairing", e.getMessage());
}
}
Broadcast Receiver :
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
}else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
}else if (BluetoothDevice.ACTION_FOUND.equals(action)){
}else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){
}else if (action.equals(BluetoothDevice.ACTION_PAIRING_REQUEST)) {
try {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// If you want to auto_insert the pin, uncomment the following lines
//String PinNew = "HereIsThePinThePrinterUses";
//device.setPin(PinNew.getBytes());
} catch (Exception e) {
Log.e("AutoPairing", "Error occurs when trying to auto pair");
e.printStackTrace();
}
}else if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device.getBondState() == 12) {
// Pairing was succesful. Do what you would normally do after that. Perhaps connect the printer now.
} else {
// Pairing was unsuccesful. **This is also what get's triggered when the pairing is getting lost**. Let's pair the device then.
pairDevice(device);
}
}
}
};
Hope i am not forgetting something.
PM: All those empty if statements are there in case you want to connect the printer as well. If you do it some other way then you can delete them and remove those actions from the filter.

Bluetooth on Android: Debuging startDiscovery()

I'm working on an app that searches for discoverable devices and displays them as buttons.
When calling startDiscovery() I would say it works 30% of the time, based on the way I'm currently debugging it, with the BroadcastReceiver and ACTION_DISCOVERY_FINISHED.
I'm also using isDiscovering() to test if the startDiscovery() function is called but it returns false.
Is there a way to know if startDiscovery() is called successfully? And can you identify something in my code that would make it not fail?
Obs.: I have both BLUETOOTH AND BLUETOOTH_ADMIN permissions.
Here is my code:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scan);
mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
String Address;
// When discovery finds a device
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Get the BluetoothDevice object from the Intent
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Address = device.getAddress();
System.out.println("Found Address: " + Address ); //DEBUG
//Do something with Address
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
System.out.println("Discovery finished");
}
}
};
// Register the BroadcastReceiver
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(mReceiver, filter);
MainActivity.mBluetoothAdapter.startDiscovery();
if (MainActivity.mBluetoothAdapter.isDiscovering()) {
System.out.println("Discovering..."); //DEBUG
}
}
Although I have a few discoverable devices available, none of them trigger onReceive() with ACTION_FOUND
UPDATE: I went to "Scan" under Bluetooth Settings while the app was running and I could not scan for new devices. I disabled/enabled Bluetooth and returned to the app and the problem was resolved. I don't know if that indicates that the adapter is busy or halted somehow.
I confirm this issue.
On some telephones you just need to disable/active BT. You can doit programatically with
mBluetoothAdapter.disable();
mBluetoothAdapter.enable();
On some telephones its not enough ( Samsung S5 ). To detect it, I use timer, and if on end of timeout the change of BT broadcast state (BluetoothAdapter.ACTION_DISCOVERY_STARTED or BluetoothAdapter.ACTION_DISCOVERY_FINISHED ) wasnt received => its sign that BT is not working. Actually I show dialog which propose to user reboot the telephone.

Why BluetoothAdapter.startDiscovery(..) require to get Bluetooth device broadcast?

Normally how Android Broadcast work is: app have to create BroadcastReceiver and have to register action intent for it want to get receive event.
But in case of Bluetooth device discovery/scanning why it required request call through BluetoothAdapter.startDsiccovery().
Basically I want to dicover BLE device through long live Service running in Background.
Any one have idea here?
private void listenPairedDevice() {
Button listenBtn = (Button)findViewById(R.id.button_listen);
listenBtn.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
Intent disc = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
startActivityForResult(disc, DISCOVERY_REQUEST);
}
});
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == DISCOVERY_REQUEST) {
boolean isDiscoverable = resultCode > 0;
if (isDiscoverable) {
String name = "bluetoothserver";
try {
final BluetoothServerSocket btserver = bluetooth.listenUsingRfcommWithServiceRecord(name, uuid);
AsyncTask<Integer, Void, BluetoothSocket> acceptThread = new AsyncTask<Integer, Void, BluetoothSocket>() {
#Override
protected BluetoothSocket doInBackground(Integer... params) {
try {
socket = btserver.accept();
return socket;
} catch (IOException e) {
Log.d("BLUETOOTH", e.getMessage());
}
finally {
//close statement added later by MR
try{
btserver.close();
} catch (IOException e){
}
}
return null;
}
#Override
protected void onPostExecute(BluetoothSocket result) {
if (result != null)
changeLayout();
}
};
acceptThread.execute(resultCode);
} catch (IOException e) {
Log.d("BLUETOOTH", e.getMessage());
}
}
}
The startDiscovery() does a 2 step process,
Finding the device by inquiring for it.
Followed by a name discovery ie paging and connecting to the device.
If you have gone through the docs it says public boolean startDiscovery ()
Start the remote device discovery process.
The discovery process usually involves an inquiry scan of about 12 seconds, followed by a page scan of each new device to retrieve its Bluetooth name.
This is an asynchronous call, it will return immediately. Register for ACTION_DISCOVERY_STARTED and ACTION_DISCOVERY_FINISHED intents to determine exactly when the discovery starts and completes. Register for ACTION_FOUND to be notified as remote Bluetooth devices are found.
Device discovery is a heavyweight procedure. New connections to remote Bluetooth devices should not be attempted while discovery is in progress, and existing connections will experience limited bandwidth and high latency. Use cancelDiscovery() to cancel an ongoing discovery. Discovery is not managed by the Activity, but is run as a system service, so an application should always call cancelDiscovery() even if it did not directly request a discovery, just to be sure.
Device discovery will only find remote devices that are currently discoverable (inquiry scan enabled). Many Bluetooth devices are not discoverable by default, and need to be entered into a special mode.
If Bluetooth state is not STATE_ON, this API will return false. After turning on Bluetooth, wait for ACTION_STATE_CHANGED with STATE_ON to get the updated value.
Requires BLUETOOTH_ADMIN.
Returns
true on success, false on error
EDIT
Plese follow Bluetooth device discovery in Android — startDiscovery()
I think you have to start on Bluetooth discovery using UUId and socket.
After getting any device you have to set up a connection to this and start a thread that will check regularly while it is connected or not.
Scanning for peripherals is not just a heavy-weight task, it is also not friendly to the battery so it can't be left on all the time.
That said, you can still have something in the background doing the work for you, like something that wakes up every 15 seconds and scans for 5 seconds. On some devices when you try to connect to a device that is no longer available you will get a disconnected callback to onConnectionStateChange in the BluetoothGattCallback, and on other devices the same connection attempt will wait until the device is back and then connect.
So basically, wait, scan, connect if you find your device and if not then wait again.

How to detect if bluetooth device is connected

In android how can my Activity will get to know if a Bluetooth A2DP device is connected to my device.
Is there any broadcast receiver for that?
How to write this broadcast receiver?
Starting from API 11 (Android 3.0) you can use BluetoothAdapter to discover devices connected to a specific bluetooth profile. I used the code below to discover a device by its name:
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.A2DP) {
boolean deviceConnected = false;
BluetoothA2dp btA2dp = (BluetoothA2dp) proxy;
List<BluetoothDevice> a2dpConnectedDevices = btA2dp.getConnectedDevices();
if (a2dpConnectedDevices.size() != 0) {
for (BluetoothDevice device : a2dpConnectedDevices) {
if (device.getName().contains("DEVICE_NAME")) {
deviceConnected = true;
}
}
}
if (!deviceConnected) {
Toast.makeText(getActivity(), "DEVICE NOT CONNECTED", Toast.LENGTH_SHORT).show();
}
mBluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP, btA2dp);
}
}
public void onServiceDisconnected(int profile) {
// TODO
}
};
mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.A2DP);
You can do that for every bluetooth profile. Take a look at Working with profiles in Android's guide.
However, as written in other answers, you can register a BroadcastReceiver to listen to connection events (like when you're working on android < 3.0).
You cannot get the list of connected devices by calling any API.
You need instead to listen to the intents ACTION_ACL_CONNECTED, ACTION_ACL_DISCONNECTED that notifies about devices being connected or disconnected.
No way to get the initial list of connected devices.
I had this problem in my app and the way I handle it (didn't find better...) is to bounce off/on the Bluetooth at application start to be sure to start with an empty list of connected devices, and then listen to the above intents.
muslidrikk's answer is broadly correct; however you can alternatively use fetchUUIDsWithSDP() and see what you get back... it's a bit of a hack though -- you'd have to know what UUIDs (capabilities) you could expect from the device, if it were turned on. And that might be difficult to guarantee.
For BluetoothHeadset specifically, you can call getConnectedDevices() to get connected devices for this specific profile.
Reference: http://developer.android.com/reference/android/bluetooth/BluetoothHeadset.html
Other cases you need to register a receiver for that.
In your activity, define broadcast receiver...
// Create a BroadcastReceiver for ACTION_FOUND
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// When discovery finds a device
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Get the BluetoothDevice object from the Intent
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// Add the name and address to an array adapter to show in a ListView
mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}
}
};
// Register the BroadcastReceiver
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy

Categories

Resources