I dowloaded the BluetoothChat sample project here :
https://android.googlesource.com/platform/development/+/master/samples/BluetoothChat?autodive=0%2F
The fact is, when I'm launching the app on two devices (which are NOT paired), it should connect the two devices WITHOUT asking for pairing the two devices, shouldn't it ?
And in fact, when I'm trying to connect the two devices (which are NOT paired), it's asking to pair the devices .
I mean, there is this function in BluetoothChatService.java which should create an insecure socket . But it seems, that it doesn't do his job ?
/**
* This thread runs while attempting to make an outgoing connection
* with a device. It runs straight through; the connection either
* succeeds or fails.
*/
private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
private String mSocketType;
public ConnectThread(BluetoothDevice device, boolean secure) {
mmDevice = device;
BluetoothSocket tmp = null;
mSocketType = secure ? "Secure" : "Insecure";
// Get a BluetoothSocket for a connection with the
// given BluetoothDevice
try {
if (secure) {
tmp = device.createRfcommSocketToServiceRecord(
MY_UUID_SECURE);
} else {
tmp = device.createInsecureRfcommSocketToServiceRecord(
MY_UUID_INSECURE);
}
} catch (IOException e) {
Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e);
}
mmSocket = tmp;
}
Can somebody explain me why it asks to pair the two devices ?
The method createInsecureRfcommSocketToServiceRecord shouldn't ask for pairing unpaired devices , should it ? x)
I'm really confused .
createInsecureRfcommSocketToServiceRecord() has of
"insecure" the key the devices are using to communicate, which is, below Bluetooth 2.1, not encrypted. That is what is "unsecure".
But it does not change the fact that if the MAC address is not already in the pairing database there will always be the prompt.
So yes, it will prompt.
Related
I tried to create a simple android application to connect to my ELM327 device to get some car diagnostic data. But I wasn't able to set up the bluetooth connection b/t my android phone and my ELM327 device.
My code is very simple as below:
public class Bluetooth {
protected BluetoothAdapter mBluetoothAdapter= BluetoothAdapter.getDefaultAdapter();
private ConnectThread mConnectThread = null;
private AcceptThread mAcceptThread = null;
private WorkerThread mWorkerThread = null;
private BluetoothDevice mOBDDevice = null;
private BluetoothSocket mSocket = null;
private String uuid;
Bluetooth() {
mBluetoothAdapter= BluetoothAdapter.getDefaultAdapter();
Set<BluetoothDevice> pairedDevices;
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled())
return;
pairedDevices = mBluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
// There are paired devices. Get the name and address of each paired device.
for (BluetoothDevice device : pairedDevices) {
String deviceName = device.getName();
String deviceHardwareAddress = device.getAddress(); // MAC address
//TODO: check whether this is OBD and whether it is connected
//by sending a command and check response
if (deviceName.contains("OBD")) {
mOBDDevice = device;
uuid = device.getUuids()[0].toString();
break;
}
}
}
mBluetoothAdapter.cancelDiscovery();
}
/**
* Start the chat service. Specifically start AcceptThread to begin a session
* in listening (server) mode. Called by the Activity onResume()
*/
public synchronized void connect()
{
try {
// Get a BluetoothSocket to connect with the given BluetoothDevice.
// MY_UUID is the app's UUID string, also used in the server code.
mSocket = mOBDDevice.createRfcommSocketToServiceRecord(UUID.fromString(uuid));
} catch (IOException e) {
Log.e(TAG, "Socket's create() method failed", e);
}
try {
// Connect to the remote device through the socket. This call blocks
// until it succeeds or throws an exception.
mSocket.connect();
} catch (IOException connectException) {
// Unable to connect; close the socket and return.
try {
mSocket.close();
} catch (IOException closeException) {
Log.e(TAG, "Could not close the client socket", closeException);
}
return;
}
}
}
In the mainactivity, I will first new a Bluetooth class then call bluetooth.connect():
mBluetooth = new Bluetooth();
mBluetooth.connect();
When I debug the program, I was able to get my ELM327 bluetooth device by querying all the bonded devices with a name of "OBD". I also was able to get the device's uuid and create a socket using createRfcommSocketToServiceRecord. But in the connect function, mSocket.connect() always fail with a return value of -1 and get a IOexception.
My questions are:
When my android application connect to the ELM327 device, my android phone is the bluetooth client and my ELM327 device is the bluetooth server, is this understanding correct?
Is there a server program running on my ELM327 device listening and accept incoming connection? Is this defined behavior of ELM327 protocol?
Any idea why mSocket.connect()has failed? Any idea on how to look into this issue? Or any obvious error in my program? Thanks.
problem solved. see source codes below:
public synchronized void connect() throws IOException {
try {
// Get a BluetoothSocket to connect with the given BluetoothDevice.
// MY_UUID is the app's UUID string, also used in the server code.
mSocket = mOBDDevice.createRfcommSocketToServiceRecord(UUID.fromString(uuid));
} catch (IOException e) {
Log.e(TAG, "Socket's create() method failed", e);
}
try {
// Connect to the remote device through the socket. This call blocks
// until it succeeds or throws an exception.
mSocket.connect();
} catch (IOException e1) {
Log.e(TAG, "There was an error while establishing Bluetooth connection. Falling back..", e1);
Class<?> clazz = mSocket.getRemoteDevice().getClass();
Class<?>[] paramTypes = new Class<?>[]{Integer.TYPE};
try {
Method m = clazz.getMethod("createRfcommSocket", paramTypes);
Object[] params = new Object[]{Integer.valueOf(1)};
mFallbackSocket = (BluetoothSocket) m.invoke(mSocket.getRemoteDevice(), params);
mFallbackSocket.connect();
mSocket.close();
mSocket = mFallbackSocket;
} catch (Exception e2) {
Log.e(TAG, "Couldn't fallback while establishing Bluetooth connection.", e2);
mSocket.close();
//throw new IOException();
}
}
inputStream = mSocket.getInputStream();
outputStream = mSocket.getOutputStream();
}
I don't know much about Android, although I know about OBD2 and the lot.
It depends on the type of your adapter. If you have a WiFi adapter, you can consider the adapter being the server and you the client. You connect to a socket and then read from it. In the case of a Bluetooth adapter, it's different. If you connect via rfcomm, it's a serial protocol and neither is the server nor the client. If you connect via BTLE, the OBD2 dongle is the Peripheral and you are the Central.
On WiFi adapters, yes. This behavior is not part of ELM327 though. ELM327 only specifies the serial commands. How you transfer these is not part of the spec, since it happens on the layer above (WiFi, rfcomm, BTLE, USB, etc.).
Are you sure that rfcomm works via the socket interface? It's a serial interface, so I would have expected file-like operations.
I checked the developer site and got some help to develop the Bluetooth chat app. I'm not able to connect to other devices. I have used the UUID which was mentioned in the developer site Bluetooth chat example. I know it should be unique but I don't know how to find. Please let me know. I am using Samsung Galaxy J7.
The Bluetooth Chat sample helps how to do a similar app based on the sample.
This application allows two Android devices to carry out two-way text chat over Bluetooth. It demonstrates all the fundamental Bluetooth API capabilites, such as: (1) Scanning for other Bluetooth devices (2) Querying the local Bluetooth adapter for paired Bluetooth devices (3) Establishing RFCOMM channels/sockets (4) Connecting to a remote device (5) Transfering data over Bluetooth
The sample isn't easy to learn you'll need some help by me.
How to make your Bluetooth Chat app based on sample
The Bluetooth connection works in the Client and Server method, even if you're connecting 2 devices, all devices will be both Client and Server (devices connect to others and also accept connections from others).
The UUIDs
UUID stands for Universally Unique Identifier, your UUID must be unique, however, uniqueness isn't always guaranteed and the chances of generating a equal UUID are so low you don't need to worry about the uniqueness. If you want, google "UUID generator" and you'll find sites like this.
Once you generated your UUID, use the same UUID for Client and Server.
Connect to other devices
To connect to other devices, you'll have to enumerate the paired devices in the first place, the BluetoothAdapter contains everything for initial setup. The android.bluetooth.* package contains everything for your app.
BluetoothAdapter is a Singleton, so you can call the method BluetoothAdapter.getDefaultAdapter() many times without making too much instances.
BluetoothAdapter.getDefaultAdapter()
Let's assign mBluetoothAdapter as BluetoothAdapter.getDefaultAdapter().
Make sure Bluetooth is on, if Bluetooth was off, let's turn on.
if (!mBluetoothAdapter.isEnabled()) mBluetoothAdapter.enable();
It will take a few seconds to turn the Bluetooth on.
Enumerate paired devices
Let's enumerate the paired devices, the BluetoothDevice contains information about a specific device, "bonded" means "paired".
List<BluetoothDevice> devices = new ArrayList<>();
for (BluetoothDevice device : mBluetoothAdapter.getBondedDevices()) {
devices.add(device);
}
//Let's add the bonded devices to an ListView
MyBluetoothAdapter adapter = new MyBluetoothAdapter(this, devices);
mListView.setAdapter(adapter);
The MyBluetoothAdapter is an example, you'll need to make your own ArrayAdapter to fit your needs.
Connect to device
When some item is selected, Bluetooth will connect to the device, to prevent UI freeze, the connection will be made in the background using a different Thread.
BluetoothDevice device = yourMethodToGetTheDevice();
ConnectThread mConnectThread = new ConnectThread(device);
mConnectThread.start();
The ConnectThread code is here (I recommend to make an inner class to access parent class method):
The Bluetooth protocol that the sample uses is RFCOMM. THe mUUID is the UUID generated with the website mentioned.
private class ConnectThread extends Thread {
public ConnectThread(BluetoothDevice device) {
try {
mBluetoothSocket = device.createRfcommSocketToServiceRecord(mUUID);
} catch (IOException e) {
Log.e(TAG, e.getMessage(), e);
}
}
#Override
public void run() {
try {
Log.i(TAG, "Connecting...");
mBluetoothSocket.connect();
new ConnectedThread(mBluetoothSocket);
Log.i(TAG, "Connected");
} catch (IOException e) {
Log.e(TAG, e.getMessage(), e);
}
}
}
After the connection was established, you'll be able to send data.
Accept connection
To accept others' connections, you'll have to start a background thread that continuously checks for a connection.
private class AcceptThread extends Thread implements Closeable {
private BluetoothServerSocket mBluetoothServerSocket;
private volatile boolean running = true;
public AcceptThread(String name) {
mBluetoothServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(name, mUUID);
}
#Override
public void run() {
while (running) {
try {
mBluetoothSocket = mBluetoothServerSocket.accept();
if (mBluetoothSocket != null) {
close();
new ConnectedThread(mBluetoothSocket);
}
} catch (IOException e) {
Log.e(TAG, e.getMessage(), e);
break;
}
}
}
#Override
public void close() throws IOException {
mBluetoothServerSocket.close();
mBluetoothServerSocket = null;
running = false;
}
}
This code restricts the connection to a single device.
Communication
Once device is connected, the ConnectedThread will start.
private class ConnectedThread extends Thread implements Closeable {
private InputStream in;
private OutputStream out;
private volatile running = true;
public ConnectedThread(BluetoothSocket socket) {
try {
in = socket.getInputStream();
out = socket.getOutputStream();
} catch (IOException e) {
Log.e(TAG, e.getMessage(), e);
}
}
#Override
public void run() {
//The maximum amount of data to receive is 4KB, if you want to receive more data, you'll have to receive large data by chunks using while loop.
//Usually text isn't as large as 4KB.
byte[] data = new byte[4096];
int length;
while (running) {
try {
length = in.read(data);
String text = new String(data, 0, length);
Log.i(TAG, text);
} catch (IOException e) {
Log.e(TAG, e.getMessage(), e);
//Connection was lost
break;
}
}
}
public void write(byte[] data) throws IOException {
out.write(data);
}
}
To send data, like text, use this code:
mConnectedThread.write("MY TEXT".getBytes());
To receive data, use the code inside ConnectedThread and handle the String text variable.
The Bluetooth tutorials i read all mentioned that i need to have the same UUID on both sides (Server and Client) to establish a connection between two devices. But what if i dont know the UUID of my Client and if i dont care?
Background information: I have over 1000 microcontrollers with bluetooth. Each microcontroller has a fix and unchangeable UUID. Smartphones should be able to send string messages to that micrcontrollers (single connection, one smartphone is controlling one microcontroller). It should not matter which Smartphone is controlling which microcontroller. So in fact i really dont care about the UUID of the Client.
So my Smartphone is the Server and is opening a listening thread for incoming Bluetooth connections but i have to put in a UUID here:
tempBluetoothServerSocket = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
But when i have thousand different UUID's and i really dont care about the UUID what should i put in there? Also the BluetoothSocket:
tempBluetoothSocket = this.bluetoothDevice.createRfcommSocketToServiceRecord(MY_UUID);
How to know which UUID?
So the core question is: How can i connect to any microcontroller?
I've been using this:
// Unique UUID for this application
private static final UUID UUID_ANDROID_DEVICE =
UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
private static final UUID UUID_OTHER_DEVICE =
UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
And it's uses:
public AcceptThread(boolean isAndroid) {
BluetoothServerSocket tmp = null;
// Create a new listening server socket
try {
if(isAndroid)
tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, UUID_ANDROID_DEVICE);
else
tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, UUID_OTHER_DEVICE);
} catch (IOException e) { }
mmServerSocket = tmp;
}
public ConnectThread(BluetoothDevice device) {
mmDevice = device;
BluetoothSocket tmp = null;
// Get a BluetoothSocket for a connection with the
// given BluetoothDevice
try {
if(BluetoothService.this.isAndroid)
tmp = device.createRfcommSocketToServiceRecord(UUID_ANDROID_DEVICE);
else
tmp = device.createRfcommSocketToServiceRecord(UUID_OTHER_DEVICE);
} catch (IOException e) { }
mmSocket = tmp;
}
Which allows my devices to connect to any bluetooth device I've tested with. For the sake of testing, it has only been varying bluetooth barcode scanners. Although I believe this is a generic RFCOMM UUID.
It hasn't failed me yet.
I'm struggling with getting consistent bluetooth connections in a star topology. I have one master phone which is a Samsung Galaxy S4 running API 10. All of the phones that connect to the bluetoothserver socket on the S4 are LG Dynamic Tracfones also running API 10.
Over the past few days, I have seen a LOT of conflicting information on the web about what type of connection to use.
This is my current set up:
MASTER CODE
public void acceptConnection() {
.... (enable bt adapter) ...
// initializes a Bluetooth server socket
bluetoothServerSocket = bc.createBluetoothServerSocket();
//connection made to Master, discovery no longer needed
bluetoothAdapter.cancelDiscovery();
BluetoothSocket bluetoothSocket;
// loops until the thread is interrupted or an exception occurs
while (!isInterrupted()) {
try {
// attempts to accept the slave application's connection
bluetoothSocket = bluetoothServerSocket.accept();
} catch (IOException e) {
// prints out the exception's stack trace
e.printStackTrace();
Log.v("Default Thread", "Connection to slave failed.");
// breaks out of the while loop
return;
}
try {
... (enumerate all input and output streams, and all bt sockets) ...
} catch (IOException e) {
// prints out the exception's stack trace
e.printStackTrace();
}
}
This is the method that is called when creating a blueToothServerSocket, and this is where half of my confusion is. How should I listen on the adapter? Currently, I'm doing it insecurely.
public BluetoothServerSocket createBluetoothServerSocket() {
// gets the name of the application
String name = "PVCED";
// gets a common UUID for both the master and slave applications
UUID uuid = UUID.fromString("23ea856c-49da-11e4-9e35-164230d1df67");
// initializes an empty Bluetooth server socket
serverSocket = null;
try {
// creates a Bluetooth socket using a common UUID
serverSocket = bluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord(name, uuid);
} catch (IOException e) {
// prints out the exception's stack trace
e.printStackTrace();
}
return serverSocket;
}
SLAVE CODE
And this is where the other half of my confusion is, how should I create a socket? Currently I'm doing it insecurely.
private BluetoothSocket createBluetoothSocket(Set<BluetoothDevice> pairedDevices) {
// gets a common UUID for both the master and slave applications
UUID uuid = UUID.fromString("23ea856c-49da-11e4-9e35-164230d1df67");
// initialises an empty Bluetooth socket
BluetoothSocket bluetoothSocket = null;
// checks to see if there are any paired devices
if (pairedDevices.size() > 0) {
// loops through each paired device
for (BluetoothDevice device : pairedDevices) {
// checks to see if the name of the paired device is MASTER
if (device.getName().equals("MASTER")) {
try {
master = device;
// creates a Bluetooth socket using a common UUID
//bluetoothSocket = master.createRfcommSocketToServiceRecord(uuid);
//Method m = master.getClass().getMethod("createRfcommSocketToServiceRecord", new Class[] {int.class});
//bluetoothSocket = (BluetoothSocket) m.invoke(master, 1);
bluetoothSocket = master.createInsecureRfcommSocketToServiceRecord(uuid);
} catch(Exception e){
Log.v("Connect Exception", e.getMessage());
}
}
}
}
//check if we paired succesfully to a master, if not, prompt user to do so.
if (master == null){
... (tell user to pair with master via toast) ...
}
return bluetoothSocket;
}
My logcat is often filled with errors such as "Bad File Descriptor", "Unable to start Service Discovery", or "Service Discovery has failed."
What is the best connection scheme to use for my scenario? If you guys need more details on how I'm enabling/disabling bt adapters, or closing bt connections, I can supply more code.
Iam trying to connect to a socket by using the connect method. Iam generating a String containing the UUID like this:
MY_UUID = UUID.fromString("45341da0-c9c1-11e1-9b21-0800200c9a66");
Then constructing a BluetoothSocket like this:
BluetoothSocket tmp = null;
I also want to connect to a specific device by its mac-address:
BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice("00:1B:DC:0F:EC:7E");
and then making the bluetoothSocket
try {
tmp = device.createInsecureRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) {
Toast.makeText(getApplicationContext(), "Exception1: " + e.getMessage(), Toast.LENGTH_LONG).show();
}
Just for to test I added a breakpoint where the tmp object is been initialized inside the try catch. But it's only containing NULL The remote device does support OBEX OPP, but this is just a layer above the RFCOMM in the bluetooth stack, so I think my device should support RFCOMM for connections. Can anybody tell me why my tmp object is set to null?
Is there anyway I can test whenever the socket is created?