Android Bluetooth Low Energy 

仕事でBluetooth Low Energyを使用したのでまとめ

# Bluetooth Low Energy（BLE）とは
[Bluetooth Low Energy の概要](https://developer.android.com/guide/topics/connectivity/bluetooth-le?hl=ja)からの引用。
Bluetooth Low Energy（BLE）は、クラシック Bluetooth と比較して、消費電力を大幅に抑えた設計となっています。そのおかげで Android アプリが、近接センサー、心拍数モニター、フィットネス端末など電力要件が厳しい BLE 端末と通信することが可能になっています。

# Bluetooth Low Energy（BLE）ドキュメント
Androidデベロッパー向けのドキュメントの[Bluetooth Low Energy の概要](https://developer.android.com/guide/topics/connectivity/bluetooth-le?hl=ja)を確認。

# Bluetooth Low Energy（BLE）の参考コード
Androidでの使用方法はAndroidの中に存在する可能性が高いです。そのためAOSPを確認します。

- [Android BluetoothLeGatt Sample](https://android.googlesource.com/platform/developers/samples/android/+/refs/heads/master/connectivity/bluetooth/BluetoothLeGatt/)
- [CTS Verifier BLE tests](https://android.googlesource.com/platform/cts/+/refs/heads/master/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/)

上記内にBLEを使用するための参考コードがあるので参照してください。
ここでは参考コードの解説はしません。


# Bluetooth Low Energy（BLE）の実装方法
## パーミッションの追加
BLEを使用するためには次の３つのパーミッションを追加する必要がある。

```xml
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

<!-- If your app targets Android 9 or lower, you can declare
     ACCESS_COARSE_LOCATION instead. -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
```

> 上記パーミッションにはアプリの権限をリクエストする必要があるものが含まれます。
> 権限のリクエスト方法は[アプリの権限をリクエストする](https://developer.android.com/training/permissions/requesting?hl=ja)を参照してください。

## インストールの制限
次の宣言を記述するとBLEサポート端末のみしかインストール出来なくなります。BLE必須の場合は宣言しても良いとは思います。BLEはあくまでも一部の機能である場合は，後述する方法でBLEサポート端末化を確認可能。

```xml
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
```

## BLE のセットアップ
`Bluetooth`のアクティビティには`BluetoothAdapter`が必要なため，BluetoothAdapterを取得する方法を記載します。
### `BluetoothAdapter`を取得する

```java
BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
```

### BLEサポート端末か確認

1. *bluetoothManager.getAdapter()*がnullの場合`Bluetooth`はサポートされていません。
2. `PackageManager.hasSystemFeature()`で`BLE`を使用可能か判断


```java
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
    // BLE not supported
}
```

### `Bluetooth`のON，OFF確認
`bluetoothAdapter.isEnabled()`で`Bluetooth`のON，OFF確認を行いOFFの場合は，ONに変更してもらう必要があります。

例：`Bluetooth`をONに切り替えてもらうためのインテント発行方法

```java
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent, REQUEST_ENABLE_BT);
```

## BLE 端末の検出
`BLE`デバイスを検索するには[BluetoothLeScanner](https://developer.android.com/reference/android/bluetooth/le/BluetoothLeScanner)のインスタンスを取得する必要があります。
次のようにして`BluetoothLeScanner`を取得します。

```java
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
BluetoothLeScanner bleScanner bluetoothAdapter.getBluetoothLeScanner();
```

`BluetoothLeScanner`の`startScan()`メソッドを使用する事でBLEデバイスの検索を行えます。

```java
bleScanner.startScan(buildScanFilters(), buildScanSettings(), callback);
```

`startScan()`メソッドへ渡すための`List<ScanFilter>`、`ScanSettings`は下記のようにして設定します。

```java
    /**
     * Return a List of {@link android.bluetooth.le.ScanFilter} objects to filter by Service UUID.
     */
    private List<ScanFilter> buildScanFilters() {
        List<ScanFilter> scanFilters = new ArrayList<>();

        ScanFilter.Builder builder = new ScanFilter.Builder();
        // Comment out the below line to see all BLE devices around you
        builder.setServiceUuid(null);
        scanFilters.add(builder.build());

        return scanFilters;
    }

    /**
     * Return a {@link android.bluetooth.le.ScanSettings} object set to use low power (to preserve
     * battery life).
     */
    private ScanSettings buildScanSettings() {
        ScanSettings.Builder builder = new ScanSettings.Builder();
        builder.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER);
        return builder.build();
    }
```

`startScan()`メソッドへ渡すためのコールバックは下記のように記述します。
対象のBLE端末がスキャンされるたびにonScanResult()が呼び出されます。


```java
/**
 * Helper class for BLE scan callback.
 */
public class BleScanCallback extends ScanCallback {
    private Set<ScanResult> mResults = new HashSet<>();
    private List<ScanResult> mBatchScanResults = new ArrayList<>();

    @Override
    public void onScanResult(int callbackType, ScanResult result) {
        if (callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES) {
            mResults.add(result);
        }
    }

    @Override
    public void onBatchScanResults(List<ScanResult> results) {
        // In case onBatchScanResults are called due to buffer full, we want to collect all
        // scan results.
        mBatchScanResults.addAll(results);
    }
}
```

## GATTサーバーへの接続(BLE端末への接続)
BLE端末に接続すること、厳密に言うと、BLE端末上の GATTサーバーに接続することです。BLE端末上の GATTサーバーに接続するには、`BluetoothDevice`の`connectGatt()`メソッドを使用します。
`BluetoothDevice`のインスタンスは次のように取得します。

```java
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
```

GATTサーバーへの接続は次のように行います。

```java
BluetoothGatt gatt = device.connectGatt(context, false, gattCallback, BluetoothDevice.TRANSPORT_LE)
```

`connectGatt()`メソッドへ渡すためのコールバックは下記のように記述します。

```java
public class BleGattCallback extends BluetoothGattCallback {
    /**
     * {@inheritDoc}
     */
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void onCharacteristicRead(BluetoothGatt gatt,
            BluetoothGattCharacteristic characteristic,
            int status) {
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt,
            BluetoothGattCharacteristic characteristic,
            int status) {
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt,
            BluetoothGattCharacteristic characteristic) {
    }
}
```

- **onConnectionStateChange**：接続・切断の際に呼び出されます。接続時は`discoverServices()`メソッドを呼びます。
- **onServicesDiscovered**：接続時に`discoverServices()`メソッドを呼び出すと、完了後にこのメソッドが呼び出されます。この後、BLEからの通知を受け取るための`UUID`の登録や`Mtu`の値変更などすると良いです。
- **onCharacteristicWrite**：BLEデバイスに向けてAndroidからメッセージを送る（Write）と書き込み完了としてonCharacteristicWriteが呼び出されるようです。
- **onCharacteristicChanged**：BLEデバイスからAndroidへメッセージが送られるとこのメソッドが呼び出されます。どのUUIDからメッセージが送られたのかを確認して適切な処理を行うのが良いでしょう。

## GATTサーバーの切断(BLE端末の切断)
`connectGatt()`メソッド呼び出しで受け取った`BluetoothGatt`の`disconnect()`メソッドを呼び出すと切断できます。終了する際は、`close()`メソッドも呼び出しておくと良いでしょう。

```java
gatt.disconnect();
gatt.close();
```

# その他注意点
1. `onCharacteristicChanged()`メソッドでBLEからメッセージを受け取って、BLEデバイスに何か値を返却するなどあると思いますが、`onCharacteristicChanged()`のスレッド内（処理内）でBLEデバイスに書き込むのは非常に危険です。別スレッド（メインスレッド）から書き込みましょう。
2. BLEデバイスに対して書き込みを連続で行うのは非常に危険です。書き込んだらその都度`onCharacteristicWrite()`メソッドの書き込み完了を待ちましょう。

# サンプルコード

* Java版

https://github.com/sacred-sanctuary/BLEDemo

* Kotlin版

https://github.com/sacred-sanctuary/BLEDemoKotlin

内容はBLEデバイスをスキャンして、選んだBLEに接続するだけのシンプルなものになります。