こんにちは、アプリケーションエンジニアの難波です。

BraveridgeではIoTデバイスを製作する時によくNordic社のSoCを使用しています。
そのNordic社はAndroid用のBLEライブラリをOSSとしてリリースしています。

AndroidアプリでBLE機能を実装しようとすると、OSのバージョンや機種依存、ハマりやすいポイント等があったりと結構クセが強いです。
NordicのAndroid用BLEライブラリはその辺りを良い感じに吸収してくれています。
  1. Android-Scanner-Compat-Library
  2. Android-BLE-Library
今回は Android-BLE-Library のPeripheralに関連する機能を実装例と共に紹介します。

Centralの場合と比べて少し複雑になります。

まず BleServerManager を継承したclassを実装します。
このclassでPeripheralのProfileを定義します。
class SampleBleServerManager(private val context: Context) 
: BleServerManager(context) {

val characteristicForRead = characteristic(
UUID.randomUUID(),
BluetoothGattCharacteristic.PROPERTY_READ, 
BluetoothGattCharacteristic.PERMISSION_READ
).apply {
value = byteArrayOf(0x1)
}
private val serviceUUID = UUID.randomUUID()

override fun initializeServer() = listOf(service(serviceUUID, characteristicForRead))
}
initializeServer() の戻り値がBLE接続時にCentralが取得するServiceリストになります。
上の例ではread用のcharacteristicを含むserviceを定義しています。

Advertiseは Android-BLE-Library の範囲外なので自分で実装する必要があります。
class SampleBleServerManager(private val context: Context) 
    : BleServerManager(context) {

// 追加
fun startAdvertising() {
val adviserSettings = AdvertiseSettings.Builder() .build()
val advertiseData = AdvertiseData.Builder()
.addServiceUuid(ParcelUuid(serviceUuid))
.build()
val advertiseCallback = object : AdvertiseCallback() {}
// Advertise開始 BluetoothAdapter.getDefaultAdapter().bluetoothLeAdvertiser
?.startAdvertising(adviserSettings, advertiseData, advertiseCallback)
}
}
次にCentralとの接続から切断までを制御するために BleManager を継承したclassを実装します。BleManager Centralの実装時にも登場しましたが、Peripheralを実装するときも使用します。
class SampleConnectedBleManager(context: Context) 
    : BleManager(context) {

    private var gattCallback: BleManagerGattCallback? = null
    private inner class GattCallback : BleManagerGattCallback() {
        override fun isRequiredServiceSupported(gatt: BluetoothGatt) = true
        override fun onDeviceDisconnected() {}
    }

    override fun getGattCallback() = gattCallback ?: run {
        gattCallback = GattCallback()
        gattCallback!!
    }
}

最後に BleServerManager に接続された時に BleManager を関連づけます。
class SampleBleServerManager(private val context: Context)
    : BleServerManager(context) {

private val connectionObserver = object: BaseConnectionObserver {}
private val serverObserver = object: ServerObserver { override fun onServerReady() {
// advertise開始 startAdvertising() }
override fun onDeviceConnectedToServer(device: BluetoothDevice) {
// BleServerManager と BleManager を関連づける
val connectedManager = SampleConnectedBleManager(context) connectedManager. setConnectionObserver(connectionObserver)
connectedManager.useServer(this) connectedManager .connect(device) .enqueue() } override fun onDeviceDisconnectedFromServer(device: BluetoothDevice) {} }
init { setServerObserver(serverObserver) } }

SampleBleServerManager#open()
をコールすると SampleBleServerManager#initializeServer() が実行され、Peripheralとしての動作準備を行います。
動作準備が完了すると SampleBleServerManager#onServerReady() がコールされるので、Advertiseを開始してPeripheralとして動作します。
val bleServerManager = SampleBleServerManager(context)
bleServerManager.open()

Peripheralを実装するには接続したCentralのBluetoothDeviceインスタンスを管理する必要がありますが、BleServerManager を利用するとその必要もありません。
またCentralとのコミュニケーションを BleManager が行うので、リクエスト・レスポンスのキューを実装する必要がありません。

AndroidのBLE処理をきちんと実装するとコード量が多くなりがちですが、Android-BLE-Libraryを利用すると結構コンパクトに実装できます。

サンプルアプリを含めたソースコード全体は github で確認できます。