In my application I need to scan the local subnet (192.168.1.*) to collect the list of MAC addresses of all connected devices.
I currently use the following strategy:
start simultaneously 255 ping commands with Runtime.exec("ping -c 1 <addr>")
use waitFor() on each of the returned process to collect the exit code
close input streams of processes and destroy them
read the /proc/net/arp file and parse the MAC addresses
In most cases, this works very well and provides a fast scan.
But on some devices (such as android 1.5, and sometimes on >=4.0), the execution gets stuck at process creation (after a few ones have been successfully started) and there's no way to kill the running thread.
Do you see anything I could try to solve this issue? Or any other strategy that would not take too long?
This can be solved by using a pool of thread running the INetAddress.isReachable() method (instead of running the ping command in native processes).
private static final int NB_THREADS = 10;
public void doScan() {
Log.i(LOG_TAG, "Start scanning");
ExecutorService executor = Executors.newFixedThreadPool(NB_THREADS);
for(int dest=0; dest<255; dest++) {
String host = "192.168.1." + dest;
executor.execute(pingRunnable(host));
}
Log.i(LOG_TAG, "Waiting for executor to terminate...");
executor.shutdown();
try { executor.awaitTermination(60*1000, TimeUnit.MILLISECONDS); } catch (InterruptedException ignored) { }
Log.i(LOG_TAG, "Scan finished");
}
private Runnable pingRunnable(final String host) {
return new Runnable() {
public void run() {
Log.d(LOG_TAG, "Pinging " + host + "...");
try {
InetAddress inet = InetAddress.getByName(host);
boolean reachable = inet.isReachable(1000);
Log.d(LOG_TAG, "=> Result: " + (reachable ? "reachable" : "not reachable"));
} catch (UnknownHostException e) {
Log.e(LOG_TAG, "Not found", e);
} catch (IOException e) {
Log.e(LOG_TAG, "IO Error", e);
}
}
};
}
Related
I have a client on a PC and a server on a tablet. I know the MAC addresses for both which means I do not do discoveries.
1. On the client if I use
connectString = "btspp://" + MACaddress + ":4;authenticate=false;encrypt=false;master=false";
It connects fine.
If I change the CN number (4) to anything else, it does not work. How is this number determined?
2. Everything works fine if the tablet is a Samsung with Android 5.0.2 When I use a Qunyico tablet with Android 10, it does not work. I get an error: Failed to connect; [10051] A socket operation was attempted to an unreachable network. What is the problem?
Client on PC – code taken from “Bluetooth-java-client-master”
public class IrcBluetoothClient {
private static void openConnection(String MACaddress) throws IOException {
// Tries to open the connection.
String connectString = "btspp://" + MACaddress + ":4;authenticate=false;encrypt=false;master=false";
StreamConnection connection = (StreamConnection) Connector.open(connectString);
if (connection == null) {
System.err.println("Could not open connection to address: " + MACaddress);
System.exit(1);
}
// Initializes the streams.
OutputStream output = connection.openOutputStream();
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader reader = new BufferedReader(isr);
// Starts the listening service for incoming messages.
ExecutorService service = Executors.newSingleThreadExecutor();
service.submit(new IncomingMessagesLoggingRunnable(connection));
// Main loop of the program which is not complete yet
LocalDevice localDevice = LocalDevice.getLocalDevice();
while (true) {
String toSend = reader.readLine();
byte[] toSendBytes = toSend.getBytes(StandardCharsets.US_ASCII);
output.write(toSendBytes);
System.out.println("[" + localDevice.getFriendlyName() + " - " +
localDevice.getBluetoothAddress() + "]: " + toSend);
System.exit(1);
}
Server on tablet – code taken from https://developer.android.com/guide/topics/connectivity/bluetooth
private static final UUID A_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
public BTacceptConnections( BluetoothAdapter mBluetoothAdapter) {
// Use a temporary object that is later assigned to mmServerSocket
// because mmServerSocket is final.
BluetoothServerSocket tmp = null;
try {
// A_UUID is the app's UUID string, also used by the client code.
tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, A_UUID);
} catch (IOException e) {
Log.e(TAG, "Socket's listen() method failed", e);
}
mmServerSocket = tmp;
// Closes the connect socket and causes the thread to finish.
public void cancel(){
try {
mmServerSocket.close();
}catch (IOException e){
}
runFlag = 1;
}
//***********************************************************************************************
//
// This thread runs all the time listening for incoming connections.
//
public void run() {
BluetoothSocket socket = null;
// Keep listening until exception occurs or a socket is returned.
while (runFlag == 0) {
try {
socket = mmServerSocket.accept();
} catch (IOException e) {
Log.e(TAG, "Socket's accept() method failed", e);
break;
}
if (socket != null) { // If a connection was accepted
// A connection was accepted. Perform work associated with
// the connection in a separate thread.
// manageMyConnectedSocket(socket);
}else{
try {
mmServerSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
break;
}
}
I know the MAC addresses for both which means I do not do discoveries.
Official Linux Bluetooth protocol stack BlueZ uses D-BUS API to establish bluetooth communication. If you check adapter-api, scanning will create device objects that you need to establish a communication which means discovering is not only done to retrieve MAC addresses only.
Your case might be the same, I would suggest doing discovery first.
I am trying to write an app that passes the coordinates of a ball to Arduino via BT. The coordinates are being sent every 4 ms. For this test I send "123" instead of full coordinates. What am I getting now (on Arduino serial monitor) is "123123123123123..." and it refreshes only after I close the application.
What I want to achieve is "123" in every line, that shows immediately after the message is sent.
Android code BT:
private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
private OutputStream outStream ;
UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb");
public ConnectThread(BluetoothDevice device) {
// Use a temporary object that is later assigned to mmSocket
// because mmSocket is final.
BluetoothSocket tmp = null;
mmDevice = device;
try {
// Get a BluetoothSocket to connect with the given BluetoothDevice.
// MY_UUID is the app's UUID string, also used in the server code.
tmp = device.createInsecureRfcommSocketToServiceRecord(uuid);
} catch (IOException e) {
Log.e(TAG, "Socket's create() method failed", e);
}
mmSocket = tmp;
}
public void run() {
// Cancel discovery because it otherwise slows down the connection.
mBluetoothAdapter.cancelDiscovery();
try {
// Connect to the remote device through the socket. This call blocks
// until it succeeds or throws an exception.
mmSocket.connect();
Log.i(TAG, "run: CONNECTED");
} catch (IOException connectException) {
Log.i(TAG, "run: NOT CONNECTED");
}
}
// Closes the client socket and causes the thread to finish.
public void cancel() {
try {
mmSocket.close();
if(outStream != null)
outStream.close();
finish();
} catch (IOException e) {
Log.e(TAG, "Could not close the client socket", e);
}
}
//Sending Message
public void writeData(String data){
String info = data;
try {
outStream = mmSocket.getOutputStream();
outStream.write(info.getBytes());
Log.i(TAG, "writeData: MSG SENT");
} catch (IOException e) {
e.printStackTrace();
Log.i(TAG, "run: CANT SEND MSG");
}
}
public boolean isConnected(){
return mmSocket.isConnected();
}
}
In my main function I call:
if(connectThread.isConnected())
connectThread.writeData("123");
Arduino code:
String incomingByte;
void setup() {
//pinMode(53, OUTPUT);
Serial.begin(9600);
}
void loop() {
// see if there's incoming serial data:
if (Serial.available() > 0) {
// read the oldest byte in the serial buffer:
incomingByte = Serial.readString();
Serial.println(incomingByte);
delay(10);
}
}
There is no concept of messages in serial communication, unless you make it yourself.
Serial.readString() delimits your "messages" with time (1 second by default) and you are sending "messages" 4 ms apart. This obviously concatenates your "messages".
To actually send messages you need to delimit them. You can do that by sending lines.
On Android, you need to end the message with a new line character:
outStream.write(info.getBytes());
outStream.write(10); // send a new line character (ASCII code 10)
And on Arduino, you need to read, until you find a new line character:
incomingByte = Serial.readStringUntil('\n');
Serial.read(); // remove the leftover new line character from the buffer
You need to put at least \n (or maybe \r\n) after the coordinates, or the Bluetooth module just keeps buffering.
I am writing a program for a new vehicle security app. the app allows the user to control lock/unlock operations via his phone app. Lets say the user's phone Bluetooth is switched off at first. If that's the case, when he opens the app, the phone bluetooth adapter should be automatically switched on and should connect with the bluetooth module fixed in to the vehicle.
according to the code I have done, the programatic enabling of the BT adapter of phone works fine. But the connection to the vehicle BT module does NOT happen.
But if the user opens the app while the phone BT adapter is already switched on, then the connection establishing between the vehicle and phone happens automatically.
I need to know why the connection does NOT happen when the BT adapter is turned on programmatically.
Note - the phone and the vehicle BT module is paired. The bluetooth modules mac address is hard coded in the coding.
The coding is as follows. I only pasted the necessary parts. I hope every needed to understand and solve my problem is here. The way I posted the code is pretty messed up. Sorry about that. Hope it's clear. I'm new to this.
private static final UUID MY_UUID =
UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
// Insert bluetooth devices MAC address
private static String address = "00:19:5D:EF:03:79";
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btAdapter = BluetoothAdapter.getDefaultAdapter();
btAdapter.enable();
#Override
public void onResume() {
super.onResume();
btAdapter.enable();
// 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 = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) {
errorExit("Fatal Error", "In onResume() and socket create failed: " + e.getMessage() + ".");
}
// Make sure Discovery isn't going on when you attempt to connect and pass your message.
btAdapter.cancelDiscovery();
// Establish the connection. This will block until it connects.
try {
btSocket.connect();
} 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.
try {
outStream = btSocket.getOutputStream();
} catch (IOException e) {
errorExit("Fatal Error", "In onResume() and output stream creation failed:" + e.getMessage() + ".");
}
}
There might be a timing problem, onCreate and onResume are called in very short order. In the case that the BT is not enabled the code in onResume might be called before the BT service is online.
My advice: Try to delay the initiation a few seconds by putting the code in a Runnable.
private Handler mHandler = new Handler();
public void onCreate() {
[...]
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
btAdapter.enable();
// 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 = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) {
errorExit("Fatal Error", "In onResume() and socket create failed: " + e.getMessage() + ".");
}
// Make sure Discovery isn't going on when you attempt to connect and pass your message.
btAdapter.cancelDiscovery();
// Establish the connection. This will block until it connects.
try {
btSocket.connect();
} 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.
try {
outStream = btSocket.getOutputStream();
} catch (IOException e) {
errorExit("Fatal Error", "In onResume() and output stream creation failed:" + e.getMessage() + ".");
}
}, 5000); // 5 second delay
[...]
Caveats: This works really bad if you exit the app promptly after startup. Put the runnable in a member variable and call mHandler.removeCallback(Runnable) in onDestroy().
I'm trying to improve an actual code that make a bluetooth connection with an Android phone to an Atmega with Arduino (An electronic micro-controller). I can receive and send data to the micro-controller but the bluetooth need to be put at ON before lunching my application or it will hang and close. I do check for a bluetooth adapter and request the user to change the bluetooth state if it's at OFF but it seem the program continue and try to make a connection before getting the result of the user selection. I would like some help to find a solution to either block my program until the user input their choice or even get a better solution.
I would like to say that I'm still new to Android programming and I did read the Android activity flowchart.
I can provide logcat, but I inspected it and it clearly state that I am trying to use bluetooth even if it's not enabled ...
Here is my code :
I want to thanks anybody that could point me on the right direction
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnOn = (Button) findViewById(R.id.btnOn); // button LED ON
btnOff = (Button) findViewById(R.id.btnOff); // button LED OFF
txtArduino = (TextView) findViewById(R.id.txtArduino); // for display the received data from the Arduino
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); // get Bluetooth adapter
checkBTState();
h = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case RECIEVE_MESSAGE: // if receive massage
byte[] readBuf = (byte[]) msg.obj;
String strIncom = new String(readBuf, 0, msg.arg1); // create string from bytes array
sb.append(strIncom); // append string
int endOfLineIndex = sb.indexOf("\r\n"); // determine the end-of-line
if (endOfLineIndex > 0) { // if end-of-line,
sbprint = sb.substring(0, endOfLineIndex); // extract string
sb.delete(0, sb.length()); // and clear
txtArduino.setText("Data from Arduino: " + sbprint);
Log.e(TAG, "Arduino"+sbprint);
//Test string value
if(sbprint.matches("-?\\d+(\\.\\d+)?")) {
try{
Float sensorReading = Float.parseFloat(sbprint);
Log.e(TAG, "Sensor value"+sensorReading);
}catch(NumberFormatException e){
Log.e(TAG, "No int format sorry",e);
}
}
if(sbprint.matches("test")){
Log.e(TAG, "garbage");
}
///////
btnOff.setEnabled(true);
btnOn.setEnabled(true);
}
//Log.d(TAG, "...String:"+ sb.toString() + "Byte:" + msg.arg1 + "...");
break;
}
};
};
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 = mBluetoothAdapter.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 e) {
errorExit("Fatal Error", "In onResume() and socket create failed: " + e.getMessage() + ".");
}
/*try {
btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) {
errorExit("Fatal Error", "In onResume() and socket create failed: " + e.getMessage() + ".");
}*/
// Discovery is resource intensive. Make sure it isn't going on
// when you attempt to connect and pass your message.
mBluetoothAdapter.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...");
mConnectedThread = new ConnectedThread(btSocket);
mConnectedThread.start();
}
You have all your code in onResume. onResume is going to get called right at startup of the activity, so it will execute almost immediately. I don't see any code in there that should delay it at all. If you don't want to try to connect until the user selects something, then all the connection code should be in a button's click handler or something similar.
In addition to #GabeSechan's comment about all of your code being in onResume(), you are also calling the Bluetooth connect() in your main activity thread which according to the this documentation is a blocking call and "should always be performed in a thread separate from the main activity thread".
I'm trying to find a solution for this setup:
I have a single Android device, which I would like to connect to multiple serial embedded devices...
And here is the thing, using the "Normal" way to retrieve the Bluetooth socket, doesn't work on all devices, and while it does, I can connect to multiple devices, and send and receive data to and from multiple devices.
public final synchronized void connect()
throws ConnectionException {
if (socket != null)
throw new IllegalStateException("Error socket is not null!!");
connecting = true;
lastException = null;
lastPacket = null;
lastHeartBeatReceivedAt = 0;
log.setLength(0);
try {
socket = fetchBT_Socket_Normal();
connectToSocket(socket);
listenForIncomingSPP_Packets();
connecting = false;
return;
} catch (Exception e) {
socket = null;
logError(e);
}
try {
socket = fetchBT_Socket_Workaround();
connectToSocket(socket);
listenForIncomingSPP_Packets();
connecting = false;
return;
} catch (Exception e) {
socket = null;
logError(e);
}
connecting = false;
if (socket == null)
throw new ConnectionException("Error creating RFcomm socket for" + this);
}
private BluetoothSocket fetchBT_Socket_Normal()
throws Exception {
/* The getType() is a hex 0xXXXX value agreed between peers --- this is the key (in my case) to multiple connections in the "Normal" way */
String uuid = getType() + "1101-0000-1000-8000-00805F9B34FB";
try {
logDebug("Fetching BT RFcomm Socket standard for UUID: " + uuid + "...");
socket = btDevice.createRfcommSocketToServiceRecord(UUID.fromString(uuid));
return socket;
} catch (Exception e) {
logError(e);
throw e;
}
}
private BluetoothSocket fetchBT_Socket_Workaround()
throws Exception {
Method m;
int connectionIndex = 1;
try {
logDebug("Fetching BT RFcomm Socket workaround index " + connectionIndex + "...");
m = btDevice.getClass().getMethod("createRfcommSocket", new Class[]{int.class});
socket = (BluetoothSocket) m.invoke(btDevice, connectionIndex);
return socket;
} catch (Exception e1) {
logError(e1);
throw e1;
}
}
private void connectToSocket(BluetoothSocket socket)
throws ConnectionException {
try {
socket.connect();
} catch (IOException e) {
try {
socket.close();
} catch (IOException e1) {
logError("Error while closing socket", e1);
} finally {
socket = null;
}
throw new ConnectionException("Error connecting to socket with" + this, e);
}
}
And here is the thing, while on phones which the "Normal" way doesn't work, the "Workaround" way provides a solution for a single connection. I've searched far and wide, but came up with zip.
The problem with the workaround is mentioned in the last link, both connection uses the same port, which in my case, causes a block, where both of the embedded devices can actually send data, that is not been processed on the Android, while both embedded devices can receive data sent from the Android.
Did anyone handle this before?
There is a bit more reference here,
UPDATE:
Following this (that I posted earlier) I wanted to give the mPort a chance, and perhaps to see other port indices, and how other devices manage them, and I found out the the fields in the BluetoothSocket object are different while it is the same class FQN in both cases:
Detils from an HTC Vivid 2.3.4, uses the "workaround" Technic:
The Socket class type is: [android.bluetooth.BluetoothSocket]
mSocket BluetoothSocket (id=830008629928)
EADDRINUSE 98
EBADFD 77
MAX_RFCOMM_CHANNEL 30
TAG "BluetoothSocket" (id=830002722432)
TYPE_L2CAP 3
TYPE_RFCOMM 1
TYPE_SCO 2
mAddress "64:9C:8E:DC:56:9A" (id=830008516328)
mAuth true
mClosed false
mClosing AtomicBoolean (id=830007851600)
mDevice BluetoothDevice (id=830007854256)
mEncrypt true
mInputStream BluetoothInputStream (id=830008688856)
mLock ReentrantReadWriteLock (id=830008629992)
mOutputStream BluetoothOutputStream (id=830008430536)
**mPort 1**
mSdp null
mSocketData 3923880
mType 1
Detils from an LG-P925 2.2.2, uses the "normal" Technic:
The Socket class type is: [android.bluetooth.BluetoothSocket]
mSocket BluetoothSocket (id=830105532880)
EADDRINUSE 98
EBADFD 77
MAX_RFCOMM_CHANNEL 30
TAG "BluetoothSocket" (id=830002668088)
TYPE_L2CAP 3
TYPE_RFCOMM 1
TYPE_SCO 2
mAccepted false
mAddress "64:9C:8E:B9:3F:77" (id=830105544600)
mAuth true
mClosed false
mConnected ConditionVariable (id=830105533144)
mDevice BluetoothDevice (id=830105349488)
mEncrypt true
mInputStream BluetoothInputStream (id=830105532952)
mLock ReentrantReadWriteLock (id=830105532984)
mOutputStream BluetoothOutputStream (id=830105532968)
mPortName "" (id=830002606256)
mSocketData 0
mSppPort BluetoothSppPort (id=830105533160)
mType 1
mUuid ParcelUuid (id=830105714176)
Anyone have some insight...
WOW, every time this strike me down with one big WTF?
This was a race condition issue, which clearly works on one version of android, and not on another. On Android peer I was parsing the packets received from the socket:
public class SocketListener
implements Runnable {
private boolean stop;
private OnIncomingPacketListener packetListener;
#Override
public void run() {
InputStream inputStream;
try {
stop = false;
inputStream = socket.getInputStream();
while (!stop) {
Packet packet = Packet.getPacket(inputStream);
lastPacket = packet;
if (packet.getDescriptor() == Packet.HeartBeat)
lastHeartBeatReceivedAt = System.currentTimeMillis();
else if (packet.getDescriptor() == Packet.LogEntry)
log.append(((LogEntryPacket) packet).getLogEntry());
synchronized (this) {
if (packetListener != null)
packetListener.onIncomingData(EmbeddedDevice.this, packet);
}
}
} catch (IOException e) {
logError("----- BLUETOOTH IO ERROR -----\n #: " + EmbeddedDevice.this, e);
return;
} catch (RuntimeException e) {
logError("----- BLUETOOTH LISTENER ERROR -----\n #: " + EmbeddedDevice.this, e);
throw e;
} finally {
socketListeningThread = null;
}
}
}
Where the Packet.getPacket(inputStream) is:
public static synchronized Packet getPacketInstance(InputStream inputStream)
throws IOException {
int data = inputStream.read();
Packet type = null;
for (Packet packetType : values())
if (packetType.packetType == data) {
type = packetType;
break;
} // race condition here...
if (type == null)
throw new IllegalArgumentException("Unknown packet type: " + data);
try {
Packet packet = type.incomingPacketType.newInstance();
packet.setDescriptor(type);
packet.readPacketData(inputStream);
return packet;
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw new IllegalStateException("Error instantiating type: " + type.incomingPacketType.getName(), e);
}
}
And every time a packet is completed, the next thread should have gone in to perform it parsing.
My guess is that there is some sort of lock on the port, that together with my implementation caused the second thread to block indefinitely, once I've removed the parsing to different instances per thread, the issue dissolved.
This insight was inspired by Daniel Knoppel, the guy from the mPort link.
Thanks Daniel!