My android application is in blueotooth SPP server mode and listening for client devices,
my application knows the passcode required for pairing of that devices.
My question is,
Is it possible to handle pairing request through application.
Thanks and Regards.
No - because from a security point of view it is important for the user to be aware of pairing. The idea is that devices are paired and bonded once, then onwards connections happen automatically initiated by applications without need for re-pairing (or user intervention)
Yes, it is possible to do pairing through application. I did pairing in my application.
for this you have to made IBluetooth interface object accessible by this way:
IBluetooth mBluetoothService;
Field fie = Class.forName(bluetoothAdapter.getClass().getName()).getDeclaredField("mService");
fie.setAccessible(true);
mBluetoothService = (IBluetooth) fie.get(bluetoothAdapter);
By using this object you can pair with device using IBluetooth interface functions.
(normal Sequence for Auto Pairing)::
mBluetoothService.setPin(deviceAddress, PIN);
mBluetoothService.setTrust(deviceAddress);
mBluetoothService.createBond(deviceAddress);
mBluetoothService.setPairingConfirmation(deviceAddress, false);
mBluetoothService.cancelPairingUserInput(deviceAddress);
By using those function you can pair with any BT device programmatically.
Related
I have two BLE devices configured to work as peripheral(s) with simple static pass pairing program and Android phone as client.
1) peripheral_1 address = 0xCECECECECE with static passkey 123456, Device name = Garden, appearance = generic tag.
2) peripheral_2 address = 0xC1C1C1C1C1 with static passkey 123456, Device name = Garden, appearance = generic tag.
Both peripherals IO configured as display only.
I try to connect to peripheral_1 through nrfConnect app, I get pop-up to feed passkey, after giving correct passkey the devices are bonded and all good.
repeated the same procedure with peripheral_2 and all good as well.
Now the real problem comes in. After disconnecting with peripheral_2, I tried to connect to peripheral_1, I get pop again to feed in passkey for already bonded device, why?
Also, after feeding in the passkey I can't read any of the characteristics values unless I disconnect and re-connect to peripheral_1. Why?
Now I repeat with peripheral_2 and I see the same behaviour(client asks to feed in passkey again).
I believe the STK/LTK should be uniquely generated based on the BT address and store in client's database.
Suppose if I change the appearance of one of the peripheral to "unknown" then I see the client does not ask for passkey to re-enter again after bonding. Only with this combination
appearance it works("unknown" vs "").
Any inputs, suggestions are greatly appreciated.
My understanding is that the SDP is a list of UUIDs that other devices can fetch.
According to this PDF from MIT, "A more general way to think of
SDP is as an information database." Does this mean I can add multiple values to SDP? Since Android has BluetoothDevice.fetchUuidsWithSdp(), how do I set the UUIDs of a device?
Also, what does each section of an UUID mean? UUIDs look like 00000000-0000-1000-8000-00805F9B34FB, but what information does this convey?
An UUID identifies a service that is available on a particular device. So if you call BluetoothDevice.fetchUUidsWithSdp() your BroadcastReceiver will receive the relevant Intent ACTION_UUID containing the device and the service UUID.
The bluetooth specification defines some common UUIDs.
If you don't want to connect to one of these well known services but intent to implement your own bluetooth application, then you have to just generate your own UUID (use uuidgen from a unix console or an online generator) that identifies your application/service.
You can create an UUID instance in java like this UUID uuid = UUID.fromString("785da8ea-1220-11e5-9493-1697f925ec7b");.
So if you create the server side for your bluetooth application on Android you typically do this
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
BluetoothServerSocket serverSocket = adapter.listenUsingRfcommWithServiceRecord("YourHumanReadableServiceName", uuid);
And this is where you "set" your UUID. The Android bluetooth API creates the SDP-entry consisting of YOUR application's UUID and name for you. Other devices can now retrieve this entry. Androids bluetooth stack will now associate a bluetooth channel to your BluetoothServerSocket. If you want to connect to this ServerSocket, the connecting side usually connects doing this:
// you will most likely already have this instance from a discovery or paired device list
BluetoothDevice serverDevice = adapter.getRemoteDevice(bluetoothMacAddress);
// connect to your ServerSocket using the uuid
BluetoothSocket socket = serverDevice.createRfcommSocketToServiceRecord(uuid);
socket.connect();
Android will again do the heavy lifting for you: It checks the SDP-Records on the remote device, looks up the bluetooth channel that corresponds to your service's UUID and connects using this information.
There is a common code snippet spooking around here on SO that advices you to use "reflection" to get to a hidden API looking similar to this code:
try {
// this is the way to go
socket = device.createRfcommSocketToServiceRecord(uuid);
socket.connect( );
} catch ( IOException exception ) {
// don't do that! You will bypass SDP and things will go sideways.
Method m = device.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
socket = (BluetoothSocket) m.invoke(device, 1);
socket.connect();
}
Most people try this and it "just works" in their dev environment but you should know what you do using this. You actively bypass the SDP lookup that retrieves the right bluetooth channel to be used with your service and you will end up connecting to channel 1. If you have more than one Service running on the device, things WILL go sideways in this cases and you will end up in debugging hell ;-)
I developed a small middleware called Blaubot to create small networks using bluetooth/wifi/nfc and experienced all sorts of problems on the devices I used to test with (12 models). It was often the case that the bluetooth stack was not fully functional anymore in cases where it got some load or after many connects/disconnects (which you usually will have, if you are developing your app). In these cases the device.createRfcommSocketToServiceRecord(uuid) would occasionally fail and only turning the bluetooth adapter off and on again helped to bring the bluetooth adapters back to life (in some cases only after a full power cycle). If this happens and you use the reflection method, you will probably not have much fun with bluetooth.
But if you know this and keep concurrent calls to the BluetoothAdapter within bounds, bluetooth connections and the adapters will be pretty stable.
I'm trying to connect my app to the RN42 module.
// Create a socket based on the application ID with a paired device
// Fetch the published UUIDs from the mbed and use the first one
bluetoothSocket = connectedDevice.createRfcommSocketToServiceRecord(connectedDevice.getUuids()[0].getUuid());
// Connect to the device
if (!bluetoothSocket.isConnected())
bluetoothSocket.connect();
// Create the input and output streams for sending/receiving messages
socketInput = bluetoothSocket.getInputStream();
socketOutput = bluetoothSocket.getOutputStream();
I've got these in the Android Manifest
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
I'm getting this error when I call bluetoothSocket.connect().
Attempt to invoke virtual method 'void android.bluetooth.BluetoothSocket.connect()' on a null object reference
After calling this line
bluetoothSocket = connectedDevice.createRfcommSocketToServiceRecord(connectedDevice.getUuids()[0].getUuid());
I've inspected the variable bluetoothSocket using Android Studio and it's not null. It somehow becomes null when bluetoothSocket.connect() is called.
Is that the expected behaviour? What can I do to fix it? The RN42 module works fine as I've tested it with the RN Bluetooth Chat app on Play Store.
I'm on Android 5.1 on a Nexus 7 if that helps.
I've managed to sort the issue by removing my Bluetooth connectivity code and instead basing it around the Android Bluetooth Chat example. I don't know what the exact issue was but Bluetooth Chat example managed to fix it. Nothing obvious stands out so my best guess is, it was something subtle. If you are having a similar issue and connection between RN42 and Android is fiddly, create a sample Bluetooth Chat application and reuse that Bluetooth connectivity code.
Many less headaches! :)
Seethis reference guide for the module (p. 21) .
This might or might not apply to your case but is probably worth trying. They have special recommendations (default UUID and custom UUID respectively) for the module when connecting to Android devices.
Use the createInsecureRfcommSocketToServiceRecord instead. Insecure socket allows the RFCOMM to communicate with a non-authenticated paired device. Embedded devices like the RN42 or KC2114 have a difficult time performing authenticated pairing, because user interaction is required (numeric comparison, yes-no response). The "Just Works" automatic pairing will not produce an authenticated pairing. KC2114 supports both automatic authenticated pairing (with a small hack) and Just Works non-authenticated pairing.
What are the differences between these two ways below connecting to a bluetooth Device:
1)
UUID uuid = UUID.fromString(Values.SPP_UUID); //Standard SerialPortService ID
mmSocket = mmDevice.createRfcommSocketToServiceRecord(uuid);
2)
Method m = mmDevice.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
mmSocket = (BluetoothSocket) m.invoke(mmDevice, 1);
I find the first way cannot work all the time, sometimes it will work, but after I close the bluetooth device, it won't work again. The second way is always work well. I know it is just open the channel one to communicate with bluetooth, but I do not know how it can do that to connect to a bluetooth device without using uuid?
Think of it a bit like the difference between opening a TCP connection to a port you specify by number, and opening one to a port you look up by name from /etc/services.
createRfcommSocketToServiceRecord takes the UUID you pass and uses SDP to decide what radio channel to use for the connection. It also checks to make sure that a server is listening on the remote endpoint, with the same UUID. In this way, it's the most reliable way to get a connection: it'll always use the correct channel, and if opening the connection succeeds, you know something at the other end can understand your protocol.
In contrast, createRfcommSocket just connects to the channel you tell it. There's no way to know whether anything is listening on the remote endpoint: you only know the device is there. Also, your choice of radio channel may be completely inappropriate. That's why this function is not published in the API, and the other function is preferred.
createRfcommSocket may appear at first to be more reliable, but it's because it's not checking for the presence of a listener at the other endpoint: it's ignoring some error cases. This might be alright for experimenting, but it's no use for a production system, because often the user will forget to start the server on the other endpoint, and your app will fail in confusing ways.
Of course, as createRfcommSocket isn't published in the API, you've no guarantee it will continue to work at all in future releases of Android.
I have an app where I am programmatically controlling Bluetooth pairing and unpairing. I can pair before connection and unpair afterwards. The reason I need to do this is specific to my application and not in the scope of my question.
Basically what I am doing is:
Get a reference ib to IBluetooth object as described in this answer
Register a BroadcastReceiver for android.bluetooth.device.action.PAIRING_REQUEST
Call ib.createBond(address)
Wait for BroadcastReceiver to trigger
Convert user pin into bytes with convertPinToBytes()
Call ib.setPin(address, pinBytes) from within BroadcastReceiver
Anyways, this approach works great, except for the fact that when I do the pairing, I get a notification in the Status bar requesting that the user enter a PIN to complete the pairing. But this is in fact unnecessary, because by the time the user sees this, my app has already used setPin(). I'd really like for that notification to either a) not appear at all, or b) be dismissed automatically somehow.
I realize this may not even be possible, but I thought I would ask in case someone has a creative idea.
Try setting the confirmation first in the PAIRING_REQUEST
BluetoothDevice device = intent.getParcelableExtra("android.bluetooth.device.extra.DEVICE");
device.getClass().getMethod("setPairingConfirmation", boolean.class).invoke(device, true);
device.getClass().getMethod("cancelPairingUserInput").invoke(device);
This worked for me between two Android devices using RFCOMM but I'm not entering any PINs
Since Android API 19 Google switched these Methods to public Methods, so there is no need for Reflection any more. :)
Do this in the PAIRING_REQUEST notification event:
BluetoothDevice localBluetoothDevice = (BluetoothDevice)intent.getParcelableExtra("android.bluetooth.device.extra.DEVICE");
Class localClass = localBluetoothDevice.getClass();
Class[] arrayOfClass = new Class[0];
localClass.getMethod("cancelPairingUserInput", arrayOfClass).invoke(paramBluetoothDevice, null)).booleanValue();
But you gotta tell me how did you pair your remote device without the user to enter Passkey/PIN? off course, you know the PIN for the remote device which is trying to pair to your device but how did you provide that PIN to the remote device.