3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AndroidAdvent Calendar 2023

Day 25

AndroidでL2CAP

Last updated at Posted at 2023-12-25

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を取得します。

kotlin XXXActivity.kt
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)
}
3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?