Can't accept an incoming bluetooth connection unless device is discoverable - android

I've hit my wits end with this one.
I have a typical BluetoothServerSocket that I'm trying to get the A&D Weight scale (UC-321PBT) to connect to. Their company has an app (myFitnessCompanion) which doesn't seem to have many connection issues, yet the scale will only connect to my app if I have the phone 'discoverable'. Mind you this is after I pair with the scale.
The only key code that is really needed here is how the listener is open, beyond that it's typical Bluetooth
serverSocket = adapter.listenUsingRfcommWithServiceRecord("PWAccessP", UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
(I have also tried listenUsingInsecureRfcommWithServiceRecord and even reflection to open a port)
I interrogated my phones Bluetooth from another phone to list get the 'PWAccessP' value used by the myFitnessCompanion application, and even used the app to get the scale to pair with my phone.
The scale WILL enter into my socket listener when I make the phone discoverable, but other than that I see the BluetoothDevice begin making the low level connection monitoring the intent 'BluetoothDevice.ACTION_ACL_CONNECTED', but unless I have the phone in a discoverable mode it will not trigger my server socket's 'accept()' method. The pairing screen does not appear on new connections.
Any help would be appreciated. Considering I have to have the phone 'discoverable' for the connection to occur in my app vs the myFitnessCompanion allowing the connection at any time after pairing I can't see how the myFitnessCompanion is getting around this.
Thanks in advance.

A and D device work as Master and the android device as the slave. So the android device must be in discoverable mode. Me tested with both listenUsingRfcommWithServiceRecord and listenUsingInsecureRfcommWithServiceRecord method.
try {
tmp = mBluetoothAdapter
.listenUsingInsecureRfcommWithServiceRecord(NAME,
MY_UUID); // #2
// tmp.accept();
// tmp =
// mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME,
// MY_UUID);
// tmp =
// mAdapter.getRemoteDevice("").createRfcommSocketToServiceRecord(MY_UUID);
mServerSocket = tmp;
mPortNumber = getPortNr();
} catch (IOException e) {
//Log.e("DROID", "listen() failed", e);
flag=false;
return flag;
}

Related

How to make Android connect after the Bluetooth Adapter has been disabled & reenabled?

I have written an app that connects to a BLE device. The app works OK on most devices; but some devices (most noticeably Huawei P8 Lite and Nexus 6P) refuse to connect after the Bluetooth adapter has been disabled.
This is the test sequence:
Make sure the app is NOT running.
Slide down from the top, disable BT for a couple of seconds, then re-enable bluetooth.
Start the app. The app automatically connects to a bluetooth address stored in the preferences.
Wait for connect. This is where nothing happens on Huawei phones, but other phones, such as Samsung, works like a charm.
Verify from another phone the device is advertising and you can
connect to it.
This is the code I use to connect:
private final Runnable mBeginConnectRunnable = new Runnable() {
#Override
public void run() {
synchronized (GattConnection.this) {
if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
try {
mBluetoothAdapter.cancelDiscovery();
mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(mAddress);
mGatt = mBluetoothDevice.connectGatt(mContext, mBackgroundConnect, mGattCallback);
final boolean connectSuccess = mGatt.connect();
Log.d(TAG, String.format(Locale.ENGLISH, "mGatt.connect(%s, %s) %s",
mAddress,
mBackgroundConnect ? "background[slow]" : "foreground[fast]",
connectSuccess ? "success" : "failed"));
refreshDeviceCache(mGatt);
} catch (Exception ex) {
Log.e(TAG, "Create connection failed: " + ex.getMessage());
setState(State.Closed);
}
} else {
Log.d(TAG, "Can't create connection. Adapter is disabled");
setState(State.Closed);
}
}
}
};
All calls are posted via a Handler to the main thread. I can see it waits for a connect, gives up after 30 seconds at which I call BluetoothGatt.close() on the object and nulls it. It's like nothing is out there.
After some time, later in the day, it works again.
Help is highly appreciated :-)
Update September 14, 2018: After great explanation from Emil I've updated our app and as such don't have this problem on the Nexus. I've noticed the Huawei P8 Lite continues to scan in the background and it seems there is nothing you can do to stop it.
To demonstrate the problems I've made a very simple and clean app that exercise the Bluetooth LE functionality on a phone and used it to demonstrate this problem and also the P8 is broken. The app is available here: https://play.google.com/store/apps/details?id=eu.millibit.bluetootherror
Source is available here: https://bitbucket.org/millibit/eu.millibit.bluetootherror/src/master/
I hope I over time can extend this app to make it a test vehicle for Android documenting all the stange behavior from Android and collect it in a database. In case you are interested in contributing, don't hesitate to drop me a mail on bt.error#millibit.dk
The Android Bluetooth stack has a design flaw in its API. When you connect to a specific device by Bluetooth Device Address, there is no way to tell if you mean a Public address or Random address.
If you start to connect to a device with autoConnect=false which is not bonded and has not recently been seen in a Scan, it will assume you mean a Public address. So if you try to connect to a device having a static random address, it will fail.
To be sure you connect with the correct address type if the device is not bonded, you MUST perform a scan first, find the device and THEN start the connection attempt.

