I have a list of BluetoothDevice and I want to pair programmatically with one of them that has a PIN.
I have read several posts here in which the subject is discussed, but I've found two very different approachs.
FIRST OPTION: You call the device.createBond() method. Then, on a BroadcastReceiver, you listen the BluetoothDevice.ACTION_PAIRING_REQUEST action and there you call
device.setPin(PIN_BYTES);
device.setPinConfirmation(true);
You can see the complete example & post here: How to pair Bluetooth device programmatically Android
SECOND OPTION: What if you call device.setPin(PIN_BYTES) and device.setPinConfirmation(true) first, and then device.createBond()? Eg:
if(connConfig!=null && connConfig.bluetooth!=null){
device.setPin(connConfig.bluetooth.pass);
device.setPairingConfirmation(true);
device.createBond();
}
And then you forget about listening the BluetoothDevice.ACTION_PAIRING_REQUEST action on your BroadcastReceiver and only pay attention to BluetoothDevice.ACTION_BOND_STATE_CHANGED events? Android + Pair devices via bluetooth programmatically
See in the first answer the code. As far as I get it, that dude isn't using any PIN for pairing, so I also need to use the setPin method.
Which of both you find better?
Am I missing something? BTW: I'm not using reflection because Im not targeting older platforms.
Related
How can we listen for special BT device commands like redial from our app? For now, I'm only able to listen to the only one - play/pause/start/end call button (KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE).
Using common BroadcastReceiver for "android.intent.action.MEDIA_BUTTON" doesn't help.
Solution with BluetoothSocket, BluetoothServerSocket won't help too since it requires our code to be invoked on BT device side.
During my redial button tests I see the next line in the logs:
01-20 05:52:30.486 942-1060/com.android.bluetooth E/bt-rfcomm: PORT_DataInd, p_port:0x5526c200, p_data_co_callback is null
It looks like there is something sending an event from BT device to the android device. But how can we catch it on app side, what should we use? I work on some system app by the way and theoretically can do very specific, low-level and system things, so maybe there could be some solution.
afaik, this isn't possible, sadly...
I've been working on custom handling BT headset keys, like VOL UP, DOWN, eventually ANSWER/DISCONNECT/REDIAL. Even made rich question, but without single answer or comment...
After some research (days, weeks...) and digging into Android source I've found that these buttons are sending some AT commands. I've also found methods which are checking these AT commands and if system is able to respond/handle them then it TRY to do it and further won't pass any event to any app/socket/rfcomm/anything... E.g. under VOL UP button we have some well-known AT command, system can handle it, so try to do so, even when we already have volume set to max. Any app won't be noticed that this happened...
btw. I don't think this logcat line posted in question is strictly relevant to button press (but may be indirectly), but you have bt-rfcomm keyword in there, so you may try to establish some RFCOMM connection with Bluetooth device, maybe you will get some luck on this topic... (personally I gave up...)
In my application, I implemented the classic Bluetooth according to the official documentation: https://developer.android.com/guide/topics/connectivity/bluetooth.
By default startDiscovery() will scan for all the nearby bluetooth devices. But in many situations user/developer already knows the desired category of devices. In my case, you need to find a specific device that implements the method:
bluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord(SERVICE_NAME, MY_UUID_INSECURE);
Ideally, you need to implement the search and connect two devices in one click. How can I select the desired device from the list of found devices? This is for the convenience of using the application, so you do not have to choose from a variety of unnecessary devices. It will be a kind of filter like in BLE. But we are talking about Bluetooth Classic. How can I use SERVICE_NAME, MY_UUID_INSECURE, BluetoothClass to accomplish this task?
EDIT:
I found several solutions to this problem. But they're all not perfect. For now, I just exclude from the list of devices those that have a device.getType() == 2 (DEVICE_TYPE_LE).
Option 1
Search for a device that broadcasts the service with the same UUID as mine. To do this, use the fetchUuidsWithSdp() method for the found devices. Example implementation: https://stackoverflow.com/a/37070600
The disadvantage of this method. Time. You should wait until you receive BluetoothAdapter.ACTION_DISCOVERY_FINISHED before you make any calls to fetchUuidsWithSdp(). It will take 12-18 seconds. In addition to this one must wait for each subsequent call to fetchuuidsWithSdp() to complete, and then give a call to this method for another device. It will take about 3 seconds per device. In total, it can take a very long time to find the right device.
Option 2
Change the device name to a private key or a special name that the client can use to identify the device.
bluetoothAdapter.setName(name);
The main thing is not to forget to return the device name to the original.
saveName = bluetoothAdapter.getName();
Example implementation: https://stackoverflow.com/a/40138077/4716092
The disadvantage of this method. Changing the device name is not so easy in reality. If you do not return the old name of the device, the user may be upset.
I have been reading the Bluetooth documentation on Android's developer site.
I want to pair a Bluetooth device and then connect to it, but I don't want the request dialog to pop up (or even if it does set the pin using some API to dismiss it). I want to do it in Android 4.3+.
So far I see this involving the following steps:
1) App discovers device : use BroadcastReceiver to get BluetoothDevice
2) App pairs the device : uses BluetoothDevice.createBond(), and registers a BroadcastReceiver to get confirmation of pairing completion
3) App connects to device : uses BluetoothDevice.createRfCommSocketToServiceRecord(UUID)
My understanding is that the pop dialog for pairing will be shown to the user after step 2.
I know there is a setPin() API (now public) in the BluetoothDevice class.
If I call it right after step 2, will the dialog still appear and wait for user input, or will it appear and then dismiss, resulting in pairing success ?
Also does step 3 involve any input from the user ?
Step 3 doesn't involve user input.
I've been trying to do exactly the same, I use setPin() and it works, but the user Input dialogue is still there, there is an option to use cancelPairingUserInput but i'm not sure if this is still valid for API19 (that is the one which uses setPin() as far as I know...) or if there is another way to do it.
Hope this helps...
Is there any way for Android to connect to a Bluetooth device using a specific port instead of using service UUID?
I know this option is available in other platforms which provide Bluetooth support (Java ME for example by specifying a "btspp://" style URL).
Thanks!
Ok, it's been a while, but I found a solution to the problem. I actually intended to give up and use UUID, but I kept getting a Service Discovery Failed (IO)exception, and when I tried to find a solution to the service discovery issue, I found the solution to my original question... Ain't life something?:)
Anyways, this is the link I stumbled upon, though you should note there is a mistake in the answer (they actually simply connected to port 1, instead of using a service UUID).
And after this short history lesson, here is the solution:
Using reflection, it is possible to create the Rfcomm socket connecting to a port number instead of UUID:
int bt_port_to_connect = 5; // just an example, could be any port number you wish
BluetoothDevice device = ... ; // get the bluetooth device (e.g., using bt discovery)
BluetoothSocket deviceSocket = null;
...
// IMPORTANT: we create a reference to the 'createInsecureRfcommSocket' method
// and not(!) to the 'createInsecureRfcommSocketToServiceRecord' (which is what the
// android SDK documentation publishes
Method m = device.getClass().getMethod("createInsecureRfcommSocket", new Class[] {int.class});
deviceSocket = (BluetoothSocket) m.invoke(device,bt_port_to_connect);
A few things to notice:
since we're using Invoke, the first parameter is the object we're invoking the method on, the second parameter of invoke is actually the first function parameter)
There is also a secure version available ('createRfcommSocket'), which accepts a bluetooth channel number as a single parameter (again, since this is invoke style, you'll need to pass the object to invoke the method on, as mentioned in -1- )
I found what appears to be a link to these functions' prototypes
Good luck to all.
Bluetooth Android connections are exclusively done via UUID. Each Bluetooth device has a UUID for every service it runs (see Bluetooth SDP).
You just give Android the UUID to watch for and, in client mode, it will find a socket to connect to automatically (including port). In server mode, it will wait for the specified device to initiate a connection using the specified UUID.
The BluetoothSocket object is also valid when connection is established (use getInput/Output Stream)
See Server Socket documentation and Client Socket documentation.
If you really want to check everything, you can see what Android decodes from the other device's SDP and the UUID you provided.
Use this tutorial to get the Bluetooth interface (very easy to do).
Then the code should look something like this:
IBluetooth ib =getIBluetooth();
Int otherDevicePort = ib.getRemoteServiceChannel(otherDeviceAddress, UUID);
I'm using bluecove which allow me to do so with the function Connector.open().
I use the following url:
btspp://" + phoneID + ":" + phonePort
N.b.: Some options can be added (e.g.: authenticate=false; or encrypt=false;).
With phoneID being the the being the Bluetooth address and phonePort the port number.
How to find the Bluetooth address?
From this link:
From the Home screen, open the app drawer, then open “Settings“.
Select “System“. (Skip this step on some models)
Scroll down to the bottom and tap “About Phone“, “About device“, or “About tablet“.
Scroll down to the bottom and tap “Status“.
Scroll down and the “Bluetooth address” will be shown in the list.
How to find the port number?
I haven't been able to find which port is supposed to be used yet...
I used 5 and it works but I need to research why and if I want to change the phone I will need to know if I also need to change the port.
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.