Question

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...

Was it helpful?

Solution

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!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top