How to get bluetooth RFCOMM to work consistently? - android

I am trying to build an Android application that will interface with an external GPS receiver via the Bluetooth Serial Port Profile (SPP). I am using a Nexus One running 2.3.3. I have managed to get my application to receive data from GPS, but I have two issues: 1) When I connect to the device, it only works some of the time. Sometimes the connection just times out, other times it says the device is busy or in use. 2) I haven't been able to figure out how to send data back to the device, which is probably an issue of how I'm using the streams since the incoming stream is a blocking call.
I moved just the relevant code to a new Android application for testing, which is the following:
/res/layout/main.xml (two buttons and a textview)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent">
<Button android:id="#+id/btnStart" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Connect"></Button>
<Button android:id="#+id/btnSend" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Send Message"></Button>
<TextView android:id="#+id/textStatus" android:textSize="24sp" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Status Goes Here" />
</LinearLayout>
/src/com.example.bluetoothspp/MainActivity.java
package com.example.bluetoothspp;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.SocketTimeoutException;
import java.util.UUID;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
private static final String BTAG = "BTThread";
static final int MSG_BT_GOT_DATA = 1;
static final int MSG_BT_STATUS_MSG = 2;
static final int MSG_BT_FINISHED = 99;
Button btnStart, btnSend;
TextView textStatus;
private BluetoothAdapter mBluetoothAdapter = null;
private BluetoothDevice btdevice = null;
Thread bThread;
BluetoothSocket bsocket;
InputStream bis = null; //Bluetooth input stream
OutputStream bos = null; //Bluetooth output stream
private String MACAddress = "00:01:95:06:1F:32";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnStart = (Button)findViewById(R.id.btnStart);
btnSend = (Button)findViewById(R.id.btnSend);
textStatus = (TextView)findViewById(R.id.textStatus);
btnStart.setOnClickListener(btnStartListener);
btnSend.setOnClickListener(btnSendListener);
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
}
private OnClickListener btnStartListener = new OnClickListener() {
public void onClick(View v){
if(btnStart.getText().equals("Connect")){
Log.i(TAG, "Connect button pressed");
if (mBluetoothAdapter == null) { //No adapter. Fail
Log.e(TAG, "getDefaultAdapter returned null");
textStatus.setText("getDefaultAdapter returned null");
} else {
if (!mBluetoothAdapter.isEnabled()) { //Bluetooth disabled
Log.e(TAG, "Bluetooth is Disabled");
textStatus.setText("Bluetooth is Disabled");
} else {
Log.i(TAG, "Connecting to Device: " + MACAddress);
btdevice = mBluetoothAdapter.getRemoteDevice(MACAddress);
Log.i(TAG, "Device: " + btdevice.getName());
Log.i(TAG, "Trying to Connect...");
textStatus.setText("Trying to Connect...");
Log.i(TAG, "Starting Thread");
try {
bThread = new Thread(new BluetoothClient(btdevice, true));
bThread.start();
} catch (IOException e) {
Log.e(TAG, "Could not create thread for bluetooth: " + e);
textStatus.setText("Could not create thread for bluetooth...");
}
btnStart.setText("Disconnect");
}
}
} else {
Log.i(TAG, "Disconnect button pressed");
btnStart.setText("Connect");
}
}
};
private OnClickListener btnSendListener = new OnClickListener() {
public void onClick(View v){
textStatus.setText("Sending Message to Thread.");
SendDataToBluetooth("something\r\n");
}
};
public class BluetoothClient implements Runnable {
public BluetoothClient(BluetoothDevice device, boolean IsAnHTCDevice) throws IOException {
if (IsAnHTCDevice) {
//This is a workaround for HTC devices, but it likes to throw an IOException "Connection timed out"
try {
Method m = device.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
bsocket = (BluetoothSocket) m.invoke(device, Integer.valueOf(1));
} catch (Exception e) {
Log.e(BTAG, "Error at HTC/createRfcommSocket: " + e);
e.printStackTrace();
handler.sendMessage(handler.obtainMessage(MSG_BT_STATUS_MSG, "MethodException: " + e));
}
} else {
//This is the normal method, but on a Nexus One it almost always throws an IOException "Service discovery failed" message
try {
UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
bsocket = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (Exception e) {
Log.e(BTAG, "Error at createRfcommSocketToServiceRecord: " + e);
e.printStackTrace();
handler.sendMessage(handler.obtainMessage(MSG_BT_STATUS_MSG, "MethodException: " + e));
}
}
}
public void run() {
try {
Log.i(BTAG, "Cancelling Discovery");
mBluetoothAdapter.cancelDiscovery();
Log.i(BTAG, "Connecting to Socket");
bsocket.connect();
bis = bsocket.getInputStream();
bos = bsocket.getOutputStream();
Log.i(BTAG, "Socket created, streams assigned");
handler.sendMessage(handler.obtainMessage(MSG_BT_STATUS_MSG, "Device Connected"));
Log.i(BTAG, "Waiting for data...");
byte[] buffer = new byte[4096];
int read = bis.read(buffer, 0, 4096); // This is blocking
Log.i(BTAG, "Getting data...");
while (read != -1) {
byte[] tempdata = new byte[read];
System.arraycopy(buffer, 0, tempdata, 0, read);
handler.sendMessage(handler.obtainMessage(MSG_BT_GOT_DATA, tempdata));
read = bis.read(buffer, 0, 4096); // This is blocking
}
} catch (SocketTimeoutException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
Log.i(BTAG, "Finished");
handler.sendMessage(handler.obtainMessage(MSG_BT_FINISHED));
}
}
}
public void SendDataToBluetooth(String cmd) { // You run this from the main thread.
try {
if (bsocket != null) {
bos.write(cmd.getBytes());
}
} catch (Exception e) {
Log.e("SendDataToBluetooth", "Message send failed. Caught an exception: " + e);
}
}
public Handler handler = new Handler() { // Handler for data coming from the network and bluetooth sockets
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_BT_GOT_DATA:
Log.i("handleMessage", "MSG_BT_GOT_DATA: " + (String) msg.obj);
textStatus.setText((String) msg.obj);
break;
case MSG_BT_STATUS_MSG:
Log.i("handleMessage", "MSG_BT_STATUS_MSG: " + (String) msg.obj);
textStatus.setText((String) msg.obj);
break;
case MSG_BT_FINISHED:
Log.i("handleMessage", "MSG_BT_FINISHED");
btnStart.setText("Connect");
break;
default:
super.handleMessage(msg);
}
}
};
#Override
protected void onDestroy() {
super.onDestroy();
if (bThread != null) { // If the thread is currently running, close the socket and interrupt it.
Log.i(BTAG, "Killing BT Thread");
try {
bis.close();
bos.close();
bsocket.close();
bsocket = null;
} catch (IOException e) {
Log.e(BTAG, "IOException");
e.printStackTrace();
} catch (Exception e) {
Log.e(BTAG, "Exception");
e.printStackTrace();
}
try {
Thread moribund = bThread;
bThread = null;
moribund.interrupt();
} catch (Exception e) {}
Log.i(BTAG, "BT Thread Killed");
}
}
}
I found that using the normal "bsocket = device.createRfcommSocketToServiceRecord(MY_UUID);" method would usually result in a "Service discovery failed" message for me, so I also tried the "bsocket = (BluetoothSocket) m.invoke(device, Integer.valueOf(1));" method. That works more often, but likes to time out when I try to connect.
What am I doing wrong here?