Android BLE reconnection very slow

Background:
I have a BLE peripheral with two modes: "Application" and "Bootloader". In both modes, the device advertises with the same MAC address.
To switch from one mode to the other, the BLE peripheral must reboot itself. In doing so, it has to disconnect any active BLE connection.
The BLE peripheral only stays in Bootloader mode for about 5 seconds. If nobody connects to it within that window, it switches to Application mode.
The Problem:
Android takes a very long time to reconnect to the BLE device, long enough that I'm missing the 5 second window. The raw code has a few layers down to the BluetoothGATT and BluetoothAdapter layers, but the sequence of calls boils down to:
BluetoothGattCharacteristic c = mCharacteristics.get(POWER_STATE_UUID);
c.setValue(SHUTDOWN_VALUE);
mBluetoothGatt.writeCharacteristic(c);
// Signalled by BluetoothGattCallback.onCharacteristicWrite
bleWriteCondition.await();
mBluetoothGatt.disconnect();
// Wait for the underlying layer to confirm we're disconnected
while( mConnectionState != BluetoothProfile.STATE_DISCONNECTED ) {
// Signalled by BluetoothGattCallback.onConnectionStateChange
bleStateCondition.await();
}
mBluetoothGatt.connect();
while (mConnectionState != BluetoothProfile.STATE_CONNECTED) {
// Signalled by BluetoothGattCallback.onConnectionStateChange
bleStateCondition.await();
if (bleStateCondition.stat != 0) {
break;
}
}
Am I going about this entirely the wrong way? I've tried calling close() on the BluetoothGatt instance, then generating a new one with BluetoothDevice.connectGatt, but I get the same extremely slow behavior.
I'm testing on a Samsung Galaxy S4, API level 21.
The problem here is that the gatt connect call issues a background connection request. It can take quite a long time for this call to result in a connection. A description of the two types of connection request is here : Direct vs Background connections
The absolute fastest way to get a connection is to do a scan and upon finding your device issue a direct connection request to it. As the scan has just found it, you know it is there and the connection will complete quickly. This is more complicated than your example code, but will be most effective given your small window. A scan is the most aggressive way to find a device. However, if you already have the device object, you could just call a direct connection request on the device.
Scans are issued using code like this :
scanner = bluetoothAdapter.getBluetoothLeScanner();
settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build();
filters = new ArrayList<ScanFilter>();
ScanFilter uuidFilter = new ScanFilter.Builder()
.setServiceUuid(YOUR_SERVICE_UUID).build();
filters.add(uuidFilter);
scanner.startScan(filters, settings, myScanCallback);
Upon finding your device (using the scan callback), issue a direct connection request via this method call :
myGatt = myDevice.connectGatt(this, false, myGattCallback);
The key part being the parameter of false. The connection request will time out in around 30s if the device is not found.

accept() and connect() methods in bluetooth socket

