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

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.

Related

How long to wait to start BLE scan after it's enabled?

In Android, it is not possible to directly start a Bluetooth scan after you enabled Bluetooth on a device. If you would do this, you will get the following error:
D/BluetoothLeScanner: Scan failed, reason: app registration failed
The onScanFailed method will be called with error code 2 in the implemented ScanCallBack. Not much is known behind the reason of this error. But I found out the following (after a few hours of trying):
If you would wait ~5 seconds after you enabled Bluetooth and then start a scan (so not directly), it works. The scan starts with success. I came up with this temporary solution by the first answer of this question: Android BLE: "Scan failed, reason app registration failed for UUID"
As you can see that question is over a year old, however the questioner is using a separate Android library to handle BLE.
My question is, is there a better solution than the one I described above?
After enabling the Bluetooth adapter, it takes time for it to actually do all the initialization required, thus not allowing you to start scanning.
You can capture the broadcast event of the adapter's turn on event using the following broadcast receiver:
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
if (state == BluetoothAdapter.STATE_OFF || state == BluetoothAdapter.STATE_TURNING_OFF) {
if (state == BluetoothAdapter.STATE_TURNING_OFF) {
onBluetoothDisabling();
} else {
onBluetoothDisabled();
}
requestEnableBluetooth();
} else if (state == BluetoothAdapter.STATE_ON) {
onBluetoothEnabled();
}
}
}
};
Also, register the broadcast receiver in your onCreate() method:
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
registerReceiver(mBroadcastReceiver, filter);

Issue handling lost Bluetooth connections using BroadcastReceiver when connected to two devices

I have the following code to handle lost Bluetooth connections.
public class BluetoothReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
//call method to cancel connection thread
}
}
}
However, I have another app that uses Bluetooth connected to a different device running in the background. If I lose connection to that device, I also lose the connection to the device within this app.
I was wondering, is there any way to prevent this?
As described here you are able to get the device instance from the intent with intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) If the device is NOT the one you are interested in, just DO NOT call:
//call method to cancel connection thread

Auto reconnect socket from wifi to 4G

I have a socket that connects to a TCP server using either Wifi or 4G signal that receives a PING every 5 minutes from the server. But if I turn Wifi off on the device, I would like the socket to detect this and reconnect using a 4g signal.
I was wondering if there was a way to reconnect the socket automatically to 4G if I go out of the range of the wifi or if I turn wifi off?
Also is there a way to go the other way as well(From 4g to wifi if the wifi is available?
Register an inclass BroadCastReceiver to listen to WIFI on or off :
IntentFilter filter = new IntentFilter();
filter.addAction("android.net.wifi.supplicant.CONNECTION_CHANGE");
receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
boolean isConnected = intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false);
if (isConnected ) {
//Reconnect using Wifi.
} else {
//Reconnect using 4G.
}
}
}
registerReceiver(receiver, filter);
Dont forget to unregister the receiver:
#Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);
}
Regarding the EXTRA_SUPPLICANT_CONNECTED extra, the documentation states:
The lookup key for a boolean that indicates whether a connection to
the supplicant daemon has been gained or lost. true means a connection
now exists. Retrieve it with getBooleanExtra(String, boolean).
Constant Value: "connected"

Android Bluetooth headset connection

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.

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.

Categories

Resources