Try listening to the incoming data and writing to the device in separate threads. This way you are separating blocking calls.
Did you have a look at Bluetooth chat sample? The sample uses the similar threading technique.

If you are targeting 2.3 and up (which is currently installed on over 50% of android devices out there) you can use the createInsecureRfcommSocketToServiceRecord method to communicate with the device which will surely make it better and more connectable.

Related

Bluetooth connection issues with IntentService

I need to transmit some data from my Android app over Bluetooth (to Arduino). I am not reading/receiving anything back from Arduino. For my single threaded needs, I went with an IntentService. After pairing, my code works fine for the first time I connect and send data. I disconnect after sending data without errors. But when I try to connect the second time onwards, I get the following error when I try myBluetoothSocket.connect() :
read failed, socket might closed or timeout, read ret: -1
Only solution is to power off the Arduino device and reconnect (it doesn't help if I force stop the app and try reconnecting).
Note that everything works fine if I spawn 2 threads (one for read and write each) regardless of how many times I connect and send data (thereby proving there is nothing wrong on the Arduino side, "holding back" an old connection).
Here is my Android code :
import android.app.IntentService;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.content.Context;
import android.os.Build;
import android.os.ParcelUuid;
import android.widget.Toast;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.util.UUID;
public class DataTransmissionService extends IntentService {
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private static final String TAG = "DataTransmissionService";
private BluetoothAdapter btAdapter = null;
private BluetoothSocket btSocket = null;
private OutputStream outStream = null;
private BluetoothDevice device = null;
public DataTransmissionService() {
super("DataTransmissionService");
}
#Override
protected void onHandleIntent(Intent intent) {
cleanup();
if (intent != null){
btAdapter = BluetoothAdapter.getDefaultAdapter();
pairedDeviceAddress = "already_paired_device_mac_addr";
try {
log.d(TAG, pairedDeviceAddress);
device = btAdapter.getRemoteDevice(pairedDeviceAddress);
log.d(TAG, "Device bond state : " + device.getBondState());
} catch (Exception e) {
log.e(TAG, "Invalid address: " + e.getMessage());
return;
}
try {
btSocket = createBluetoothSocket(device);
} catch (IOException e) {
log.e(TAG, "Socket creation failed: " + e.getMessage());
return;
}
try {
if (!btSocket.isConnected()) {
btSocket.connect();
log.d(TAG, "Connected");
} else {
log.d(TAG, "Already Connected"); //flow never reaches here for any use case
}
} catch (IOException e) {
log.e(TAG, "btSocket.connect() failed : " + e.getMessage());
return;
}
try {
outStream = btSocket.getOutputStream();
} catch (IOException e) {
log.e(TAG, "Failed to get output stream:" + e.getMessage());
return;
}
sendData("test");
//cleanup(); called in onDestroy()
}
}
#Override
public void onDestroy(){
cleanup();
//notify ui
super.onDestroy();
}
private void cleanup(){
try {
if (outStream != null) {
outStream.close();
outStream = null;
}
} catch (Exception e) {
log.e(TAG, "Failed to close output stream : " + e.getMessage());
}
try {
if (btSocket != null) {
btSocket.close();
btSocket = null;
}
}catch (Exception e) {
log.e(TAG, "Failed to close connection : " + e.getMessage());
}
}
private BluetoothSocket createBluetoothSocket(BluetoothDevice device) throws IOException {
/*if(Build.VERSION.SDK_INT >= 10){
try {
final Method m = device.getClass().getMethod("createInsecureRfcommSocketToServiceRecord", new Class[] { UUID.class });
return (BluetoothSocket) m.invoke(device, MY_UUID);
} catch (Exception e) {
log.e(TAG, "Could not create Insecure RFComm Connection",e);
}
}*/
return device.createRfcommSocketToServiceRecord(MY_UUID);
}
private void sendData(String message) {
byte[] msgBuffer = message.getBytes();
log.d(TAG, "Sending : " + message);
try {
outStream.write(msgBuffer);
} catch (IOException e) {
log.e(TAG, "failed to write " + message);
}
}
}
I have tested on Nexus 5 and Samsung S5 devices (running 5.1 and 5.0 respectively).
When you try to connect the second time you have to create the corresponding socket again.
Also you must consider Arduino is a slow platform, there might be some considerable delay between closing the connection and you being able to open it again.
I am not sure why it works, but this approach finally worked :
private BluetoothSocket createBluetoothSocket(BluetoothDevice bluetoothDevice) throws IOException {
try {
Method m = bluetoothDevice.getClass().getMethod(
"createRfcommSocket", new Class[] { int.class });
btSocket = (BluetoothSocket) m.invoke(bluetoothDevice, 1);
} catch (Exception e) {
e.printStackTrace();
}
return btSocket;
}
The onDestroy() method is only called when the garbage collector runs. You need call the cleanup() from the onHandleIntent(Intent) as you did before otherwise the socket will remain open indefinitely. Since you left it open, you are unable to connect again.
Android's Bluetooth stack seems to be agnostic of the application lifecycle: the socket will remain open even if you force stop the application. In your current scenario, to close the socket do a disable-enable of the Bluetooth in Settings.

R cannot be resolved to a variable --

package br.com.buceta;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.util.UUID;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity {
private static final String TAG = "bluetooth1";
Button btnOn, btnOff;
private BluetoothAdapter btAdapter = null;
private BluetoothSocket btSocket = null;
private OutputStream outStream = null;
// SPP UUID service
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
// MAC-address of Bluetooth module (you must edit this line)
private static String address = "00:15:FF:F2:19:5F";
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); (error)
btnOn = (Button) findViewById(R.id.btnOn); (error)
btnOff = (Button) findViewById(R.id.btnOff); (error)
btAdapter = BluetoothAdapter.getDefaultAdapter();
checkBTState();
btnOn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
sendData("1");
Toast.makeText(getBaseContext(), "Turn on LED", Toast.LENGTH_SHORT).show();
}
});
btnOff.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
sendData("0");
Toast.makeText(getBaseContext(), "Turn off LED", Toast.LENGTH_SHORT).show();
}
});
}
private BluetoothSocket createBluetoothSocket(BluetoothDevice device) throws IOException {
if(Build.VERSION.SDK_INT >= 10){
try {
final Method m = device.getClass().getMethod("createInsecureRfcommSocketToServiceRecord", new Class[] { UUID.class });
return (BluetoothSocket) m.invoke(device, MY_UUID);
} catch (Exception e) {
Log.e(TAG, "Could not create Insecure RFComm Connection",e);
}
}
return device.createRfcommSocketToServiceRecord(MY_UUID);
}
#Override
public void onResume() {
super.onResume();
Log.d(TAG, "...onResume - try connect...");
// Set up a pointer to the remote node using it's address.
BluetoothDevice device = btAdapter.getRemoteDevice(address);
// Two things are needed to make a connection:
// A MAC address, which we got above.
// A Service ID or UUID. In this case we are using the
// UUID for SPP.
try {
btSocket = createBluetoothSocket(device);
} catch (IOException e1) {
errorExit("Fatal Error", "In onResume() and socket create failed: " + e1.getMessage() + ".");
}
// Discovery is resource intensive. Make sure it isn't going on
// when you attempt to connect and pass your message.
btAdapter.cancelDiscovery();
// Establish the connection. This will block until it connects.
Log.d(TAG, "...Connecting...");
try {
btSocket.connect();
Log.d(TAG, "...Connection ok...");
} catch (IOException e) {
try {
btSocket.close();
} catch (IOException e2) {
errorExit("Fatal Error", "In onResume() and unable to close socket during connection failure" + e2.getMessage() + ".");
}
}
// Create a data stream so we can talk to server.
Log.d(TAG, "...Create Socket...");
try {
outStream = btSocket.getOutputStream();
} catch (IOException e) {
errorExit("Fatal Error", "In onResume() and output stream creation failed:" + e.getMessage() + ".");
}
}
#Override
public void onPause() {
super.onPause();
Log.d(TAG, "...In onPause()...");
if (outStream != null) {
try {
outStream.flush();
} catch (IOException e) {
errorExit("Fatal Error", "In onPause() and failed to flush output stream: " + e.getMessage() + ".");
}
}
try {
btSocket.close();
} catch (IOException e2) {
errorExit("Fatal Error", "In onPause() and failed to close socket." + e2.getMessage() + ".");
}
}
private void checkBTState() {
// Check for Bluetooth support and then check to make sure it is turned on
// Emulator doesn't support Bluetooth and will return null
if(btAdapter==null) {
errorExit("Fatal Error", "Bluetooth not support");
} else {
if (btAdapter.isEnabled()) {
Log.d(TAG, "...Bluetooth ON...");
} else {
//Prompt user to turn on Bluetooth
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, 1);
}
}
}
private void errorExit(String title, String message){
Toast.makeText(getBaseContext(), title + " - " + message, Toast.LENGTH_LONG).show();
finish();
}
private void sendData(String message) {
byte[] msgBuffer = message.getBytes();
Log.d(TAG, "...Send data: " + message + "...");
try {
outStream.write(msgBuffer);
} catch (IOException e) {
String msg = "In onResume() and an exception occurred during write: " + e.getMessage();
if (address.equals("00:00:00:00:00:00"))
msg = msg + ".\n\nUpdate your server address from 00:00:00:00:00:00 to the correct address on line 35 in the java code";
msg = msg + ".\n\nCheck that the SPP UUID: " + MY_UUID.toString() + " exists on server.\n\n";
errorExit("Fatal Error", msg);
}
}
}
There is no error in R.layout.activity_main, but the code can't recognize the buttons i have declared in activity_main.xml.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="#+id/btnOff"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="#string/btn_OFF" />
<Button
android:id="#+id/btnOn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="#+id/btnOff"
android:layout_centerHorizontal="true"
android:text="#string/btn_ON" />
<ImageView
android:id="#+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:alpha="0.5"
android:src="#drawable/cxemnet_logo" />
</RelativeLayout>
Ps.: I solved the 'R cannot be resolved as a type' error by Cleaning > Building project and then this error appeared.
You have to declare your buttons. Either you declare them in your method
...
setContentView(R.layout.activity_main);
Button btnOn = findViewById(R.id.btnOn);
Button btnOff = findViewById(R.id.btnOff);
...
or as a local field
public class Classname {
private Button btnOn;
private Button btnOff;
...
}
Delete gen folder from project and then clean up the whole project also remove your import statement used for r.class used in activity class.
Check your manifest file. You probably forgot to declare something. Usually an Activity.
Eclipse added the "R" import automatically. Unless you are trying to reference resources from another package, it is unnecessary and can be deleted.
For example, if you have a package com.package2 but your manifest has a package name of com.package1 then any class in com.package2 will need:
import com.package1.R;
In your case, it appears to be added automatically. Delete it (don't use "organize imports") and "clean"
Android won't generate R file properly if there is any error in one of the XML files. Maybe the error is in one of them (i.e. the layout file): have you closed all tags? Is there any unsupported attribute?
Try also to organize imports (in windows the shortcut is CTRL+MAIUSC+O [it's a vocal, not zero], in Mac is CMD+MAIUSC+O)

Google Glass GDK: How to Communicate with Android Device

I'm looking for a way to send data between my Android Device and Google Glass that doesn't rely on Cloud API's. Is this supported? I see the Bluetooth connections in the My Glass app, which makes me think it can be done. Is there an example source code that shows how this is done? Or do I have to decompile the MyGlass app to figure it out?
Is there a preferred method for doing this kind of data transfer? Ideally I'd like to transfer data in both directions.
Ok, for the requesters....
EDIT: The code below still works, but I've put it into a git repo for those who are interested...
https://github.com/NathanielWaggoner/GoogleGlassBlutooth
Here is my Bluetooth Host/Client code. It's not perfect - You're going to need some patience, and there are some bugs on reconnection and such, but it does work. I've been sending data up to Glass From the Hand Held and driving UI updates (publishing live cards, updating live cards etc...) for about three days using this now.
Host:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.UUID;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.Menu;
import android.widget.TextView;
public class BluetoothHost extends Activity {
public static String msgToSend="";
public static final int STATE_CONNECTION_STARTED = 0;
public static final int STATE_CONNECTION_LOST = 1;
public static final int READY_TO_CONN = 2;
public static final String DEVICE_NAME = "device_name";
public static final String TOAST = "toast";
// our last connection
ConnectedThread mConnectedThread;// = new ConnectedThread(socket);
// track our connections
ArrayList<ConnectedThread> mConnThreads;
// bt adapter for all your bt needs (where we get all our bluetooth powers)
BluetoothAdapter myBt;
// list of sockets we have running (for multiple connections)
ArrayList<BluetoothSocket> mSockets = new ArrayList<BluetoothSocket>();
// list of addresses for devices we've connected to
ArrayList<String> mDeviceAddresses = new ArrayList<String>();
// just a name, nothing more...
String NAME="G6BITCHES";
// We can handle up to 7 connections... or something...
UUID[] uuids = new UUID[2];
// some uuid's we like to use..
String uuid1 = "05f2934c-1e81-4554-bb08-44aa761afbfb";
String uuid2 = "c2911cd0-5c3c-11e3-949a-0800200c9a66";
// just a tag..
String TAG = "G6 Bluetooth Host Activity";
// constant we define and pass to startActForResult (must be >0), that the system passes back to you in your onActivityResult()
// implementation as the requestCode parameter.
int REQUEST_ENABLE_BT = 1;
AcceptThread accThread;
TextView connectedDevices;
Handler handle;
BroadcastReceiver receiver;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// the activity for this is pretty stripped, just a basic selection ui....
setContentView(R.layout.activity_main);
uuids[0] = UUID.fromString(uuid1);
uuids[1] = UUID.fromString(uuid2);
connectedDevices = (TextView) findViewById(R.id.connected_devices_values);
handle = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case STATE_CONNECTION_STARTED:
connectedDevices.setText(msg.getData().getString("NAMES"));
break;
case STATE_CONNECTION_LOST:
connectedDevices.setText("");
startListening();
break;
case READY_TO_CONN:
startListening();
default:
break;
}
}
};
// ....
myBt = BluetoothAdapter.getDefaultAdapter();
// run the "go get em" thread..
accThread = new AcceptThread();
accThread.start();
}
public void startListening() {
if(accThread!=null) {
accThread.cancel();
}else if (mConnectedThread!= null) {
mConnectedThread.cancel();
} else {
accThread = new AcceptThread();
accThread.start();
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
private class AcceptThread extends Thread {
private BluetoothServerSocket mmServerSocket;
BluetoothServerSocket tmp;
public AcceptThread() {
BluetoothServerSocket tmp = null;
try {
tmp = myBt.listenUsingInsecureRfcommWithServiceRecord(NAME, uuids[0]);
} catch (IOException e) { }
mmServerSocket = tmp;
}
public void run() {
Log.e(TAG,"Running?");
BluetoothSocket socket = null;
// Keep listening until exception occurs or a socket is returned
while (true) {
try {
socket = mmServerSocket.accept();
} catch (IOException e) {
e.printStackTrace();
break;
}
// If a connection was accepted
if (socket != null) {
try {
mmServerSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// Do work to manage the connection (in a separate thread)
manageConnectedSocket(socket);
break;
}
}
}
/** Will cancel the listening socket, and cause the thread to finish */
public void cancel() {
try {
mmServerSocket.close();
Message msg = handle.obtainMessage(READY_TO_CONN);
handle.sendMessage(msg);
} catch (IOException e) { }
}
}
private void manageConnectedSocket(BluetoothSocket socket) {
// start our connection thread
mConnectedThread = new ConnectedThread(socket);
mConnectedThread.start();
// Send the name of the connected device back to the UI Activity
// so the HH can show you it's working and stuff...
String devs="";
for(BluetoothSocket sock: mSockets) {
devs+=sock.getRemoteDevice().getName()+"\n";
}
// pass it to the UI....
Message msg = handle.obtainMessage(STATE_CONNECTION_STARTED);
Bundle bundle = new Bundle();
bundle.putString("NAMES", devs);
msg.setData(bundle);
handle.sendMessage(msg);
}
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket) {
Log.d(TAG, "create ConnectedThread");
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the BluetoothSocket input and output streams
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.e(TAG, "temp sockets not created", e);
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
Log.i(TAG, "BEGIN mConnectedThread");
byte[] buffer = new byte[1024];
int bytes;
// Keep listening to the InputStream while connected
while (true) {
try {
//byte[] blah = ("System Time:" +System.currentTimeMillis()).getBytes();
if(!msgToSend.equals("")) {
Log.e(TAG,"writing!");
write(msgToSend.getBytes());
setMsg("");
}
Thread.sleep(1000);
} catch (Exception e) {
Log.e(TAG, "disconnected", e);
connectionLost();
}
}
}
public void connectionLost() {
Message msg = handle.obtainMessage(STATE_CONNECTION_LOST);
handle.sendMessage(msg);
}
/**
* Write to the connected OutStream.
* #param buffer The bytes to write
*/
public void write(byte[] buffer) {
try {
mmOutStream.write(buffer);
} catch (IOException e) {
Log.e(TAG, "Exception during write", e);
connectionLost();
}
}
public void cancel() {
try {
mmSocket.close();
Message msg = handle.obtainMessage(READY_TO_CONN);
handle.sendMessage(msg);
} catch (IOException e) {
Log.e(TAG, "close() of connect socket failed", e);
}
}
}
public static synchronized void setMsg(String newMsg) {
msgToSend = newMsg;
}
public static class HostBroadRec extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Bundle b= intent.getExtras();
String vals ="";
for(String key: b.keySet()) {
vals+=key+"&"+b.getString(key)+"Z";
}
BluetoothHost.setMsg(vals);
}
}
}
Client:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Set;
import java.util.UUID;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.format.DateFormat;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
public class BluetoothClient extends Activity {
public static final int READY_TO_CONN =0;
public static final int CANCEL_CONN =1;
public static final int MESSAGE_READ =2;
// holds the bluetooth names/ids that we're associated with.
ArrayAdapter<String> btArray;
// bt adapter for all your bt needs
BluetoothAdapter myBt;
String NAME="G6BITCHES";
String TAG = "G6 Bluetooth Slave Activity";
UUID[] uuids = new UUID[2];
// some uuid's we like to use..
String uuid1 = "05f2934c-1e81-4554-bb08-44aa761afbfb";
String uuid2 = "c2911cd0-5c3c-11e3-949a-0800200c9a66";
// DateFormat df = new DateFormat("ddyyyy")
ConnectThread mConnThread;
Spinner devices;
Handler handle;
// constant we define and pass to startActForResult (must be >0), that the system passes back to you in your onActivityResult()
// implementation as the requestCode parameter.
int REQUEST_ENABLE_BT = 1;
// bc for discovery mode for BT...
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// When discovery finds a device
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Get the BluetoothDevice object from the Intent
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// Add the name and address to an array adapter to show in a ListView
if(device!= null) {
if(device.getName().contains("Nexus")) {
} else {
btArray.add(device.getName() + "\n" + device.getAddress());
}
}
update();
}
}
};
Context ctx;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// publishCards(this);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
ctx = this;
handle = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case READY_TO_CONN:
mConnThread=null;
update();
break;
case CANCEL_CONN:
break;
case MESSAGE_READ:
byte[] readBuf = (byte[]) msg.obj;
// construct a string from the valid bytes in the buffer
String readMessage = new String(readBuf, 0, msg.arg1);
Log.e(TAG,"received: "+readMessage);
if (readMessage.length() > 0) {
// do soemthing...
}
// updateCards(ctx, readMessage);
// update()
// mConversationArrayAdapter.add(mConnectedDeviceName+": " + readMessage);
break;
default:
break;
}
}
};
btArray = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, android.R.id.text1);
btArray.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
uuids[0] = UUID.fromString(uuid1);
uuids[1] = UUID.fromString(uuid2);
// spinner for displaying available devices for pairing
devices = (Spinner) findViewById(R.id.devices_spinner);
devices.setAdapter(btArray);
// use the same UUID across an installation
// should allow clients to find us repeatedly
myBt = BluetoothAdapter.getDefaultAdapter();
if (myBt == null) {
Toast.makeText(this, "Device Does not Support Bluetooth", Toast.LENGTH_LONG).show();
}
else if (!myBt.isEnabled()) {
// we need to wait until bt is enabled before set up, so that's done either in the following else, or
// in the onActivityResult for our code ...
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
} else {
detectAndSetUp();
}
setContentView(R.layout.bluetooth_activity_layout);
}
#Override
public void onDestroy() {
unregisterReceiver(mReceiver);
super.onDestroy();
}
#Override
protected void onActivityResult (int requestCode, int resultCode, Intent data){
if(requestCode == REQUEST_ENABLE_BT) {
if (resultCode != RESULT_OK) {
Toast.makeText(this, "Failed to enable Bluetooth", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(this, "Bluetooth Enabled", Toast.LENGTH_LONG).show();
detectAndSetUp();
}
}
}
private void detectAndSetUp() {
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy
Set<BluetoothDevice> pairedDevices = myBt.getBondedDevices();
// If there are paired devices
if (pairedDevices.size() > 0) {
// Loop through paired devices
for (BluetoothDevice device : pairedDevices) {
if(device.getName().contains("Nexus")) {
} else {
btArray.add(device.getName() + "\n" + device.getAddress());
}
// Add the name and address to an array adapter to show in a ListView
// btArray.add(device.getName() + "\n" + device.getAddress());
// update();
}
}
myBt.startDiscovery();
}
public void update() {
devices = (Spinner) findViewById(R.id.devices_spinner);
devices.setAdapter(btArray);
devices.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> arg0, View arg1,
int position, long id) {
if(mConnThread!=null) {
Log.e(TAG,"Canceling old connection, and starting new one.");
mConnThread.cancel();
} else {
Log.e(TAG,"got a thing...");
String str = ((TextView)arg1).getText().toString();
Log.e(TAG,"tots: "+str);
String[] vals = str.split("\n");
Log.e(TAG,"mac: "+vals[1]);
BluetoothDevice dev = myBt.getRemoteDevice(vals[1]);
mConnThread = new ConnectThread(dev);
mConnThread.run();
}
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
// TODO Auto-generated method stub
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.bluetooth, menu);
return true;
}
private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
public ConnectThread(BluetoothDevice device) {
Log.e(TAG,"ConnectThread start....");
// Use a temporary object that is later assigned to mmSocket,
// because mmSocket is final
BluetoothSocket tmp = null;
mmDevice = device;
// Get a BluetoothSocket to connect with the given BluetoothDevice
try {
// this seems to work on the note3...
// you can remove the Insecure if you want to...
tmp = device.createInsecureRfcommSocketToServiceRecord(uuids[0]);
// Method m;
// this is an approach I've seen others use, it wasn't nescesary for me,
// but your results may vary...
// m = device.getClass().getMethod("createInsecureRfcommSocket", new Class[] {int.class});
// tmp = (BluetoothSocket) m.invoke(device, 1);
// } catch (NoSuchMethodException e1) {
// // TODO Auto-generated catch block
// e1.printStackTrace();
// } catch (IllegalArgumentException e2) {
// // TODO Auto-generated catch block
// e2.printStackTrace();
// } catch (IllegalAccessException e3) {
// // TODO Auto-generated catch block
// e3.printStackTrace();
// } catch (InvocationTargetException e4) {
// // TODO Auto-generated catch block
// e4.printStackTrace();
// }
// if(tmp.isConnected()) {
// break
// }
} catch (Exception e) {
Log.e(TAG,"Danger Will Robinson");
e.printStackTrace();
}
mmSocket = tmp;
}
public void run() {
// Cancel discovery because it will slow down the connection
myBt.cancelDiscovery();
Log.e(TAG,"stopping discovery");
try {
// Connect the device through the socket. This will block
// until it succeeds or throws an exception
Log.e(TAG,"connecting!");
mmSocket.connect();
} catch (IOException connectException) {
Log.e(TAG,"failed to connect");
// Unable to connect; close the socket and get out
try {
Log.e(TAG,"close-ah-da-socket");
mmSocket.close();
} catch (IOException closeException) {
Log.e(TAG,"failed to close hte socket");
}
Log.e(TAG,"returning..");
return;
}
Log.e(TAG,"we can now manage our connection!");
// Do work to manage the connection (in a separate thread)
manageConnectedSocket(mmSocket);
}
/** Will cancel an in-progress connection, and close the socket */
public void cancel() {
try {
mmSocket.close();
Message msg = handle.obtainMessage(READY_TO_CONN);
handle.sendMessage(msg);
} catch (IOException e) { }
}
}
public void manageConnectedSocket(BluetoothSocket mmSocket) {
ConnectedThread t = new ConnectedThread(mmSocket);
t.start();
// manage your socket... I'll probably do a lot of the boiler plate here later
}
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket) {
Log.d(TAG, "create ConnectedThread");
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the BluetoothSocket input and output streams
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.e(TAG, "temp sockets not created", e);
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
Log.i(TAG, "BEGIN mConnectedThread");
byte[] buffer = new byte[1024];
int bytes;
// Keep listening to the InputStream while connected
while (true) {
try {
// byte[] blah = ("System Time:" +System.currentTimeMillis()).getBytes();
// write(blah);
// Thread.sleep(1000);
// Read from the InputStream
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI Activity
handle.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
// .sendToTarget();
} catch (Exception e) {
Log.e(TAG, "disconnected", e);
connectionLost();
// break;
}
}
}
public void connectionLost() {
Message msg = handle.obtainMessage(CANCEL_CONN);
// Bundle bundle = new Bundle();
// bundle.putString("NAMES", devs);
// msg.setData(bundle);
handle.sendMessage(msg);
}
/**
* Write to the connected OutStream.
* #param buffer The bytes to write
*/
public void write(byte[] buffer) {
try {
mmOutStream.write(buffer);
// Share the sent message back to the UI Activity
// mHandler.obtainMessage(BluetoothChat.MESSAGE_WRITE, -1, -1, buffer)
// .sendToTarget();
} catch (IOException e) {
Log.e(TAG, "Exception during write", e);
}
}
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, "close() of connect socket failed", e);
}
}
}
}
Host Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="transapps.android_bluetooth_host"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="7"
android:targetSdkVersion="17" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name" >
<activity
android:name="transapps.android_bluetooth_host.BluetoothHost"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".BluetoothHost$HostBroadRec" >
<intent-filter>
<action android:name="transapps.g6.new.alert" />
</intent-filter>
</receiver>
</application>
</manifest>
Client Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="transapps.android_blutooth"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="15"
android:targetSdkVersion="15" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name" >
<activity
android:name="transapps.android_blutooth.BluetoothClient"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
I'll leave the UI as an exercise to the reader.
There is no preferred method, but if you're looking to do it wirelessly, a Bluetooth 3.0 RFCOMM does work.
If you provide more specifics about the problem you're trying to solve in your question, I'll be able to provide a more specific answer.
Several things I noticed when following this solution (awesome work by the way!)
1) I could only create a bluetooth connection when my phone and the Google Glass were not already paired through the MyGlass app - If you are having trouble establishing a connection, try forgetting that pairing.
2) The Glass API does not support controlling your bluetooth connection through intents using commands like
Intent discoveryIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoveryIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, DISCOVER_DURATION);
startActivityForResult(discoveryIntent, REQUEST_BLU);
In order to make the Glass headset discoverable (rather than the phone it is connecting to), I needed to follow solutions similar to those like user4934624 provided at this question and shantanu gave at this question. I invoked a hidden method to access bluetooth functionality directly. Warning: the hidden method seems to have been there for years, but there is no guarantee it will continue to be in future APIs.
// this method allows us to make the device discoverable without alerting the users
// NOTE!!!! This uses a hidden method, so it may be removed from the API in the future
public void makeDiscoverable (){
Class <?> baClass = BluetoothAdapter.class;
Method[] methods = baClass.getDeclaredMethods();
// we want to use method setScanMode(int mode, int duration)
// there are 2 setScanModes
// so select the first setScanMode you see
// test to see which method is the one we want
//for (int i = 0; i<50; i++) {Log.d(Integer.toString(i), methods[i].getName());}
//I had trouble calling the first setScanMode, so I called the second.
// I need to pass in a discoverable time, but it stays discoverable indefinitely
// Thus you must turn off the setScanMode as soon as the connection is established
// I should probably write in some other security stuff to turn it off if connection fails
// maybe a timer running on a different thread?
Method mSetScanMode = methods[38];
try {
mSetScanMode.invoke(myBt, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE,300);
//mSetScanMode.invoke(myBt, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
} catch (Exception e) {
Log.e("discoverable", e.getMessage());
}
}
// this method allows us to make the device not discoverable without alerting the user
// NOTE!!!! This uses a hidden method, so it may be removed from the API in the future
public void makeNotDiscoverable (){
// see notes for makeDiscoverable
Class <?> baClass = BluetoothAdapter.class;
Method [] methods = baClass.getDeclaredMethods();
Method mSetScanMode = methods[38];
try {
mSetScanMode.invoke(myBt, BluetoothAdapter.SCAN_MODE_CONNECTABLE,300);
} catch (Exception e) {
Log.e("discoverable", e.getMessage());
}
}
Note that I first ran a test that output the names of all the methods in the class I mirrored; that allowed me to narrow my search for which method I wanted to use.
The JoeGlass app for android claims to be a replacement for MyGlass. It will talk to your glass directly, using a bluetooth connection. I have not tried yet it but it is open source (github), so if it works, you are good.
My book Beginning Google Glass Development has a full chapter on this topic, with fully working example code on using Bluetooth and socket to transfer data between Glass and Android (or iOS) devices.
The book is available at amazon: http://www.amazon.com/Beginning-Google-Glass-Development-Jeff/dp/1430267887
and the source code is available for download at http://www.apress.com/downloadable/download/sample/sample_id/1562/