I am getting bluetooth socket connection problem and so tried various alternatives to connect.
It is like if first fails then second and if second also fails then third and so the main UI blocks during the process.
The exceptions are like -
"Service discovery failed" OR "Host is down"
My all three alternatives -
1) Connect with a connect() function
m = bdDevice.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
socket = (BluetoothSocket) m.invoke(bdDevice, Integer.valueOf(i));
mBluetoothAdapter.cancelDiscovery();
socket.connect();
2) Connect with a accept() function
m = bdDevice.getClass().getMethod ("listenUsingRfcommOn", new Class [] {int.class});
BluetoothServerSocket returnValue =
(BluetoothServerSocket) m.invoke(bdDevice, new Object [] {29});
socket = returnValue.accept();
3) Connect with a well know SPP UUID
private final UUID my_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
socket = bdDevice.createInsecureRfcommSocketToServiceRecord(my_UUID);
mBluetoothAdapter.cancelDiscovery();
socket.connect();
The second alternative is recently added and so I am not sure that how is it different from first and third one. All will block the UI until connect, all can through IOException.
1) You are connecting as a client to your SPP device. Therefore your device must be expecting a connection before you can actually connect.
2) You are accepting the connection as the server. Your bluetooth device will be responsible for firing the connection requests.
3) You are connecting to a device that uses a particular UUID as it's Serial Port Profile (SPP). Think of this as similar to ports in http protocol. The 00001101-0000-1000-8000-00805F9B34FB, is one of the most common ones. More info : https://developer.bluetooth.org/TechnologyOverview/Pages/SPP.aspx
Additionally, if you are thinking on supporting multiple OS / handset versions. You might see those error messages after an initial broken comm. Android's bluetooth stack (bluez) was totally replaced on 4.2, but you might need to still handle previous known bugs.
Reflection in this case is a good thing (some parts of the bluetooth API wasn't public in older versions). But, in my experience, API level < 17; using createInsecureRfcommSocket instead of createRfcommSocket is much more reliable.
Hope this helps. But if you are building an app for a custom bluetooth spp device of yours, explaining how that devices handles comms could help pointing out the exact root of your problem. Although, bluetooth on Android is never straight forward.

Bluetooth connection between Android and another phone over the Handsfree profile

I'm trying to use my Android phone as a handsfree kit (like the one for cars) in order to connect to another phone (any phone) and perform some handsfree functionality like (answer an incoming call, reject,.. etc) which can be done using the AT commands for handsfree profile.
For that, I'm using the well-known Bluetooth chat App, and reflection work around in order to establish a connection with any device:
Method m = device.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
tmp = (BluetoothSocket) m.invoke(device,1);
However, in order to achieve the handsfree functionality and understand the AT commands that I'm sending, the connected phone needs to be over the handsfree profile which uses the UUID: "0000111f-0000-1000-8000-00805F9B34FB"
Therefore, is there a way to achieve a connection to the handsfree profile?
Thanks!
You should only use this code when you have no other choice. The 1 in this code is the RFCOMM port. Each service has it's own RFCOMM port. This port is usually random between 1 and 31. You need to know which port the service (here handsfree profile) is using on the device that you want to connect to. You have to use the createRfcommSocketToServiceRecord method from the BluetoothDevice object to do this:
try { clientSocket = bluetoothDevice.createRfcommSocketToServiceRecord( serviceUUID ); }
catch (IOException e)
{
// handle error
}
This code is the correct way to use Bluetooth and should replace the one you're using.

RFCOMM_CreateConnection - already opened state:2, RFC state:4, MCB state:5

I´m writing an android app that connects to a device through bluetooth using RFCOMM. I use the BluetoothChat example as basis for establishing a connection and everything works perfectly most of the time.
However, sometimes I cannot reconnect due to a message that the socket is already open:
RFCOMM_CreateConnection - already opened state:2, RFC state:4, MCB
state:5
This tends to happen if I connect to the device, close the app (call onDestroy()), reopen it and try to connect again, which results in the above.
I use this method for connecting in the ConnectThread(ref.BluetoothChat example):
Method m = device.getClass().getMethod("createRfcommSocket",new Class[] {int.class });
tmp = (BluetoothSocket) m.invoke(device, Integer.valueOf(1));
mmSocket = tmp;
The only thing that resolves this problem is turning off/on the Bluetooth of the Android phone.
This leads me to believe that the socket is not being closed in onDestroy() but still I´m calling on closing all threads as shown in the before mentioned example.
Any ideas would be appreciated.
I stumbled upon this one too, and here is the answer I found:
This error may happen, if you open and close a bluetooth socket connection multiple times.
Solution
Starting from API Level 14 there is a Method in BluetoothSocket called isConected(), which returns true, if this socket is already connected and false otherwise, here the original excerpt from the API:
Get the connection status of this socket, ie, whether there is an
active connection with remote device.
For API levels < 14 you can work around this issue by putting your Bluetooth Handling Thread to sleep after closing the connection - 1000 ms should be enough, here is an example (btDevice is of the type BluetoothDevice and has been initialized prior to the code snippet below):
try {
//Open the socket to an SPP device (UUID taken from Android API for createRfcommSocketToServiceRecord)
BluetoothSocket btSocket = btDevice.createRfcommSocketToServiceRecord("00001101-0000-1000-8000-00805F9B34FB");
//Connect to the socket
btSocket.connect();
//Close the socket
btSocket.close();
//Sleep time of 1000ms after closing the socket
SystemClock.sleep(POST_RESET_DELAY);
} catch (Throwable e) {
// Log error message
}
P.s. Instead of SystemClock.sleep you can also use Thread.sleep - however the SystemCock's sleep can't be interrupted, whereas the Thread.sleep can be interrupted, so it depends on your use-case which option better suits your purpose.
Source: Louis A. Prado

Categories

Resources