I have been following this Android guide for Bluetooth communication
To explain exactly what I want to do, when the two devices are paired, two different activities open up on each device (server and client) where on the server activity I have different buttons, and on the client activity there is just a textview.
I want to be able to press a button on the server device and display it on the client.
I have managed to establish a connection between the two devices, but now I want to send data which I have not been able to do.
They give this code for data transfer:
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the input and output streams, using temp objects because
// member streams are final
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) { }
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
byte[] buffer = new byte[1024]; // buffer store for the stream
int bytes; // bytes returned from read()
// Keep listening to the InputStream until an exception occurs
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI activity
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
break;
}
}
}
/* Call this from the main activity to send data to the remote device */
public void write(byte[] bytes) {
try {
mmOutStream.write(bytes);
} catch (IOException e) { }
}
/* Call this from the main activity to shutdown the connection */
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
}
But this line generates an error
// Send the obtained bytes to the UI activity
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer).sendToTarget();
And is not explained in the guide. I don't know what the mHandler is or does.
Apart from the error, I don't even really understand where to put this code. Should it be in the second activities (server and client) that I open or in the main? If in the Server activity, should it be in the onClick method for all the buttons with a different byte code to send for each button? And in this code, how do we distinguish who is sending and who is receiving?
Check out the BluetoothChat example that Google provides in the SDK. It'll show you how to implement basic sending of text over bluetooth.
You can also try the tutorial example here
Can you please describe the error as seen by you?
As informed by Ankit and Addy, BlueToothChat is the best code for you to refer. Conduct an experiment by loading it on 2 android devices - use one as server other as client to exchange the messages between them. Such experiment will help you to understand it's code and decide your coding logic.
mHandler is used for passing message from your BluetoothHandle.java to your Activity. This will help you to update messages on your screen which are returned by BluetoothHandler.
you have to create mHandler from your activity and call your handler like this -
mBluetoothHandler = new BluetoothHandler(this, mHandler);
and your BluetoothHandler.java has constructor like this -
public class BluetoothHandler {
public BluetoothHandler(Context context, Handler handler) {
mAdapter = BluetoothAdapter.getDefaultAdapter();
mState = STATE_NONE;
mHandler = handler;
mcontext = context;
}
}
For more details, please refer Android sample project of Bluetooth Chat .
You can also use this link :
http://myandroidappdevelop.blogspot.in/2013/05/bluetooth-chat-example.html
// Enter code here
Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
byte[] writeBuf = (byte[]) msg.obj;
int begin = (int)msg.arg1;
int end = (int)msg.arg2;
switch(msg.what) {
case 1:
String writeMessage = new String(writeBuf);
writeMessage = writeMessage.substring(begin, end);
break;
}
}
};
Related
stuck in One Issue ,
I am using BluetoothSocket class, I am sending and receiving data with the help of input and output streams.
when App receives large amount of data from input stream, I am killing my app forcefully and after it I am again restarting my app, but InputStream returns me previous data, which is not needed anymore.how to discard that old data?
has Anyone Some Solution for this Issue?
Following is my source code:
public class MyBluetoothService {
private static final String TAG = "MY_APP_DEBUG_TAG";
private Handler mHandler; // handler that gets info from Bluetooth service
// Defines several constants used when transmitting messages between the
// service and the UI.
private interface MessageConstants {
public static final int MESSAGE_READ = 0;
public static final int MESSAGE_WRITE = 1;
public static final int MESSAGE_TOAST = 2;
// ... (Add other message types here as needed.)
}
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
private byte[] mmBuffer; // mmBuffer store for the stream
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the input and output streams; using temp objects because
// member streams are final.
try {
tmpIn = socket.getInputStream();
} catch (IOException e) {
Log.e(TAG, "Error occurred when creating input stream", e);
}
try {
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.e(TAG, "Error occurred when creating output stream", e);
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
mmBuffer = new byte[1024];
int numBytes; // bytes returned from read()
// Keep listening to the InputStream until an exception occurs.
while (true) {
try {
// Read from the InputStream.
numBytes = mmInStream.read(mmBuffer);
// Send the obtained bytes to the UI activity.
Message readMsg = mHandler.obtainMessage(
MessageConstants.MESSAGE_READ, numBytes, -1,
mmBuffer);
readMsg.sendToTarget();
} catch (IOException e) {
Log.d(TAG, "Input stream was disconnected", e);
break;
}
}
}
// Call this from the main activity to send data to the remote device.
public void write(byte[] bytes) {
try {
mmOutStream.write(bytes);
// Share the sent message with the UI activity.
Message writtenMsg = mHandler.obtainMessage(
MessageConstants.MESSAGE_WRITE, -1, -1, mmBuffer);
writtenMsg.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "Error occurred when sending data", e);
// Send a failure message back to the activity.
Message writeErrorMsg =
mHandler.obtainMessage(MessageConstants.MESSAGE_TOAST);
Bundle bundle = new Bundle();
bundle.putString("toast",
"Couldn't send data to the other device");
writeErrorMsg.setData(bundle);
mHandler.sendMessage(writeErrorMsg);
}
}
// Call this method from the main activity to shut down the connection.
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, "Could not close the connect socket", e);
}
}
}
}
// Keep listening to the InputStream until an exception occurs
The problem is here. You should keep reading from the stream until end of stream or an exception occurs. You need to break out of the read loop if read() returns -1.
At present you are reading beyond end of stream, and ignoring the condition altogether, so of course the data that was in the buffer on the last successful read is still there.
For your application to keep seeing that data, you must also be ignoring the read count and assuming the buffer was filled, which also is invalid.
I think you should close the socket to manage the bug.
I recommend you to do this in finalizer like the code below.
private class ConnectedThread extends Thread
{
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
private byte[] mmBuffer; // mmBuffer store for the stream
#override
protected void finalize() throws Throwable
{
try
{
cancel();
}
finally
{
super.finalize();
}
}
...
Also as I mentioned in comment, it is safer to close every streams before closing the socket.
So, try this cancel() method.
// Call this method from the main activity to shut down the connection.
public void cancel()
{
try {
mmInStream.close();
} catch( NullPointerException | IOException e) {}
try {
mmOutStream.close();
} catch( NullPointerException | IOException e) {}
try
{
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, "Could not close the connect socket", e);
}
}
And more information about finalize method.
EDIT: bold ones are important than other suggestions.
Reading comments of EJP I understood why your app stops when you get large data : you maybe have to clear buffers before calling read(). And he says that finalizer can happen not to be called by system (I don't know why).
How about breaking the loop when the read() returned -1?
And just now I found a helpful link about proper method to read a stream. I hope it helped.
Code cited from the link
private String extract(InputStream inputStream) throws IOException
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int read = 0;
while ((read = inputStream.read(buffer, 0, buffer.length)) != -1) {
baos.write(buffer, 0, read);
}
baos.flush();
return new String(baos.toByteArray(), "UTF-8");
}
Also
Though the finalizer may be able not to be called by system closing streams before closing sockets is safer (I read some SO threads before).
I need to send a list of commands to OBD port with some delay because the ELM327 can't manage all commands together...
I'm trying with this code but not work
public void repeatCommand(){
for (final String command : commandArray){
Log.d(TAG, "Giro for");
final Handler handlerTimed = new Handler();
handlerTimed.postDelayed(new Runnable() {
#Override
public void run() {
//Do something after 100ms
sendMessage(command);
}
}, 1000);
}
/*String message = "010C\r";
sendMessage(message);*/
}
It's only send the first command after 1 sec but the other commands nope.
How can i send all comands delayed for let the write to manage all commands sended to the OBD?
Ok i use the suggested method that send the first command and wait for response.... when get response, send the next message.
private synchronized void manage(BluetoothSocket socket, BluetoothDevice
device) {
Log.d(TAG, "connected, Socket Type:");
// Cancel the thread that completed the connection
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
// Cancel any thread currently running a connection
if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
// Cancel any thread currently managing connections
if (mManageThread != null) {
mManageThread.cancel();
mManageThread = null;
}
// Start the thread to manage the connection and perform transmissions
mManageThread = new ManageDataThread(socket);
mManageThread.start();
// Send the name of the connected device back to the UI Activity
Message msg = mHandler.obtainMessage(Constants.MESSAGE_DEVICE_NAME);
Bundle bundle = new Bundle();
bundle.putString(Constants.DEVICE_NAME, device.getName());
msg.setData(bundle);
mHandler.sendMessage(msg);
// Update UI title
updateUserInterfaceTitle();
}
Here the Thread that manage connection..
public class ManageDataThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
private boolean wait_response = false;
public ManageDataThread(BluetoothSocket socket) {
Log.d(TAG, "create ManageDataThread: ");
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;
mState = STATE_CONNECTED;
}
public void run() {
while(true) {
for (final String command : commandArray) {
byte[] send = command.getBytes();
write(send);
//mState = STATE_WAIT_RESPONSE;
byte[] buffer = new byte[1024];
int bytes;
// Keep listening to the InputStream while connected
while (wait_response) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
//TODO devo gestire l'arrivo di bytes
ObdCommand obc = new ObdCommand();
obc.readResult(mmInStream);
formattedMessage = obc.getResult();
//buffer = (byte) obc.getBuffer();
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(Constants.MESSAGE_READ, bytes, -1, formattedMessage)
.sendToTarget();
wait_response = false;
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
connectionLost();
break;
}
}
}
}
}
It's little bit imperfect but now it's work....
I will open a new post for stop it and update the array list of commands because if i change the list of commands, the loop keep the old array list, so i need to notify the thread that the arraylist has change
EDIT
Don't use while(true) inside thread, better to use a variable set to True e when need to stop thread set the variable to false, or problem occurs when stop thread....
The proper way to work with OBD2 – which is a serial protocol – is to implement something like a command queue with command-specific callbacks that deliver the response to the command you have requested to it. The command queue should work in a background thread, operating the queue and listening to the serial port. Don't even start w/ delays or similar approaches.
One thing to consider is that when you create a handler and don't provide it with a looper it will use the looper of it's current thread.
So my best guess, since we don't have your code, is that you do in fact run it in a thread which is not the main one. make sure your thread doesn't get killed somewhere along the way and it should work.
Also, a few coding tips:
First, I must say that it would be more efficient to set the handler outside of the loop.
That way you don't waste memory creating a handler in each iteration.
So it should look something like that:
public void repeatCommand()
{
final Handler handlerTimed = new Handler();
for (final String command : commandArray){
Log.d(TAG, "Giro for");
handlerTimed.postDelayed(new Runnable() {
#Override
public void run() {
//Do something after 100ms
sendMessage(command);
}
}, 1000);
}
}
Second, I strongly recommend that you will set the consts of your class, like that 1000 value.
I have an android app designed to pair with other phones and send/receive data over bluetooth. Specifically, when a user clicks a button, it will start discovery, connect to a phone, open a socket etc. In general this works, however, I also want to set it up so that during this whole process, if the user clicks the button again, it will correctly close/stop this process. The problem I'm having is that when the user clicks to stop, sometimes it will close a socket prematurely while the app is still trying to check for incoming data because this is all happening across multiple threads. I'm trying to find the correct way to handle this
So when the user clicks to start the process, discovery will happen and then it will try to connect to the paired device over a socket:
private class ConnectThread extends Thread {
private BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
public ConnectThread(BluetoothDevice device) {
// Use a temporary object that is later assigned to mmSocket,
// because mmSocket is final
mmDevice = device;
}
public void run() {
// Cancel discovery because it will slow down the connection
mBluetoothAdapter.cancelDiscovery();
while(!this.isInterrupted()){
BluetoothSocket tmp = null;
// Get a BluetoothSocket to connect with the given BluetoothDevice
try {
// SERVICE_UUID is the app's UUID string, also used by the server code
tmp = mmDevice.createRfcommSocketToServiceRecord(SERVICE_UUID);
} catch (IOException e) {
e.printStackTrace();
}
mmSocket = tmp;
if(mmSocket != null){
try {
// Connect the device through the socket. This will block
// until it succeeds or throws an exception
mmSocket.connect();
Log.d(TAG, "Socket connected");
// Do work to manage the connection (in a separate thread)
connectedThread = new ConnectedThread(mmSocket);
connectedThread.start();
break;
} catch (IOException connectException) {
// Unable to connect; close the socket and get out
Log.d(TAG, "Unable to connect socket: " + connectException.getMessage());
}
}
}
}
/** Will cancel an in-progress connection, and close the socket */
public void cancel() {
try {
Log.d(TAG, "Closing socket");
mmSocket.close();
this.interrupt();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Once the socket has connected, I start a different thread to set up the datastreams:
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the input and output streams, using temp objects because
// member streams are final
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
byte[] buffer = new byte[1024]; // buffer store for the stream
int bytes; // bytes returned from read()
// Keep listening to the InputStream until an exception occurs
while (!this.isInterrupted()) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI activity
mHandler.obtainMessage(Constants.MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
e.printStackTrace();
break;
}
}
}
/* Call this from the main activity to send data to the remote device */
public void write(byte[] bytes) {
try {
mmOutStream.write(bytes);
} catch (IOException e) {
e.printStackTrace();
}
}
/* Call this from the main activity to shutdown the connection */
public void cancel() {
try {
mmSocket.close();
this.interrupt();
} catch (IOException e) {
e.printStackTrace();
}
}
}
So this all works as far as sending/receiving data goes. But when the user clicks the button a second time to stop the entire process (or kill the connection if it already exists) I get an error. On pressing Stop the following code gets executed:
if(btHelper.discoveryStarted){
this.discoveryStarted = false;
mBluetoothAdapter.cancelDiscovery();
}
//Stop socket connection
if(connectThread != null & connectedThread != null){
connectedThread.cancel();
connectThread.cancel();
}
The error I get is an ioexception: bt socket closed, read return: -1 and its pointing to the line where I'm trying to read from the datastream:
bytes = mmInStream.read(buffer)
So the problem is that its trying to read from the stream, but on button click I have already told the socket to close, therefore theres no socket to read from. Whats the correct way to interrupt these threads to avoid a race condition like this?
Furthermore, how do I set this up so that regardless of where the user is in this process the appropriate threads/sockets/connections will be closed correctly? (for example, sometimes the socket will be opened, but data streams have not been set up yet, vs both/neither have been created)
I have an android application connected to a Bluetooth mate silver chip. I'm in the process of testing the send/receive function of it. Mostly I have been following the bluetooth examples on the android dev site.
I can tell sending data works because when I write("$$$") to the chip, it enters command mode and flashes it's status LED very quickly. When the chip enters command mode, it sends a reply: "CMD". I am having trouble receiving this reply.
When I press a button, the following code is executed. mct is the global ConnectedThread that I am using to read and write. As poor form as it is, all functions are inside MainActivity.java
if(connected){
if (cmdMode == false){
mct.write("$$$".getBytes()); //enter command mode
mct.listen();
TextView lbl_history = (TextView) findViewById(R.id.lbl_history);
lbl_history.setText(message);
cmdMode = true;
}
else{
mct.write("k,\n".getBytes()); //kill the connection
cmdMode = false;
}
}
My communication thread:
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) { }
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void listen() {
handled = false;
byte[] buffer = new byte[1024]; // buffer store for the stream
int bytes; // bytes returned from read()
reply=null;
while (reply==null) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
reply = buffer.toString();
//message is a global String to store the latest message received
message = reply;
} catch (IOException e) {
break;
}
}
reply = null;
}
//write and cancel functions removed for simplicity
}
When I run this code, the result is a textview that says "[B#415f8910", which I assume is junk. Multiple runs of the same code will produce similar results, with the last few digits varying. The expected result would be "CMD". Any ideas on what the problem is here? I am new to android development, so any help is appreciated.
Further inspection reveals that multiple runs strictly increase "[B#415f8910", leading me to believe that it is a memory address. Still, I don't know What to do with it though.
I found the problem. Rather than straight up calling "toString()" on the array of bytes, I needed to call the String constructor to properly convert the data:
String message = new String(buffer, "UTF-8");
specifying UTF-8 is what made the difference.
I need help by transferring a string from a PC to an Android mobile device via Bluetooth. The Android mobile device should act as a server and displays the string message on the screen of the device. The PC which is the client should send the string to the mobile device.
I want the server react on the extracted string (transferred via Bluetooth). That means that on one side the server always has to listen for new strings to arrive, but on the other side still has to be able to react on these messages (e.g. navigate from one menu to another).
I tried it by using BlueCove (2.1.1) as BluetoothStack (for which I add the jar from BlueCove as a library to both projects) in combination with an example for a server-client communication that I found here.
Updates:
Updated code from server thanks to user_CC using a RFComm connection for the server:
public class RFCommServer extends Thread{
//based on java.util.UUID
private static UUID MY_UUID = UUID.fromString("446118f0-8b1e-11e2-9e96-0800200c9a66");
// The local server socket
private BluetoothServerSocket mmServerSocket;
// based on android.bluetooth.BluetoothAdapter
private BluetoothAdapter mAdapter;
private BluetoothDevice remoteDevice;
private Activity activity;
public RFCommServer(Activity activity) {
this.activity = activity;
}
public void run() {
BluetoothSocket socket = null;
mAdapter = BluetoothAdapter.getDefaultAdapter();
// Listen to the server socket if we're not connected
while (true) {
try {
// Create a new listening server socket
Log.d(this.getName(), ".....Initializing RFCOMM SERVER....");
// MY_UUID is the UUID you want to use for communication
mmServerSocket = mAdapter.listenUsingRfcommWithServiceRecord("MyService", MY_UUID);
//mmServerSocket = mAdapter.listenUsingInsecureRfcommWithServiceRecord(NAME, MY_UUID); // you can also try using In Secure connection...
// This is a blocking call and will only return on a
// successful connection or an exception
socket = mmServerSocket.accept();
} catch (Exception e) {
}
try {
Log.d(this.getName(), "Closing Server Socket.....");
mmServerSocket.close();
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the BluetoothSocket input and output streams
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
DataInputStream mmInStream = new DataInputStream(tmpIn);
DataOutputStream mmOutStream = new DataOutputStream(tmpOut);
// here you can use the Input Stream to take the string from the client whoever is connecting
//similarly use the output stream to send the data to the client
RelativeLayout layout = (RelativeLayout) activity.findViewById(R.id.relativeLayout_Layout);
TextView text = (TextView) layout.findViewById(R.id.textView_Text);
text.setText(mmInStream.toString());
} catch (Exception e) {
//catch your exception here
}
}
}
Code of the SPP Client from here:
/**
* 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("446118f08b1e11e29e960800200c9a66", 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) {
if(servRecord!=null && servRecord.length>0){
connectionURL=servRecord[0].getConnectionURL(0,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
}
For testing I use a Galaxy Nexus (GT-I9250) with the latest Android API.
Thanks to user_CC, the client and server now runs without an exception. But sadly the client is not able to connect to the server (see the screenshot below). It is because the connectionURL is never set (thus it jumps in here if(connectionURL==null) by default.
How can I change the client code, so that I actually can connect it with the server? I need a proper connectionURL in the following line:
StreamConnection streamConnection=(StreamConnection)Connector.open(connectionURL)
So far I only found out that I somehow need to get the ServiceRecord, sadly this is also not described in the example code from here.
You will need to use the RFComm APIS to make the communication work I have managed to define a class which is a Thread and will be acting as a server and listening for client connections. I have also placed some comments for you to understand.
private class AcceptThread extends Thread {
// The local server socket
private BluetoothServerSocket mmServerSocket;
public AcceptThread() {
}
public void run() {
BluetoothSocket socket = null;
BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
// Listen to the server socket if we're not connected
while (true) {
try {
// Create a new listening server socket
Log.d(TAG, ".....Initializing RFCOMM SERVER....");
// MY_UUID is the UUID you want to use for communication
mmServerSocket = mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
//mmServerSocket = mAdapter.listenUsingInsecureRfcommWithServiceRecord(NAME, MY_UUID); you can also try using In Secure connection...
// This is a blocking call and will only return on a
// successful connection or an exception
socket = mmServerSocket.accept();
} catch (Exception e) {
}
try {
Log.d(TAG, "Closing Server Socket.....";
mmServerSocket.close();
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the BluetoothSocket input and output streams
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
mmInStream = new DataInputStream(tmpIn);
mmOutStream = new DataOutputStream(tmpOut);
// here you can use the Input Stream to take the string from the client whoever is connecting
//similarly use the output stream to send the data to the client
} catch (Exception e) {
//catch your exception here
}
}
}
}
I hope this helps
For your another question:
Declaring javax.bluetooth.UUID on Client side (PC) UUID class should be from javax.bluetooth.UUID
uuidSet2[0] = new UUID("446118f08b1e11e29e960800200c9a66", false);
Declaring java.util.UUID at Server Side (Android)
UUID MY_UUID = UUID.fromString("446118f0-8b1e-11e2-9e96-0800200c9a66");
I'm not a Java developer but I've had a similar issue with Mono for Android (c#)
The UUID for SPP should be "00001101-0000-1000-8000-00805F9B34FB"
This is a well known UID to identify a Bluetooth SPP adapter.
In my c# code that looks like
private static UUID MY_UUID = UUID.FromString("00001101-0000-1000-8000-00805F9B34FB");
I'm guessing you can update your Java code to something like:
new UUID("00001101-0000-1000-8000-00805F9B34FB", true);
Although I'm not sure what parameters that function accepts, so you may have to check that.
I was using the Android device as a client, but the information may be of use to you,
so I'll include my c# code here which I originally translated from Java samples, so you should be able to translate it back:
btAdapter = BluetoothAdapter.DefaultAdapter;
btAdapter.CancelDiscovery(); //Always call CancelDiscovery before doing anything
remoteDevice = btAdapter.GetRemoteDevice(Settings["deviceaddress"].ToString());
socket = remoteDevice.CreateRfcommSocketToServiceRecord(MY_UUID);
socket.Connect();
Basically I get the default adapter, cancel any running discovery operations and then
create a socket to the other device. In your case you'll want to listen instead of connecting, but just for your information.
I hope it helps, sorry I could not give you more Java specific information.
'Update:' Just found a little sample in Java that more or less follows the same method
as what I'm using: Problems with connecting bluetooth SPP in android?