There are similar questions to this here already, but the answers and suggestions relate to older versions of Android. I understand that the bluetooth stack has been completely revised from 4.2 onwards and older solutions do not work anymore.
I have tried all the older solutions to no avail. the use of the private APIs no longer works because they have changed. I dont mind using private APIs but it must work on the newest versions and later (ie API 17+)
I am trying to do the following:
set up a bluetooth pairing between an Android device and an embedded device using legacy PIN pairing without the embedded device being discoverable nor the user having to manually enter the PIN. In fact I want no PIN entry dialog box at all.
The plan is that the two devices have a predefined shared secret PIN, so that I can perform the pairing programmatically and then open an RFCOMM connection between them. All of this without UI. The hardware address of the embedded device is known to the Android program.
There is no security issue here. the project involves just talking to a nearyby, small embedded device through BT as simple as possible.
Ideas that might work on Android 4.2 (Jelly Bean) most welcome, thanks.
turns out some of the problem was inside the embedded device. on the Android side, the following works:
BluetoothSocket s = null;
try
{
s = device.createInsecureRfcommSocketToServiceRecord(SerialPortServiceClass_UUID);
}
catch (IOException e)
{
Log.e(TAG, "BT connect failed", e);
return false;
}
where
private static final UUID SerialPortServiceClass_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
Related
I'm slightly familiar with BLE and I am facing some problem with an inherited code. So the app works like that:
With BLE enabled the app scans for devices
The app displays the devices found
The user selects the device to pair with
The app pairs with the device
The problem I'm facing is that after pairing several times (it varies) the phone is not able to discover devices, hence blocking the user to pair.
I'm using GattServer to connect with the client device, and I'm reseting the services as below:
public void resetBluetoothGattServer() {
Log.i(TAG," resetBluetoothGattServer: bluetoothGattServer: "+ bluetoothGattServer);
if (bluetoothGattServer != null) {
if(!bluetoothGattServer.getServices().isEmpty()){
Log.i(TAG," resetBluetoothGattServer: clearing services on bluetooth Gatt Server");
bluetoothGattServer.clearServices();
}
Log.i(TAG," resetBluetoothGattServer: closing bluetoothGattServer");
bluetoothGattServer.close();
}
bluetoothGattServer = openGattServer();
}
Restarting the phone, turning bluetooth off and then back on, and uninstalling and installing the app won't fix the problem. The only solution is to clear the cache from the Bluetooth Share app on the android apps manager.
This post How to programmatically force bluetooth low energy service discovery on Android without using cache adresses to a similar problem but since we are not using BluetoothGatt to connect it's no a suitable solution. Neither will be to refactor the whole inherited code.
I'm asking you if there is a way to clear the cache programmatically using BluetoothGattServer.
One solution - solve this issue using reflection.
private void refreshDeviceCache(BluetoothGatt gatt) {
try {
Method localMethod = gatt.getClass().getMethod("refresh");
if(localMethod != null) {
localMethod.invoke(gatt);
}
} catch(Exception localException) {
Log.d("Exception", localException.toString());
}
}
Note : I am not recommended this way
We are using DiscoveredPrinterBluetooth to keep discovered printer, ZebraPrinterConnection and ZebraPrinter to send data to printer. Everything is working ok on older android versions, but on let say 4.0 is not working, finds printer but keeps showing insert pin screen. Get error in log:
V/BluetoothSocket.cpp(5371): ...connect(96, RFCOMM) = 111 (errno 111)
As I see there is problem with newer versions, looking at
Bluetooth pairing without user confirmation
but I don't know how to solve it in my case? Any ideas? Thanks
The 2.0 version of the Zebra SDK has an implementation of insecure bluetooth connections.
Check out BluetoothConnectionInsecure in the new SDK. This could be used instead of BluetoothConnection if you want insecure BT connections...
Alright here's the deal. I got two Galaxy Nexus phones both with bluetooth enabled.
I've written a bluetooth connection management app that I use for device discovery and connectivity. It also outputs all the available UUIDs the devices can support.
Looking from http://www.bluetooth.org/Technical/AssignedNumbers/service_discovery.htm the following standard UUIDs are exposed from Galaxy Nexus devices.
0x1116 - NAP
0x112f - PBAP (Phonebook Access Profile)
0x111f - HFP (Hands free)
0x1105 - OPP (Object Push Profile)
0x1112 - HSP (Headset Profile)
0x110c - AVRCP
0x110a - A2DP
I am trying to connect via the OPP profile (UUID 00001105-0000-1000-8000-00805F9B34FB) and push objects (files) between the devices. I've gone though the entire Android API documentation on how to discover, pair/bond (threading etc.) and manage all bluetooth connections. I've managed to successfully connect and talk to a legacy board device via the SPP (0x1101) profile.
However, when I try to use socket.connect() between the two galaxy nexus phones, the pairing dialog shows up and I click Pair button on both devices. After that, I immediately get a Connection Refused IOException. Note that after pairing has occurred once I never get asked again which makes sense since the secure link is cached.
If I can't connect to these standard profiles using these standard UUIDs why are they exposed? How can I connect from my app to any of these profiles and interact with them? Is it because my app is not somehow trusted? What's weird is that even the Share functionality on Android does not work at all either. Is this something completely broken on Android?
Please avoid giving me hints to use the "well known UUID SPP one 0x1101" like the docs say. This is not what I want. I have a fairly good understanding of how this stuff works and I am looking for a real solution or explanation of the problem.
I've seen the typical "reflection" solution but I do not understand why is this still a problem on Android? Why do people use reflection to make this work? Can we file a bug on Android to fix this?
If those UUIDs are standard any app should be able to connect and interact with them. Why is this an issue and why do I get this exception?
Thanks in advance.
UPDATE
So for some reason the object push in the Android system started working. I actually attempted to connect via my app and it was not working. Then, I went to the Contacts app and tried to share a contact which magically worked. Then, I went back to my app and it now it works...wow. That is very weird and there must be an explanation to this.
I ran into this same issue and managed to find a solution that worked for me.
In my case I using three different test devices (Nexus 5, Galaxy S4, Note 2) and for some reason, the Note 2 wouldn't connect to my Bluetooth module yet the other two would.
The reasoning I've found is that Bluetooth drivers vary, and slightly different connection methods are needed to create a connection between different devices.
The three methods I use are called 'Secure', 'Insecure' and 'Reflection method'/'hax'.
switch(connType)
{
case Secure:
tmpSocket = device.createRfcommSocketToServiceRecord(_uuid);
break;
case Insecure:
tmpSocket = device.createInsecureRfcommSocketToServiceRecord(_uuid);
break;
case Hax:
Method createSocket = device.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
tmpSocket = (BluetoothSocket)createSocket.invoke(device, Integer.valueOf(1));
break;
}
In my case, the Secure mode worked for both the Nexus 5 and Galaxy S4 however it didn't work for the Note 2.
After some testing I discovered the Note 2 only works using 'Insecure' mode, so to cater to this, I basically attempt a connection and cycle through the different modes if necessary. When attempting a different connection mode I simply prompt 'retrying connection'. So, if the connection fails using secure, then I will attempt using Insecure and then using the reflection method.
I haven't run into the case where one of these three methods haven't worked.
Have you tried using a nonstandard profile? i.e. a custom UUID just for your app. This will also help you know your are (most likely) only connecting to your own app rather than some other app that is registered with the same profile.
From my experience, Bluetooth pairing is very buggy for the first pair attempt. However, using a custom UUID helps this somewhat.
The reflection method (I think) was originally an attempt to fix a bug with a specific device, however I think some people found success in using it elsewhere as well. The device was called the Spica or something similar.
As one of the comments also posted, I would also try connecting again after failing.
Basically write code that plans to fail the first attempt, but then the code tries to connect again in 5 seconds if there was a failure.
These are imperfect solutions but Bluetooth implementation on Android is also imperfect (IMHO). Hope that helps
EDIT
Based on the question update and comments:
I agree something is definitely buggy. Part of the problem I think is the BT drivers vary and each has a different BT stack with different quirks. I also found a question that makes use of both the reflection method AND custom UUID, AND other standard methods. This seems extreme to me but it does cover the most ground. Unfortunately as app developers we have no control over the low level stack/code/drivers.
I have found with my two Bluetooth sharing apps that the first pairing is always tricky.
I'm glad to know it's not just me.
I am working on using the BT 4.0 API that Motorola has provided with the RAZR. In one of their documents it states to use the Android API to pair before connecting and using their framework. Per their instructions I have been pairing with OS Bluetooth settings application, but it never prompts me for a key. It will pair but doesn't appear to bond, and this is critical for me.
My question is, when they say "using the Android API" is this referring to simply using the OS Bluetooth utility to pair before hand (like I have been doing), or is there some way to do it with code in my application. They reference the "createBond()" function which, to my knowledge, is not an accessible function (at least not without some squirrely libraries or reflection).
Any advice is greatly appreciated, especially anyone who has used the API successfully, if they could give an account of their process. I'm just looking for some clarity at this point :)
Lloyd,
You are correct, follow the instructions in the link you posted.
Outside of coding, when they say use the standard android api for "non-le" operations, they mean go ahead and pair the ble device the same way you would any bluetooth classic devices inside android settings -> wireless & network -> bluetooth -> scan for devices.
If the device you are using is a motorola le compatible device the ble device will be paired but not connected.
Now, in the code, you can detect this paired device through the same method of
BluetoothAdapter.getDefaultAdapter().getBondedDevices()
To double check if your Android Phone is LE compatible, run this code:
public static boolean checkBLESupport() {
boolean deviceSupportsLE;
try {
#SuppressWarnings({ "unused", "rawtypes" })
Class object = Class.forName("android.server.BluetoothGattService");
deviceSupportsLE = true;
} catch (Exception e) {
deviceSupportsLE = false;
}
return deviceSupportsLE;
}
And to double check if the bluetooth device you paired is LE, when you are looping through the bonded devices.
Check the device with this code.
if (device.getBluetoothClass() == null) {
Log.i(TAG, "This device is BLE compatible");
b = true;
} else {
Log.i(TAG, "This device is not BLE");
b = false;
}
Now for establishing connection from your LE compatible phone to your LE compatible bluetooth device, follow the Gatt service instructions under the link you posted. http://developer.motorola.com/docs/bluetooth-low-energy-api/
Take note that under this example it is connecting to a bluetooth low energy heart rate monitor.
If you are not trying to connect to the heart rate monitor with LE heart rate profile, here is a link to another Motorola document that details creating your own LE Profile to use with the GATT framework. http://developer.motorola.com/docs/bluetooth-low-energy-gatt-framework-api/
If the instructions are not clear enough at any point in either of these documents, motorola offers sample android applications using the frameworks in those documents.
I guess motorola stack has BLE support. But what i feel is that it does not pair with the devices that require bonding though It does work some sensors. I have tried with a proximity sensor that require bonding. It never gets paired though the devices is discovered with Razr which even does not with S3.
There's a helpful video here.
Late to the game, but can confirm -
If your BLE Peripheral requires bonding, Moto X - and some other older Motorola devices - MUST be paired via Bluetooth Settings prior to programmatic connection via the Android GATT interface.
If you bond via the createBond method, or reading of an encrypted characteristic, your connection will be dropped typically in under 60 seconds, despite DDMS logs that show a good bond may be established.
I have been playing around with the bluetooth API for Android 2.2 (API level 8, HTC Desire) and had an app connecting to an embedded Bluetooth device using:
device.createRfcommSocketToServiceRecord(DEV_UUID);
This generated a pairing request as expected, however to streamline the connection process I wanted to avoid the user interaction when pairing so moved to API level 10 (HTC Desire with CyanogenMod 7) so I could use:
device.createInsecureRfcommSocketToServiceRecord(DEV_UUID);
When testing this also works as expected (connecting without prompting the user to pair), however when I try to create the secure RfcommSocket under API level 10 as before with 2.2 I get a connection refused exception...
java.io.IOException: Connection refused
at android.bluetooth.BluetoothSocket.connectNative(Native Method)
at android.bluetooth.BluetoothSocket.connect(BluetoothSocket.java:204)
As far as I can tell this should still work in the same way, prompting the user to pair?
EDIT:
Just tried again using the following code and the outcome is the same (working for insecure but not for secure), I will try and get my hands on a stock 2.3 device to test on.
try {
Method m = dev.getClass().getMethod("createInsecureRfcommSocketToServiceRecord", new Class[] { UUID.class } );
BluetoothSocket bs = (BluetoothSocket)m.invoke(dev, devUUID);
Log.d("TEST", "Method Invoked");
bs.connect();
Log.d("TEST", "Connected to socket");
bs.close();
Log.d("TEST", "Closed Socket");
}
While looking for the solution of similar problem in my app, I have found this blog from code.google.com
It will help all those who are still looking for this problem solution on SO
http://mobisocial.stanford.edu/news/2011/03/bluetooth-reflection-and-legacy-nfc/ (link not working anymore)
The solution has become very simple now. Just include InsecureBluetooth.java in your project and change 2 lines in BluetoothChatService.java.
tmp = InsecureBluetooth.listenUsingRfcommWithServiceRecord(mAdapter, NAME, MY_UUID, true);
and
tmp = InsecureBluetooth.createRfcommSocketToServiceRecord(device, MY_UUID, true);
Thats it !