Android Bluetooth: Unable to connect to listening socket after generating a random UUID

There are two different instances of my program not being about to connect to a BluetoothServerSocket.
One of the instances has only 1 UUID generated randomly before it initiates scan mode with SCAN_MODE_CONNECTABLE_DISCOVERABLE and before using the same generated UUID to create a BluetoothServerSocket for listening. The other intance generates a random UUID just before a BluetoothDevice tries to connect with the UUID just generated.
Each of the instances cannot complete the Bluetooth connection. Throughout the entire program, I put many Logs just to try and see why it wouldn't be able to connect.
Below is the code for the first instance. (Generate 1 random UUID at the launch of the app.) If anyone likes to download my Eclipse project just to take a look, here's the link of the project from MediaFire. As for the second instance, uncommenting the C-style comments in the code below will reveal it.
I expected the results would be to have a successful connection between two devices. The successful connection connects a device to a listening socket, by using a generated UUID. The observed results show it is unlikely.
As far as I know, the only way to obtain a UUID is to obtain it from UUID.randomUUID(). If there are other ways, please post a comment below, and I'll check it. But for now, it's not the answer I wanted.
Thanks in advance.
package o.p;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
/**
* The purpose of this app is to create a server-only Bluetooth connection that
* only accepts incoming Bluetooth connections. It is used only for testing
* one device that needs Bluetooth tweaking in order to consistently work
* correctly on the other device. In short, 2 devices are required.
* */
public class Main extends Activity implements View.OnClickListener {
//Button
private Button acceptButton;
private Button scanButton;
//Bluetooth stuffs.
private BluetoothAdapter btAdapter;
private BluetoothServerSocket serverSocket;
private BluetoothSocket socket;
private BluetoothDevice targetDevice;
private final UUID uuid = UUID.randomUUID();
/*private UUID randomUUID;*/
//Accepting thread.
private class Accept implements Runnable {
private BluetoothServerSocket socket;
private BluetoothSocket result = null;
public Accept(BluetoothServerSocket s) {
socket = s;
result = null;
}
#Override
public void run() {
try {
Log.d("DEBUG", "Accepting.");
result = socket.accept();
Log.d("DEBUG", "Closing server socket.");
socket.close();
}
catch (IOException e) {
Log.d("DEBUG - onClick(), case Accept", "Unable to accept incoming connection.");
}
}
public BluetoothSocket getSocket() {
while (result == null);
return result;
}
}
//Connecting thread.
private class Connecting implements Runnable {
private BluetoothDevice device;
public Connecting(BluetoothDevice d) {
device = d;
}
#Override
public void run() {
try {
/*Log.d("DEBUG", "Generating a new random UUID.");
randomUUID = UUID.randomUUID();*/
Log.d("DEBUG", "Obtaining a socket.");
BluetoothSocket s = device.createRfcommSocketToServiceRecord(uuid);
Log.d("DEBUG", "Cancelling discovery, if it's still discovering.");
if (btAdapter.isDiscovering())
btAdapter.cancelDiscovery();
Log.d("DEBUG", "Connecting to listening socket with UUID: " + uuid.toString());
s.connect();
}
catch (IOException e) {
Log.d("DEBUG - Connecting.run()", "Unable to connect to the listening socket.");
}
}
}
//Thread executor
private ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newCachedThreadPool();
//BroadcastReceiver for accepting Bluetooth
private BroadcastReceiver receiver;
#Override
public void onCreate(Bundle b) {
super.onCreate(b);
setContentView(R.layout.main);
init();
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button_accept:
Log.d("DEBUG", "Pressing the Accept button.");
Accept acceptThread = new Accept(serverSocket);
Connecting connectThread = new Connecting(targetDevice);
if (serverSocket != null) {
executor.execute(acceptThread);
executor.execute(connectThread);
socket = acceptThread.getSocket();
}
else {
Log.d("DEBUG", "Server socket isn't ready.");
Toast.makeText(this, "Server socket isn't ready.", Toast.LENGTH_LONG).show();
}
break;
case R.id.button_scan:
if (btAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
Log.d("DEBUG", "Initiating discovery scan mode.");
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
this.startActivity(discoverableIntent);
Toast.makeText(this, "Being discovered...", Toast.LENGTH_LONG).show();
}
if (btAdapter.isDiscovering()) {
Toast.makeText(this, "Re-scanning...", Toast.LENGTH_SHORT).show();
Log.d("DEBUG", "Re-scanning.");
btAdapter.cancelDiscovery();
}
Log.d("DEBUG", "Scanning.");
Toast.makeText(this, "Scanning...", Toast.LENGTH_LONG).show();
btAdapter.startDiscovery();
break;
}
}
private void init() {
Log.d("DEBUG", "Initializing.");
Log.d("DEBUG", "Button initializing.");
acceptButton = (Button) findViewById(R.id.button_accept);
acceptButton.setOnClickListener(this);
scanButton = (Button) findViewById(R.id.button_scan);
scanButton.setOnClickListener(this);
Log.d("DEBUG", "Registering BroadcastReceiver.");
receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
Log.d("DEBUG", "Device has been found.");
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.d("DEBUG", "Obtained a device from Intent.");
if (device.getBondState() == BluetoothDevice.BOND_BONDED) {
Log.d("DEBUG", "Removing paired device.");
try {
Method m = device.getClass().getMethod("removeBond", (Class[]) null);
m.invoke(device, (Object[]) null);
Log.d("DEBUG", "Removed " + device);
}
catch (NoSuchMethodException e) {
Log.e("ERROR - DeviceReceiver.onReceive()", "", e);
}
catch (IllegalArgumentException e) {
Log.e("ERROR - DeviceReceiver.onReceive()", "", e);
}
catch (IllegalAccessException e) {
Log.e("ERROR - DeviceReceiver.onReceive()", "", e);
}
catch (InvocationTargetException e) {
Log.e("ERROR - DeviceReceiver.onReceive()", "", e);
}
}
else {
Log.d("DEBUG", "Obtaining remote device's address.");
btAdapter.getRemoteDevice(device.getAddress());
try {
serverSocket = btAdapter.listenUsingRfcommWithServiceRecord(device.getName(), uuid);
Log.d("DEBUG", "Listening to " + device.getName() + "...");
}
catch (IOException e) {
Log.d("DEBUG - onReceive()", "Unable to create a server socket after receiving a broadcast.", e);
serverSocket = null;
Log.d("DEBUG", "Server socket is set to null.");
}
}
targetDevice = device;
}
else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
Log.d("DEBUG", "Scanning finished.");
}
}
};
Log.d("DEBUG", "Creating Bluetooth Adapter.");
btAdapter = BluetoothAdapter.getDefaultAdapter();
try {
Log.d("DEBUG", "Creating a server socket for listening using UUID: " + uuid.toString());
serverSocket = btAdapter.listenUsingRfcommWithServiceRecord("server", uuid);
}
catch (IOException e) {
Log.d("DEBUG - init()", "Error in creating a server socket from uuid.");
}
}
#Override
public void onResume() {
super.onResume();
//TODO: Not done with the receivers.
IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(receiver, filter);
filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(receiver, filter);
}
#Override
public void onPause() {
//TODO: Complete this one. Same for onResume().
super.onPause();
unregisterReceiver(receiver);
}
}
To be able to connect the UUIDs should match.
On the Server side what you are doing is correct i.e generating a ramdom UUID and listening on it,
But the client needs to connect using the same UUID that the server is listening on.
The way to get it will be from your client use the fetchUuidsWithSdp() on the Server BluetoothDevice object and use the obtained UUID to connect to the server.

