AndroidのL2CAPサポートについて
Androidでは、Android 10でCoCがサポートされました。
従来のBLEでは、クライアントからサーバーに分かれて、少量のデータ転送やデータ通知でコミュニケーションを行ってましたが、BLE CoCでは、接続が確立されたまま、大容量なデータを連続的に送受信できるものになります。
AndroidのBLE CoCを担うものとして(おそらく)L2CAPがAPI 29から追加されているので、その使い方を説明します。
AndroidでL2CAP接続を確立してデータを送信する
Androidから、他のデバイスへL2CAP接続を行うためには、まず対向の端末をL2CAPとして接続し、データ交換のための BluetoothSocket
を取得する必要があります。
Androidが他端末に接続する場合、またはAndroid端末でL2CAPチャネルを開いて接続を待ち受ける場合、どちらの場合もBluetoothAdapterを使いますので、まずはBluetoothAdapterを取得します。
val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
// Bluetooth アダプターの取得
val bluetoothAdapter = bluetoothManager.adapter
Androidから他デバイスへL2CAP接続
Androidから、他のデバイスへL2CAP接続を行うためには、BluetoothDevice
を使っての接続を行います。
BluetoothDeviceは様々な取得方法があり、BLEのスキャンによって端末を取得する方法がありますが、すでにペアリングしている端末に対しては、アドレスを用いての接続も可能になります。
以下のコードは、ペアリング済み端末からBluetoothDeviceを取得して、createInsecureL2capChannel
でL2CAP接続を行うコードになります。
対向の端末からL2CAPの識別子PSM(Protocol/Service Multiplexer) を取得して、その値でL2CAP接続を行います。
createInsecureL2capChannelの返り値でBluetoothSocketが帰ってきます。
// ペアリング済み端末のアドレス
val deviceAddress = "00:00:00:00:00:00"
val device: BluetoothDevice? = bluetoothAdapter?.getRemoteDevice(deviceAddress)
// PSMを対向端末から教えてもらう
val psm = 192
bluetoothSocket = device?.createInsecureL2capChannel(psm)!!
bluetoothSocket?.connect()
他デバイスからAndroidにL2CAP接続
他デバイスがAndroidに対してL2CAP接続をする場合、Android側でPSMを発行してL2CAPチャネルを開ける必要があります。
listenUsingL2capChannel
を行うことで、BluetoothServerSocketが取得できます。
BluetoothServerSocketはpsmを持っているので、これを対向の端末に伝えることでL2CAP接続を要求してもらうことになります。
その際、accept()
などを行うことにより、対向端末の接続を待ち受けることが可能です。
accept()は接続完了まで処理をブロックするので非同期になるように策を講じておくことが必要です。
accept()の戻り値がBluetoothSocketになります。
val serverSocket = bluetoothAdapter.listenUsingL2capChannel()
// PSMを対向の端末に伝える
Log.d(TAG, "psm ${serverSocket.psm}")
lifecycleScope.launch {
val bluetoothSocket = withContext(Dispatchers.IO) {
serverSocket.accept()
}
}
BluetoothSocketでデータのやり取り
BluetoothSocketに備わっている、inputStreamやoutputStreamを用いてでデータのやり取りが可能です。
L2CAPはBLEよりも大容量のデータやり取りが可能です。
データ受信
inputStreamを使ってデータを受信します。
while (true) {
val buffer = ByteArray(65536)
val inputStream = bluetoothSocket.inputStream
val ret = inputStream.read(buffer, 0, 65536)
if (ret > 0) {
// データ処理
val str = String(buffer, 0, ret)
}
}
データ送信
outputStreamを使ってデータを書き込みます。
val bytes = "こんにちは".toByteArray()
if (bluetoothSocket.isConnected) {
bluetoothSocket.outputStream.write(bytes)
}