On a bluetooth socket created with device.createRfcommSocketToServiceRecord(MY_UUID) I wish that after an certain amount of time when nothing arrives, to run some code, but still be able to process the bytes as soon as they arrive.
The description of .setSoTimeout explains exactly what I am willing to do:
With this option set to a non-zero timeout, a read() call on the InputStream associated with this Socket will block for only this amount of time. If the timeout expires, a java.net.SocketTimeoutException is raised, though the Socket is still valid.
So it looks like the perfect opportunity to put my code in the catch statement.
But unfortunately .setSoTimeout does not work with Bluetooth sockets according to my Android Studio. How can I implement such functionality without such method?
Thread.sleep is obviously also not a option, because I cannot lock the thread.
I solved it with Thread.sleep anyway, by using small intervals for the sleep and so trying to mimic the .setSoTimeout operation:
short sleep, check for incoming data, cycle until the timeout is reached then execute the timeout code.
I suppose there are better solutions, but this works for now.
The code given will execute the "timeout code" every second (set by the int timeOut), when no byte arrives on the input stream. If a byte arrives, then it resets the timer.
// this belongs to my "ConnectedThread" as in the Android Bluetooth-Chat example
public void run() {
byte[] buffer = new byte[1024];
int bytes = 0;
int timeOut = 1000;
int currTime = 0;
int interval = 50;
boolean letsSleep = false;
// Keep listening to the InputStream
while (true) {
try {
if (mmInStream.available() > 0) { // something just arrived?
buffer[bytes] = (byte) mmInStream.read();
currTime = 0; // resets the timeout
// .....
// do something with the data
// ...
} else if (currTime < timeOut) { // do we have to wait some more?
try {
Thread.sleep(interval);
} catch (InterruptedException e) {
// ...
// exception handling code
}
currTime += interval;
} else { // timeout detected
// ....
// timeout code
// ...
currTime = 0; // resets the timeout
}
} catch (IOException e) {
// ...
// exception handling code
}
}
}
Related
I've been editing androids toyvpn sample project for vpn and i got this one for my sample app
I know there is something wrong/missing with my code because when i manually set up the vpn via android settings, there are packets Receive that's why
i've been searching how to receive packets and i dont know how to get this working.
here is my source code that VCL that extends VpnService
import android.app.PendingIntent;
import android.net.VpnService;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
/**
* Created by Jameshwart Lopez on 8/18/15.
*/
public class VCL extends VpnService {
private static final String TAG = "VpnClientLibrary";
private Thread mThread;
private ParcelFileDescriptor mInterface;
private String mServerAddress;
private String mServerPort;
private PendingIntent mConfigureIntent;
private String mParameters;
//a. Configure a builder for the interface.
Builder builder = new Builder();
public void vclRun(){
try {
//a. Configure the TUN and get the interface.
mInterface = builder.setSession("thesessionname")
.addAddress("192.168.0.1",24)
.addDnsServer("8.8.8.8")
.addRoute("0.0.0.0", 0).establish();
//b. Packets to be sent are queued in this input stream.
FileInputStream in = new FileInputStream(mInterface.getFileDescriptor());
//b. Packets received need to be written to this output stream.
FileOutputStream out = new FileOutputStream(mInterface.getFileDescriptor());
// Allocate the buffer for a single packet.
ByteBuffer packet = ByteBuffer.allocate(32767);
//c. The UDP channel can be used to pass/get ip package to/from server
DatagramChannel tunnel = DatagramChannel.open();
// Connect to the server, localhost is used for demonstration only.
mServerAddress="";//some of the vpn ip address here
mServerPort="1723";
InetSocketAddress server = new InetSocketAddress(mServerAddress, Integer.parseInt(mServerPort) );
tunnel.connect(server);
// For simplicity, we use the same thread for both reading and
// writing. Here we put the tunnel into non-blocking mode.
tunnel.configureBlocking(false);
// Authenticate and configure the virtual network interface.
handshake(tunnel);
//d. Protect this socket, so package send by it will not be feedback to the vpn service.
protect(tunnel.socket());
int timer = 0;
//e. Use a loop to pass packets.
while (true) {
//get packet with in
//put packet to tunnel
//get packet form tunnel
//return packet with out
//sleep is a must
// Assume that we did not make any progress in this iteration.
boolean idle = true;
// Read the outgoing packet from the input stream.
int length = in.read(packet.array());
if (length > 0) {
// Write the outgoing packet to the tunnel.
packet.limit(length);
tunnel.write(packet);
packet.clear();
// There might be more outgoing packets.
idle = false;
// If we were receiving, switch to sending.
if (timer < 1) {
timer = 1;
}
}
// Read the incoming packet from the tunnel.
length = tunnel.read(packet);
if (length > 0) {
// Ignore control messages, which start with zero.
if (packet.get(0) != 0) {
// Write the incoming packet to the output stream.
out.write(packet.array(), 0, length);
}
packet.clear();
// There might be more incoming packets.
idle = false;
// If we were sending, switch to receiving.
if (timer > 0) {
timer = 0;
}
}
// If we are idle or waiting for the network, sleep for a
// fraction of time to avoid busy looping.
if (idle) {
Thread.sleep(100);
// Increase the timer. This is inaccurate but good enough,
// since everything is operated in non-blocking mode.
timer += (timer > 0) ? 100 : -100;
// We are receiving for a long time but not sending.
if (timer < -15000) {
// Send empty control messages.
packet.put((byte) 0).limit(1);
for (int i = 0; i < 3; ++i) {
packet.position(0);
tunnel.write(packet);
}
packet.clear();
// Switch to sending.
timer = 1;
}
// We are sending for a long time but not receiving.
//if (timer > 20000) {
// throw new IllegalStateException("Timed out");
//}
}
}
} catch (Exception e) {
// Catch any exception
e.printStackTrace();
} finally {
try {
if (mInterface != null) {
mInterface.close();
mInterface = null;
}
} catch (Exception e) {
}
}
}
private void handshake(DatagramChannel tunnel) throws Exception {
// To build a secured tunnel, we should perform mutual authentication
// and exchange session keys for encryption. To keep things simple in
// this demo, we just send the shared secret in plaintext and wait
// for the server to send the parameters.
// Allocate the buffer for handshaking.
ByteBuffer packet = ByteBuffer.allocate(1024);
// Control messages always start with zero.
String password = "";//vpn password here
packet.put((byte) 0).put(password.getBytes()).flip();
// Send the secret several times in case of packet loss.
for (int i = 0; i < 3; ++i) {
Log.e("packetsdata", packet.toString());
packet.position(0);
tunnel.write(packet);
}
packet.clear();
// Wait for the parameters within a limited time.
for (int i = 0; i < 50; ++i) {
Thread.sleep(100);
// Normally we should not receive random packets.
int length = tunnel.read(packet);
if (length > 0 && packet.get(0) == 0) {
configure(new String(packet.array(), 1, length - 1).trim());
return;
}
}
//throw new IllegalStateException("Timed out");
}
private void configure(String parameters) throws Exception {
// If the old interface has exactly the same parameters, use it!
if (mInterface != null) {
Log.i(TAG, "Using the previous interface");
return;
}
// Configure a builder while parsing the parameters.
Builder builder = new Builder();
for (String parameter : parameters.split(" ")) {
String[] fields = parameter.split(",");
try {
switch (fields[0].charAt(0)) {
case 'm':
builder.setMtu(Short.parseShort(fields[1]));
break;
case 'a':
builder.addAddress(fields[1], Integer.parseInt(fields[2]));
break;
case 'r':
builder.addRoute(fields[1], Integer.parseInt(fields[2]));
break;
case 'd':
builder.addDnsServer(fields[1]);
break;
case 's':
builder.addSearchDomain(fields[1]);
break;
}
} catch (Exception e) {
throw new IllegalArgumentException("Bad parameter: " + parameter);
}
}
// Close the old interface since the parameters have been changed.
try {
mInterface.close();
} catch (Exception e) {
// ignore
}
// Create a new interface using the builder and save the parameters.
mInterface = builder.setSession(mServerAddress)
.setConfigureIntent(mConfigureIntent)
.establish();
mParameters = parameters;
Log.i(TAG, "New interface: " + parameters);
}
}
this is how i use the class above
private Thread mThread;
/*
* Services interface
* */
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Start a new session by creating a new thread.
mThread = new Thread(this, "VpnRunnable");
//start the service
mThread.start();
/*
*service is left "started" and will later be restarted by the system
* http://android-developers.blogspot.com.au/2010/02/service-api-changes-starting-with.html
*/
return START_STICKY;
}
#Override
public void onDestroy() {
if (mThread != null) {
mThread.interrupt();
}
super.onDestroy();
}
#Override
public synchronized void run() {
/*
* to run the vpn interface call the vclRun method inside VCL class
* */
this.vclRun();
}
Firstly, check that there are bytes being sent to your android device. As it won't be reading anything if there is nothing to receive.
Then have a look at this, as it may be messing up your connection.
You need to include this in the onStartCommand:
// The handler is only used to show messages.
if (mHandler == null) {
mHandler = new Handler(this);
}
// Stop the previous session by interrupting the thread.
if (mThread != null) {
mThread.interrupt();
}
// Extract information from the intent.
String prefix = getPackageName();
mServerAddress = intent.getStringExtra(prefix + ".ADDRESS");
mServerPort = intent.getStringExtra(prefix + ".PORT");
mSharedSecret = intent.getStringExtra(prefix + ".SECRET").getBytes();
// Start a new session by creating a new thread.
mThread = new Thread(this, "ToyVpnThread");
mThread.start();
return START_STICKY;
And also the details (some shown below) of the sychronized void.
#Override
public synchronized void run() {
try {
Log.i(TAG, "Starting");
// If anything needs to be obtained using the network, get it now.
// This greatly reduces the complexity of seamless handover, which
// tries to recreate the tunnel without shutting down everything.
// In this demo, all we need to know is the server address.
InetSocketAddress server = new InetSocketAddress(
mServerAddress, Integer.parseInt(mServerPort));
// We try to create the tunnel for several times. The better way
// is to work with ConnectivityManager, such as trying only when
// the network is avaiable. Here we just use a counter to keep
// things simple.
for (int attempt = 0; attempt < 10; ++attempt) {
mHandler.sendEmptyMessage(R.string.connecting);
// Reset the counter if we were connected.
// See BELOW
if (run(server)) {
attempt = 0;
}
// Sleep for a while. This also checks if we got interrupted.
Thread.sleep(3000);
} /..../
You are not managing your thread actions well. It is advised to receive any bytes that need to be received before attempting your run. That not doing so can cause problems.
I would go back through your code and put in the things you took out.
I also suggest you change your code here:
packet.put((byte) 0).put(password.getBytes()).flip();
Try to use explicit encoding:
packet.put((byte) 0).put(password.getBytes("UTF-8")).flip();
As data can be lost without it. See this answer:
https://stackoverflow.com/a/7947911/3956566
I have checked and your project is using "UTF-8".
Let me know if this doesn't help.
I am trying to control/operate a motor from an android phone in "as close as possible" realtime using the Android SPP Bluetooth socket interface. The motor ought to run in a so called 'dead man' operation mode. So the motor will only turn if a button on the android APP is touched and ought to stop immediately if the touch is released.
I implemented this by continuously sending 'keep turning' telegrams of 20 Bytes about every 20ms to keep the motor turning and to have the motor stop immediately as soon as no more telegrams are received or if a STOP telegram is received.
This seem to work acceptable well on some phone but others continue sending 'keep turning' telegrams even after the MotionEvent.ACTION_UP event has been processed and no more data are being send.
I assume that this is caused by some internal buffers that cache the transmit data and continue sending until the buffer is empty.
Simple questions:
Is there a way to purge the BT stream transmit buffer to stop all data transfer immediately?
Or can I get the fill level of the transmit buffer in which case I would not put anything more than about 2 telegrams into it?
Or is there a way to specify the buffer size when opening the stream?
Searching the net, I was not able to find anything that talks about BT stream buffer size of buffer management.
And Yes, I have implemented read and write functions as threads and I do not have any problems in reading all telegrams, and I do not need to deliver telegrams in real time but I should be able to stop sending 'keep turning' telegrams within about 50 to 100ms.
Any hints are very welcome.
I am sorry that I did not add the code, I thought it may not be necessary as it is straight forward as:
#Override
public boolean onTouch(final View v,MotionEvent event) {
int eventAction = event.getAction();
switch (eventAction) {
case MotionEvent.ACTION_DOWN:
if (v == btnUp || v == btnDown) {
// Start a thread that sends the goUP or DOWN command every 10 ms until
// btnUp released
tvCounter.setText("----");
action_touched = true;
new Thread(new Runnable() {
#Override
public void run() {
int counter = 1;
// Disable heart beat
ServiceRequest.send(EnRequest.REQ_SET_HEARTBEAT,0);
// Send GoUp command plus a wrapping counter byte every nn ms
// until the button is released
while (action_touched) {
try {
setDeadmanMove(v==btnUp,counter);
Thread.sleep(20);
++counter;
}
catch (InterruptedException ex) {
action_touched = false;
}
catch (Exception ex) {
action_touched = false;
}
}
// Send a STOP command
setDeadmanStop();
// Enable heart beat again
ServiceRequest.send(EnRequest.REQ_SET_HEARTBEAT,1);
// We are done
}
}).start();
}
break;
case MotionEvent.ACTION_UP:
// Stop Thread
action_touched = false;
break;
}
return true;
}
The snipped below is part of the communication class that manages the Bluetooth serial communication.
public void btWrite(DeviceRecord message) {
if (runBTreceiver) {
if (message.isValidRecord()) {
try {
lock.lock();
++lockCounter;
mmBufferedOut.write(message.getFullRecord());
mmBufferedOut.flush();
}
catch (IOException e) {
if (GlobalData.isDebugger) Log.i(TAG, "Failed sending " + message + " " + e.getMessage());
ServiceResponse.send(EnEvent.EVT_BT_RECEIVER_ERROR, "Error data send: " + e.getMessage());
resetConnection();
runBTreceiver=false;
}
finally {
--lockCounter;
lock.unlock();
}
}
}
}
The code snipped that allocates and opens the Bluetooth connection
try {
// Set up a pointer to the remote node using it's address.
BluetoothDevice device = myBluetoothAdapter.getRemoteDevice(myBluetoothMacId);
if (device != null)
{
// 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 {
myBluetoothSocket = device.createRfcommSocketToServiceRecord(GlobalData.MY_UUID);
}
catch (IOException e) {
sendEventStatus(EnEvent.EVT_BTADAPTER_FAIL,
String.format(GlobalData.rString(R.string.srv_failcrt),BTERROR_CREATE,e.getMessage()));
}
// Establish the connection. This will block until it connects or
// timeout?
try {
if (! myBluetoothSocket.isConnected()) {
myBluetoothSocket.connect();
}
}
catch (IOException e) {
try {
Log.e("","trying fallback...");
myBluetoothSocket =(BluetoothSocket) device.getClass().getMethod("createRfcommSocket", new Class[] {int.class}).invoke(device,1);
myBluetoothSocket.connect();
}
catch (IOException e2) {
sendEventStatus(EnEvent.EVT_BTADAPTER_FAIL,e2.getMessage());
}
}
}
else {
sendEventStatus(EnEvent.EVT_BTADAPTER_FAIL,
String.format(GlobalData.rString(R.string.srv_failcrt),BTERROR_DEVICE,"getRemoteDevice failed"));
}
}
catch (Exception e) {
sendEventStatus(EnEvent.EVT_BTADAPTER_FAIL, e.getMessage());
return;
}
InputStream tmpIn = null;
OutputStream tmpOut = null;
mmSocket = socket;
// Get the input and output streams, using temp objects because
// member streams are final
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
}
catch (IOException e) {
ServiceResponse.send(EnEvent.EVT_ERROR, GlobalData.rString(R.string.srv_failcst) + e.getMessage());
resetConnection();
runBTreceiver=false;
}
mmInStream = tmpIn;
// mmOutStream = tmpOut;
mmBufferedOut = new BufferedOutputStream(tmpOut,80);
// Initial request
btWrite(new DeviceRecord(0, 4));
I have never discovered any problems sending and receiving data via this code. All records are sent and received properly. Only problem was that I am unable to purge the transmit buffer at the moment the operate button was released.
To overcome this problem, I have changed the protocol in such a way, that only a single 'keep turning' telegram is send at a time, the next telegram will be send after a response from the other end (sort of handshaking), the program then continue to run this ping/pong until the button is released.
This method works quite well as the transmit buffer will never hold more than one telegram at a time.
the mentioned problem is solved though but I still have no clue of whether it would be possible to purge a transmit buffer
I am doing Client server communication in java successfully but now i need to write client in Android rather the java.
client: public class ExampleClient2 {
public static void main(String[] args) throws IOException,
InterruptedException {
int port = 1114;
SocketChannel channel = SocketChannel.open();
// we open this channel in non blocking mode
channel.configureBlocking(false);
channel.connect(new InetSocketAddress("192.168.1.88", port));
if(!channel.isConnected())
{
while (!channel.finishConnect()) {
System.out.println("still connecting");
}
}
System.out.println("connected...");
while (true) {
// see if any message has been received
ByteBuffer bufferA = ByteBuffer.allocate(60);
int count = 0;
String message = "";
while ((count = channel.read(bufferA)) > 0) {
// flip the buffer to start reading
bufferA.flip();
message += Charset.defaultCharset().decode(bufferA);
}
if (message.length() > 0) {
System.out.println("message " + message);
if(message.contains("stop"))
{
System.out.println("Has stop messages");
// break;
}
else
{
// write some data into the channel
CharBuffer buffer = CharBuffer.wrap("Hello Server stop from client2 from 88");
while (buffer.hasRemaining()) {
channel.write(Charset.defaultCharset().encode(buffer));
}
}
message = "";
}
}
}
}
this code is running successfully in java but in android it consuming lots of memory and not running reliably, due to its while (true) loop its like polling , plz let me know some solution that without polling i can read and write the data.
Thanks.
You need to compact() the buffer after calling decode() (or get(), or write(), anything that takes data out of the buffer).
Youu shouldn't allocate a new buffer every time around that while loop, and you should break out of it if read() returned -1. I don't actually see a need for the while loop at all.
I am trying to reset a counter that runs on a Bluetooth receive thread. I have 4 data bytes in each packet being sent by a BT transmitter. Packet time is 300 ms. The receive data gets out of sequence for an unknown reason after some time so I thought I could add a timer to reset the counter between packets to ensure valid data. According to the blogs I have read there is no built in timeouts so I assume someone has created one.
while (true) {
try {
byte[] packetBytes = new byte[20]; // Start new packetbyte
Log.d(TAG, " Counter " + counter);
mmInStream.read(packetBytes); // Read Bluetooth socket
b [counter]= packetBytes[0]; // Save each byte into B
counter++;
if(counter == 4){ // After 4 bytes DO this
Log.d(TAG, " Done ");
counter=0; // Reset counter
h.obtainMessage(RECIEVE_MESSAGE, counter, -1, b).sendToTarget(); // Send to message queue Handler
}
} catch (IOException e) {
break;
}
}
This is the code to count the data. I am trying to reset the counter.
I have an application which communicates with a bluetooth device via async task
if I receive a phone call and during the call I return to the app
the screen dims and the application is unresponsive
back button doesn't work... and no ANR dialog is shown
any ideas?
here is the code which handles the connection:
#Override
protected Object doInBackground(Object... params) {
//boolean protocolUpdated;
int read = 0; // The amount of bytes read from the socket.
byte[] buff = new byte[MessageHandler.BUFFERSIZE]; // The data buffer.
byte[] tmpSend = null; // Misc bytes arrays returned from ProtocolParser as answers to send after decoding calls.
in = null;
out = null;
try {
if (Float.parseFloat(version) > 2.2){
Method m = dev.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
sock = (BluetoothSocket) m.invoke(dev, 1);
}
else sock = dev.createRfcommSocketToServiceRecord(UUID_RFCOMM_GENERIC); // UUID is constant for serial BT devices.
sock.connect(); // connect to the BT device. This is rather heavy, may take 3 secs.
sendMessage(MESSAGE_CONNECTION_ESTABLISHED);
in = sock.getInputStream();
out = sock.getOutputStream();
timer = new Timer();
startFinishTimer(); //initialize finish timer
while(read != -1) { // read = -1 means EOF.
do { // as long as there is anything to send in the send queue - send it.
tmpSend = parser.nextSend();
if(tmpSend != null){
String msg = parseMessage(tmpSend);
Log.d("Writing:",msg);
out.write(tmpSend);
}
} while(tmpSend != null);
read = in.read(buff); // read. This is a blocking call, to break this, interrupt the thread.
timer.cancel();
startFinishTimer(); //read is a blocking call so timer should be restarted only after read bytes.
parser.parse(buff,read); // parse the read message using the logic in the ProtocolParser derived class.
tmpSend = parser.getPool(); // if pool ack is required - send it.
if (tmpSend != null){
Log.d("Writing:",parseMessage(tmpSend));
out.write(tmpSend);
}
if (read != 0){
Log.d("Read:",parseMessage(buff));
tmpSend = parser.getAnswer(); // if answer is required (based on message) - send it.
if(tmpSend != null){
out.write(tmpSend);
}
}
else {
Exception e = new IOException();
throw e;
}
}
}catch (IOException e){
e.printStackTrace();
Log.d("Connection: ", "Bluetooth Connection CRASHED!");
sendMessage(MESSAGE_CONNECTION_LOST);
}catch (Exception e){
e.printStackTrace();
}
return null;
}
Actually there is not enough context to find your problem.
Make sure that you launch this task from Main thread in other case PostExecute will be attached to wrong thread, you could get a race.
Make sure that you don't send same message to multiple handlers in your code.
Message it's a linked list and your could get ANR in that case.
Get /data/anr/traces.txt to make sure that it's not ANR.
You could make sure by time in the beginning of the file.