【Kotlin/Android】Bluetoothのペアリング(ボンディング)を行う方法!createBond

【Kotlin/Android】Bluetoothのペアリング(ボンディング)を行う方法!createBond

この記事からわかること

  • Android Studio/KotlinBluetooth接続アプリ実装方法
  • Central側の実装
  • ペアリング(ボンディング)するには?
  • createBondメソッド使い方
  • ACTION_BOND_STATE_CHANGEでボンディング状態変化を検知する

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

環境

ペアリングとは?

例えばBluetoothイヤホンなどを使用する際に初回にBluetooth接続(ペアリング)を行えば次回からは自動で接続される経験があると思います。これは両方の機器がお互いを登録し合っているために2回目以降のペアリング作業を省略できるようになっています。

Bluetooth機器を使用しているとよく聞く「ペアリング」という言葉ですがこれはセントラルとペリフェラル間のデータ暗号化のための鍵の交換をすること自体を指します。一般的にはペアリングが機器同時の接続のようなニュアンスですが厳密にみると少し異なるので注意してください。

ボンディングとは?

一度ペアリング(鍵を交換)した機器がペアリングなしに接続できるようになるのは交換した鍵をキャッシュしているからです。そしてこの「暗号鍵をキャッシュしておき再度同じ相手と接続するときに、ペアリングを省略できる機能」のことを「ボンディング」と呼びます。

Androidアプリでボンディングする方法

記事タイトルにはあえてペアリングと書きましたがボンディングする方法をまとめていきます。(ペアリングは接続時に自動で行われるので特に触れません)

またセントラル側の実装は前回の記事を参考にしてください。この続きで今回は進めていきます。

前回の記事ではデバイスアドレスをローカルに保存することで再度ペアリングしなくても接続できるように実装していました。ただこれはボンディング機能を実装することでローカル保存処理を不要にすることができます。

実装はGitHubに公開しているので参考にしてください。

createBondメソッド

実際にボンディングを行うにはBluetoothDevice#createBondメソッドを使用します。

// 対象機器とボンディングする
device.createBond()

サンプルコードではスキャン処理を実行して対象のペリフェラルを検出した際になどに呼び出しています。


private fun leScanCallback(): ScanCallback {
    return object : ScanCallback() {
        override fun onScanResult(
            callbackType: Int,
            result: ScanResult,
        ) {
            super.onScanResult(callbackType, result)
            result.device ?: return
            // ペリフェラルデバイスが発見された
            logArea.append( "デバイス「${result.device.name}」を検出\n")
            // スキャンの停止
            bluetoothLeScanner?.stopScan(scanCallback)
            // デバイスアドレスを取得(接続処理に必要)
            val deviceAddress = result.device.address
            // ローカルにデバイスアドレスを保存
            // sharedPreferencesManager.save(SharedPreferencesManager.ADDRESS_KEY, deviceAddress)
            // 対象機器とボンディングする
            result.device.createBond()
            logArea.append( "スキャン停止\n")
        }
    }
}

createBondを実行すると端末上にペアリングを許可するかどうかのポップアップが表示されます。ここで「ペア設定する」を選択することでボンディングが完了します。

【Kotlin/Android】Bluetoothのペアリング(ボンディング)を行う方法!createBond

ボンディングが完了するとAndroid端末の「設定アプリ」>「接続設定」>「保存済みのデバイス」ボンディングしているデバイス情報が表示されるようになります。ここから接続やボンディング情報の削除を行うことができるようになっています。

【Kotlin/Android】Bluetoothのペアリング(ボンディング)を行う方法!createBond

ボンディングしているデバイスと接続する

ボンディングしただけでは接続はできていないのでボンディングしているデバイスと接続する処理を実装していきます。端末でボンディングしている機器情報はBluetoothAdapter#bondedDevicesからリストで取得することが可能です。この中には他にボンディングしている機器の情報もあるのでアプリ内で使用するにはデバイス名などでフィルタリングをかけて対象のBluetoothDeviceを取得します。

// ボンディングしているデバイス一覧
bluetoothAdapter?.bondedDevices
// 対象のデバイスアドレスを取得する
val deviceAddress = bluetoothAdapter?.bondedDevices?.firstOrNull { it.name == BleServiceConfig.PERIPHERAL_NAME }?.address

デバイス情報が取得できれば接続処理が実行できるようになります。

/** ③ デバイスアドレスを元に接続処理 */
private fun connect(address: String) {
    val device: BluetoothDevice? = bluetoothAdapter?.getRemoteDevice(address)

    if (device == null) {
        logArea.append("デバイス取得失敗\n")
        return
    }
    logArea.append("対象デバイスと接続開始\n")
    bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback)
}

ボンディング状態を識別する

BluetoothDeviceボンディング済みかどうかbondStateプロパティから識別することができます。取得できる値はint型になっているのでBluetoothDeviceに定義されている定数と照らし合わせて状態を確認します。


public static final int BOND_BONDED = 12;  // ボンディング済み
public static final int BOND_BONDING = 11; // ボンディング中
public static final int BOND_NONE = 10;    // 未ボンディング

ボンディング状態の変化を検知する

ボンディングリクエストを行いポップアップが表示された際に「ペア設定をする」と「キャンセル」が選択できるようになっており、その後のボンディング状態の変化を検知することが可能です。

公式リファレンス:BluetoothDevice#ACTION_BOND_STATE_CHANGED

BluetoothDevice#ACTION_BOND_STATE_CHANGEDというBroadcast Actionが用意されているのでこれを使用すれば検知できました。

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    // 〜〜〜〜〜〜〜〜〜〜〜〜〜

    // ボンディング状態の変化をBroadcastで受け取るためのIntentFilter
    val bondStateFilter = IntentFilter().apply {
        addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED)
    }
    // レシーバーをセット
    registerReceiver(bondingBroadcastReceiver, bondStateFilter)
}

結果はBroadcastReceiver型のオブジェクトを作成して受け取ります。

 /** ボンディング状態が変化した際に取得できるBroadcast */
private val bondingBroadcastReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        context ?: return
        val action = intent?.action ?: return
        when (action) {
            BluetoothDevice.ACTION_BOND_STATE_CHANGED -> {
                logArea.append("ボンディングBroadcast取得")
                // ボンディング対象デバイスを取得する
                val device =
                    intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
                        ?: return
                logArea.append("${device.address}")
                val bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 0)
                // ボンディング状態を識別
                when (bondState) {
                    BluetoothDevice.BOND_BONDED -> {
                        logArea.append("ボンディング成功")
                    }

                    BluetoothDevice.BOND_BONDING -> {
                        logArea.append("ボンディング中")
                    }

                    BluetoothDevice.BOND_NONE -> {
                        logArea.append("ボンディング失敗(キャンセル)")
                    }
                }
            }
        }
    }
}

他にもペアリングをリクエストしたことを検知するACTION_PAIRING_REQUESTなどがあります。詳細は公式を参考にしてください。

コードからボンディング情報を削除する

ボンディング情報を削除するためのメソッドとしてBluetoothDevice#removeBondがありますがこちらはprivateで定義されているため開発者が呼び出して使用することは想定されていません。そのためコードからボンディング情報を削除することは不可能になっているようです。

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index