initialize a bluetooth connection between Android (server) and bluecove on pc (client)

I know there are other topics about that but in my case I want the Android device to initialize the bluetooth connection as a server. I followed the Documentation and I wrote the server in this way:
private class AcceptThread implements Runnable {
private final BluetoothServerSocket mmServerSocket;
public AcceptThread() {
BluetoothServerSocket tmp = null;
try {
tmp = mBluetooth.listenUsingRfcommWithServiceRecord(
"myService", mUuid);
} catch (IOException e) { }
mmServerSocket = tmp;
}
public void run() {
BluetoothSocket socket = null;
// Keep listening until exception occurs or a socket is returned
while (true) {
try {
System.out.println("SERVER SOCKET LISTENING");
socket = mmServerSocket.accept();
} catch (IOException e) {
break;
}
// If a connection was accepted
if (socket != null) {
System.out.println("SIGNAL RECEIVED");
// Do work to manage the connection (in a separate thread)
Toast.makeText(getApplicationContext(), "SIGNAL RECEIVED", Toast.LENGTH_LONG).show();
try {
mmServerSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
}
}
}
/** Will cancel the listening socket, and cause the thread to finish */
public void cancel() {
try {
mmServerSocket.close();
} catch (IOException e) { }
}
}
On the other side I have bluecove API that discover remote devices and services.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Vector;
import javax.bluetooth.DeviceClass;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.DiscoveryListener;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.ServiceRecord;
import javax.bluetooth.UUID;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
/**
* A simple SPP client that connects with an SPP server
*/
public class SampleSPPClient implements DiscoveryListener{
//object used for waiting
private static Object lock=new Object();
//vector containing the devices discovered
private static Vector vecDevices=new Vector();
private static String connectionURL=null;
public static void main(String[] args) throws IOException {
SampleSPPClient client=new SampleSPPClient();
//display local device address and name
LocalDevice localDevice = LocalDevice.getLocalDevice();
System.out.println("Address: "+localDevice.getBluetoothAddress());
System.out.println("Name: "+localDevice.getFriendlyName());
//find devices
DiscoveryAgent agent = localDevice.getDiscoveryAgent();
System.out.println("Starting device inquiry...");
agent.startInquiry(DiscoveryAgent.GIAC, client);
try {
synchronized(lock){
lock.wait();
}
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Device Inquiry Completed. ");
//print all devices in vecDevices
int deviceCount=vecDevices.size();
if(deviceCount <= 0){
System.out.println("No Devices Found .");
System.exit(0);
}
else{
//print bluetooth device addresses and names in the format [ No. address (name) ]
System.out.println("Bluetooth Devices: ");
for (int i = 0; i <deviceCount; i++) {
RemoteDevice remoteDevice=(RemoteDevice)vecDevices.elementAt(i);
System.out.println((i+1)+". "+remoteDevice.getBluetoothAddress()+" ("+remoteDevice.getFriendlyName(true)+")");
}
}
System.out.print("Choose Device index: ");
BufferedReader bReader=new BufferedReader(new InputStreamReader(System.in));
String chosenIndex=bReader.readLine();
int index=Integer.parseInt(chosenIndex.trim());
//check for spp service
RemoteDevice remoteDevice=(RemoteDevice)vecDevices.elementAt(index-1);
UUID[] uuidSet = new UUID[1];
uuidSet[0]=new UUID("4e3aea40e2a511e095720800200c9a66", false);
System.out.println("\nSearching for service...");
agent.searchServices(null,uuidSet,remoteDevice,client);
try {
synchronized(lock){
lock.wait();
}
}
catch (InterruptedException e) {
e.printStackTrace();
}
if(connectionURL==null){
System.out.println("Device does not support Simple SPP Service.");
System.exit(0);
}
//connect to the server and send a line of text
StreamConnection streamConnection=(StreamConnection)Connector.open(connectionURL);
//send string
OutputStream outStream=streamConnection.openOutputStream();
PrintWriter pWriter=new PrintWriter(new OutputStreamWriter(outStream));
pWriter.write("Test String from SPP Client\r\n");
pWriter.flush();
//read response
InputStream inStream=streamConnection.openInputStream();
BufferedReader bReader2=new BufferedReader(new InputStreamReader(inStream));
String lineRead=bReader2.readLine();
System.out.println(lineRead);
}//main
//methods of DiscoveryListener
public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) {
//add the device to the vector
if(!vecDevices.contains(btDevice)){
vecDevices.addElement(btDevice);
}
}
//implement this method since services are not being discovered
public void servicesDiscovered(int transID, ServiceRecord[] servRecord) {
System.out.println(servRecord[0].getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false));
if(servRecord!=null && servRecord.length>0){
connectionURL=servRecord[0].getConnectionURL(ServiceRecord.AUTHENTICATE_ENCRYPT,false);
}
synchronized(lock){
lock.notify();
}
}
//implement this method since services are not being discovered
public void serviceSearchCompleted(int transID, int respCode) {
synchronized(lock){
lock.notify();
}
}
public void inquiryCompleted(int discType) {
synchronized(lock){
lock.notify();
}
}//end method
}
The client found the device and the service but when retrieve the url from the ServiceRecord to establish the connection it fails. It retrieve an Url in which the channel is wrong and it throws an exception: javax.bluetooth.BluetoothConnectionException: Failed to connect;
How can I solve the problem?
I managed to find some phone ServiceRecords when using:
UUID[] uuidSet = new UUID[1];
uuidSet[0]=new UUID(0x0100);
int[] attrIds = { 0x0100 };
System.out.println("\nSearching for service...");
agent.searchServices(attrIds, uuidSet, remoteDevice, client);
And you will be calling lock.notify() twice after a serviceSearch, remove it in the servicesDiscovered function.
You should also go through the service records and look for the one you are interested in. The URL will state btgoep:// or btspp://
When searching through the for loop use this code to list the service name
for(int i = 0; i < servRecord.length; i++)
{
String url = servRecord[i].getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);
DataElement serviceName = srs[i].getAttributeValue(0x0100);
if (serviceName != null) {
System.out.println("service " + serviceName.getValue() + " found " + url);
} else {
System.out.println("service found " + url);
}
I have the exact problem, it seems like the android api doesn't register the ServiceRecord with the SDP so the Bluecove api can find it.
No matter what UUID I use it will only find the ones my phone register as default, i e Audio gateways and Phonebook OBEX push and such.
EDIT ---
I had the same problem, but realized I had not actually called listenUsingInsecureRFCOMMSocket yet. And then it did not register the service record.
But after that it worked just fine.

Categories

Resources