Could someone tell me if i can use my android device as a slave while using Bluetooth. I want another device (for example: PC) to act as a Master. I want the PC to connect to the Android device via Bluetooth and then receive messages from the Android device say on the hyper terminal of the PC.
BR,
Suppi
You need to create a RFCOMM connection where your Android device will listen to incomming connections. Here is some sample code of my own. BluetoothServiceEndpoint and BluetoothDeviceConnection are abstraction interfaces, but in your case you do not need them (just use Android API objects). This chunk of code will use an unauthenticated RFCOMM socket (without pairing) if available (only from Gingerbread, but it works in previous versions since I use reflection).
You would call bind() and then accept() to accept connections.
public void bind() throws IOException
{
if (serverSocket != null)
{
throw new IOException("Service already bound");
}
UUID serviceUUID = UUID.fromString(localServiceEndpoint.getUuid()
.toRFC4122String());
boolean boundWithAuthentication = false;
if (!localServiceEndpoint.isAuthenticationRequired())
{
serverSocket = listenUsingInsecureRfcommWithServiceRecord(
localServiceEndpoint.getServiceName(), serviceUUID);
}
if (serverSocket == null)
{
/*
* Si no hemos podido utilizar un socket inseguro (sin
* autenticación) aunque se haya solicitado así usamos uno seguro.
*/
serverSocket = ba.listenUsingRfcommWithServiceRecord(
localServiceEndpoint.getServiceName(), serviceUUID);
boundWithAuthentication = true;
}
int usedChannel = getUsedChannelPrivate(serverSocket);
remoteServiceEndpoint = new BluetoothServiceEndpoint(
BluetoothServiceEndpoint.TYPE_SPP, ba.getAddress().replaceAll(
":", ""), usedChannel, null,
localServiceEndpoint.isAuthenticationRequired());
info("Service bound " + (boundWithAuthentication ? "with" : "without")
+ " authentication");
}
private BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(
String serviceName, UUID serviceUUID)
{
BluetoothServerSocket socket = null;
try
{
Method m = ba.getClass().getMethod(
"listenUsingInsecureRfcommWithServiceRecord", String.class,
UUID.class);
socket = (BluetoothServerSocket) m.invoke(ba, serviceName,
serviceUUID);
}
catch (Exception e)
{
warn("Unable to bind service without authentication. This device does not support API level >= 10");
}
return socket;
}
public BluetoothDeviceConnection accept() throws IOException
{
if (serverSocket == null)
{
throw new IOException("Service not bound");
}
BluetoothSocket socket = serverSocket.accept();
return new BluetoothDeviceConnectionImpl(new AndroidNativeConnection(
socket), params, listener, logger);
}
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.
everyone!
I'm developing an Android app that allows to chat with nearby devices that have installed this app. In order to accomplish this, I'm using the Wi-Fi P2P API and Network Service Discovery to search for such nearby devices.
I've written the code for searching the nearby devices in a thread started by a Service. When a device is detected, the Service sends it (through a broadcast intent) to an Activity which displays the devices detected so far.
The detected devices are added to a recyclerView and, when the user presses one of them, a connection must be established to such device.
The Wi-Fi Direct connection gets established successfully (that is, the WifiP2pManager.connect() method succeeds) and the WIFI_P2P_CONNECTION_CHANGED_ACTION is caught.
In the broadcast receiver, when such broadcast intent is caught, the following code is executed:
NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
if (networkInfo.isConnected()) {
mManager.requestConnectionInfo(mChannel, connectionInfoListener); }
With the requestConnectionInfo() method I can obtain more information about the connection, such as the IP address of the device I'm trying to connect to.
To obtain such information, I provide an implementation of WifiP2pManager.ConnectionInfoListener to that method, which is denoted by the connectionInfoListener variable.
This is the code of my implementation of WifiP2pManager.ConnectionInfoListener:
private WifiP2pManager.ConnectionInfoListener connectionInfoListener = new WifiP2pManager.ConnectionInfoListener() {
#Override
public void onConnectionInfoAvailable(WifiP2pInfo info) {
InetAddress deviceIP = info.groupOwnerAddress;
int port = servicesConnectionInfo.get(device);
ConnectThread connectThread = new ConnectThread(deviceIP, port, device);
connectThread.start();
"device" is an instance variable of my implementation of BroadcastReceiver which is not important right now. What is important, instead, is the ConnectThread thread. That's the thread that handles the code necessary to connect the socket between the two devices. When I try to connect to a detected device, ConnectThread, in its run() method, creates a new instance of ChatConnection passing the IP address and the port number previously obtained to this constructor:
public ChatConnection(InetAddress srvAddress, int srvPort, String macAddress) throws IOException {
...
connSocket = new Socket(srvAddress, srvPort);
...
}
And here is where the problem occurs. When I test my app on my physical device, all I get is this exception:
W/System.err: java.net.ConnectException: failed to connect to /192.168.49.1 (port 6770): connect failed: ECONNREFUSED (Connection refused)
Of course, I installed my app on a second physical device too, which gets successfully detected and a Wi-Fi Direct connection gets successfully established. But, when comes to this line of code:
connSocket = new Socket(srvAddress, srvPort);
that exception is thrown...
I apologize for the length of this question, but I wanted to be the clearest possible.
I really thank you in advance for any help.
EDIT: I forgot to mention the code for initializing the ServerSocket.
The ServerSocket is initialized in a thread that is started as soon as the Wi-Fi is enabled.
That is, when the WifiP2pBroadcastReceiver (an inner class of the app's Service which extends BroadcastReceiver) catches a WIFI_P2P_STATE_CHANGED_ACTION intent, it checks if the Wi-Fi is enabled and, if enabled, it starts the the thread where the ServerSocket is located:
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION)) {
int statoWiFi = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
if (statoWiFi == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
mNsdService = new NsdProviderThread();
mNsdService.start();
}
The ServerSocket is initialized in the run() method of NsdProviderThread:
public void run() {
...
try {
server = new ServerSocket(0);
} catch (IOException ex) {
return;
}
...
while (!Thread.currentThread().isInterrupted()) {
Socket clientSocket = null;
try {
clientSocket = server.accept();
} catch (IOException ex) {
break;
}
try {
ChatConnection chatConn = new ChatConnection(clientSocket);
synchronized (connections) {
connections.add(chatConn);
}
} catch (IOException ex) {
continue;
}
}
"server" is an instance variable of NsdProviderThread declared as ServerSocket.
It looks like you just need to use the correct port number on both ends.
You're using zero, which from the documentation means:
A port number of 0 means that the port number is automatically
allocated, typically from an ephemeral port range.
So, when you create your ServerSocket, make sure it is listening on the same port that the other device uses to initiate the connection:
private static final int port = 6770;
//.....
try {
server = new ServerSocket(port);
} catch (IOException ex) {
ex.printStackTrace();
}
again! I've finally managed to get my app working. Here's what I've done:
Hard-code the port number;
When you get the group owner address in the ConnectionInfoListener implementation, make sure if it is the IP address of the device in use. If it is not, connect a client socket to the group owner address; otherwise, make your app wait for an incoming connection;
Initialize the ServerSocket as soon as possible (for example, when the app starts up).
In order to get the device actual IP address after a Wi-Fi Direct connection has been established, I've used this function which I've found in this project (which is derived by the original Android WiFiDirectdemo) in the "Utils" class:
public static String getLocalIPAddress() {
/*
* modified from:
*
* http://thinkandroid.wordpress.com/2010/03/27/incorporating-socket-programming-into-your-applications/
*
* */
try {
for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
NetworkInterface intf = en.nextElement();
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
InetAddress inetAddress = enumIpAddr.nextElement();
String iface = intf.getName();
if(iface.matches(".*" +p2pInt+ ".*")){
if (inetAddress instanceof Inet4Address) { // fix for Galaxy Nexus. IPv4 is easy to use :-)
return getDottedDecimalIP(inetAddress.getAddress());
}
}
}
}
} catch (SocketException ex) {
Log.e("AndroidNetworkAddressFactory", "getLocalIPAddress()", ex);
} catch (NullPointerException ex) {
Log.e("AndroidNetworkAddressFactory", "getLocalIPAddress()", ex);
}
return null;
}
"p2pInt" is a private static String costant declared in the Utils class as:
private final static String p2pInt = "p2p-p2p0"
However, in my app, I've changed the "p2p-p2p0" string in "p2p-wlan0" since it looks like the network interface of my device for Wi-Fi Direct has that (different) name.
I hope this can help any developer who's trying to create an app that uses Wi-Fi Direct connectivity.
I want to connect two cell phones using bluetooth with one of them being the host(listening for connections) and the other one initiating the connection. The code that initiates the connection works well and gets successfully connected to the other phone.
However the host gets stuck at socket.accept() because it doesn't detect the connection. The UUIDs are the same on both devices so I really have no clue about what the porblem is.
Here's the AcceptThread of the host:
private class AcceptThread extends Thread {
// The local server socket
private final BluetoothServerSocket mmServerSocket;
boolean running = true;
public AcceptThread() {
BluetoothServerSocket tmp = null;
// Create a new listening server socket
try {
tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, MY_UUID_SECURE);
} catch (IOException e) {
running = false;
Toast.makeText(getApplicationContext(), "socket failed: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
mmServerSocket = tmp;
}
public void run() {
setName("AcceptThread");
BluetoothSocket socket = null;
int state = getMState();
// Listen to the server socket if we're not connected
while (running) {
try {
// This is a blocking call and will only return on a
// successful connection or an exception
socket = mmServerSocket.accept();
} catch (IOException e) {
break;
}
// If a connection was accepted
if (socket != null) {
setState(STATE_CONNECTED);
Toast.makeText(getApplicationContext(), "Connected to: " + socket.getRemoteDevice().getName(), Toast.LENGTH_SHORT).show();
startConnectedThread(socket);
}
}
}
I can assure you the UUIDs that the both devices are using are the same: 00001101-0000-1000-8000-00805f9b34fb
Also I was testing this app on an older phone that has API Level lower than 15(as the host) and another phone(initiating) with API level 17. Can that cause connection issues??
I think that in a while loop should be running = false Once accepts is OK and also close acceptThread when starting ConnectedThread.
I would add check that mmServerSocket != Null
Alternatively, if that does not help to, add here more code (for ex. what is in StartConnectedThread)
I am developing an app for bluetooth remote control a small robot (no, not arduino). The robot has a bluetooth chip (BK3221), which, from what I've gotten to know through their UUIDs works with A2DP and AVRCP protocols (oriented audio).
UUID 1: 0000110b-00000-1000-8000-00805f9b34fb
UUID 2: 0000110e-00000-1000-8000-00805f9b34fb
I can create the connection of the mobile and device through the connect() of Bluetooth A2DP classbut that is what I have, I have the proxy and the connected device but I do not know how to give information .
Moreover I tried program the connection in a basic way with functions for android with RFCOMM (which is supposed to support AVRCP). The robot is a closed system and I don´t know if it worked as a server or client (I assumed it was because the server because it accepts the conexion with the function "conect()" in the previous case). But when I call the connect function generates an exception BluetoothSocket me: "JSR82 connect connection is not created (failed or aborted)". I looked on JSR82 but gave me the feeling of being obsolete ...
If anyone has any idea of something... Thanks
Code of the connection with BluetoothA2dp:
//adaptador == BluetoothAdapter and proxy1 == BluetoothA2dp
protected Boolean doInBackground(BluetoothDevice device) throws RemoteException {
Method connect = getConnectMethod();
final BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.A2DP) {
proxy1 = (BluetoothA2dp) proxy;
}
}
public void onServiceDisconnected(int profile) {
if (profile == BluetoothProfile.A2DP) {
proxy1 = null; }
}};
adaptador.getProfileProxy(getBaseContext(), mProfileListener, BluetoothProfile.A2DP);
try {
connect.setAccessible(true);
connect.invoke(proxy1, device);
Toast.makeText(getBaseContext(),"Connection OK!", Toast.LENGTH_LONG).show();
} catch (Exception ex) {
Toast.makeText(getBaseContext(),"Connection Error"+ex.getMessage(), Toast.LENGTH_LONG).show();
}
private Method getConnectMethod () {
try {
return BluetoothA2dp.class.getDeclaredMethod("connect", BluetoothDevice.class);
} catch (NoSuchMethodException ex) {
Toast.makeText(getBaseContext(),"Method dont appear", Toast.LENGTH_LONG).show();
return null;
}
}