BrainBand を 2 台接続する

Android で Bluetooth を使うを書いてから随分と時間が経ってしまいましたが、ちょっとハマっておりました。

BrainBand を追加で 1 台購入して、2 台接続を試してみたら動かないのです。1 台だけを接続する分には問題ないのですが、2 台目を接続しようとすると下記の部分で Service discovery failed. となってしまいます。

device.createRfcommSocketToServiceRecord(SPP_UUID);

下記の情報により解決はされました。


最終的なコードは下記のようになりました。

public class BluetoothService {

    private static final UUID SPP_UUID = UUID
        .fromString("00001101-0000-1000-8000-00805F9B34FB");

    private List<ClientThread> clients = new LinkedList<ClientThread>();

    public synchronized void connect(
        BluetoothDevice device, ByteStreamParser parser) {
        try {
            ClientThread thread = new ClientThread(device, parser);
            thread.start();
            clients.add(thread);
        } catch (IOException ex) {
        }
    }

    public synchronized void stop() {
        for (ClientThread thread : clients) {
            thread.cancel();
        }
        clients.clear();
        if ((LogTag.MASK & LogTag.FLAG_BLUETOOTH) != 0)
            Log.d(LogTag.S, "Clients was closed.");
    }

    public synchronized boolean isActive() {
        return clients.isEmpty() != false;
    }

    private class ClientThread extends Thread {

        private final ByteStreamParser listener;
        private final BluetoothSocket btSocket;

        ClientThread(BluetoothDevice device, ByteStreamParser listener)
                throws IOException {
            this.listener = listener;
            try {
                this.btSocket = createRfcommSocket(device);
            } catch (IOException ex) {
                Log.e(LogTag.S, "BluetoothSocket can't create, address = "
                        + device.getAddress());
                throw ex;
            }
        }

        private BluetoothSocket createRfcommSocket(BluetoothDevice device)
            throws IOException {
            /* This code cause 'Service discovery failed.'
            try {
                return device.createRfcommSocketToServiceRecord(SPP_UUID);
            } catch (IOException ex) {
            }
            */
            try {
                Method m =
                    device.getClass().getMethod(
                        "createRfcommSocket", new Class[] { int.class });
                return (BluetoothSocket)m.invoke(device, 1);
            } catch (Exception ex) {
                throw new IOException(ex.getMessage());
            }
        }

        @Override
        public void run() {
            try {
                if ((LogTag.MASK & LogTag.FLAG_BLUETOOTH) != 0)
                    Log.d(LogTag.S, "BluetoothSocket connect...");
                btSocket.connect();
                if ((LogTag.MASK & LogTag.FLAG_BLUETOOTH) != 0)
                    Log.d(LogTag.S, "BluetoothSocket connected.");
                InputStream iStream = btSocket.getInputStream();
                if ((LogTag.MASK & LogTag.FLAG_BLUETOOTH) != 0)
                    Log.d(LogTag.S, "Listen stream...");
                listener.parse(iStream);
            } catch (IOException ex) {
                Log.e(LogTag.S, ex.getMessage() + ", adderss = "
                        + btSocket.getRemoteDevice().getAddress());
                closeSocket();
            }
        }

        void cancel() {
            closeSocket();
        }

        private void closeSocket() {
            try {
                btSocket.close();
            } catch (IOException ex) {
                Log.e(LogTag.S, "BluetoothSocket can't close, address = "
                        + btSocket.getRemoteDevice().getAddress());
            }
        }
    }
}

ちょっと createRfcommSocket(int port) メソッドの呼び出しについて調べてみると、メソッド内部では BluetoothSocket.createRfcommSocket(String address, int port) を呼び出しています。createRfcommSocketToServiceRecord(UUID uuid) メソッドの呼び出しでも BluetoothSocket.createRfcommSocket(String address, int port) を呼び出しています。createRfcommSocketToServiceRecord では UUID から channel(=port) を探し出して BluetoothSocket を生成しています。これを channel を 1 固定にすると何故動くのかは不明。詳しい方いらっしゃったら教えてください。