Android BLE ScanSettingsの使い方

AndroidのBLEスキャン設定(ScanSettingsクラス)についてまとめてみました。

# 注意点
BLEスキャン設定で注意したほうが良いことをまとめるとこんな感じです。

- Bluetotohのチップに依存するスキャン設定があります。
- Bluetoothチップは機種によって違うのでBLEスキャンの設定によっては機種毎に動作が異ります。
- 機種依存を回避したいなら、ハードウェアに依存する設定はしないほうが良さそう。
- BLEスキャンモードはなるべく省電力なモードを選ぶと良さそう。

# setCallbackType(int callbackType)
#### 概要
BLEスキャン結果を通知する**コールバックの通知方法**を設定します。
何も設定しない場合のデフォルト値はCALLBACK_TYPE_ALL_MATCHESです。

#### APIレベル
23 (Android 6.0 Marshmallow)

#### パラメータ
| 値          | 説明                 |
|:-----------------|:-------------------------------|
| CALLBACK_TYPE_ALL_MATCHES|スキャンフィルタにマッチするすべてのスキャン結果を通知します。スキャンフィルタが設定されていない場合は、すべてのスキャン結果が通知されます。|
| CALLBACK_TYPE_FIRST_MATCH|フィルタにマッチした最初のスキャン結果だけを通知します。|
| CALLBACK_TYPE_MATCH_LOST|CALLBACK_TYPE_FIRST_MATCHで通知したスキャン結果が、スキャンで見つけられなくなった時に通知します。|

### 注意事項
Androidの公式ドキュメントに以下に記載があります。
>アプリで Bluetooth Low Energy スキャンを実行する場合は、新しいsetCallbackType() メソッドを使って、設定された ScanFilter に一致する広告パケットが最初に見つかったときと、長時間経過した後に見つかったときにコールバックを通知するようにシステムに指定します。このスキャン アプローチでは、以前のプラットフォーム バージョンで提供されていたアプローチよりも電力が効率的に使用されます。

これは、CALLBACK_TYPE_FIRST_MATCH、CALLBACK_TYPE_MATCH_LOSTを使った時に省電力になるという意味かと思います。
Android OSのソースを見るとこの省電力機能はHWレイヤーで行われています。
なので、Android端末がこの動作をHWレベルでサポートしていない場合はBLEスキャンがエラーになります。
サポートしている場合は、その動作はHW依存なのでAndroidの機種によってはスキャン結果が通知されたりされなかったりする可能性があります。

Nexus 5XでCALLBACK_TYPE_FIRST_MATCHを試してみましたが、スキャン結果は２件通知されることがありました。必ず１件通知されるわけではなさそうです。
(2018/10/11 追記)
Galaxy S8(Android 8.0)でCALLBACK_TYPE_FIRST_MATCHを試したところ、必ず１件通知されました。やはり、機種によって動作に違いがあるようです。

(2018/10/12 追記)
Galaxy S8(Android 8.0)でCALLBACK_TYPE_FIRST_MATCHを試したところ
BLEスキャンがエラー(SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES)になることがありました。

# setLegacy(boolean legacy)
#### 概要
Bluetooth 4.2以前のAdvertiseだけをスキャン結果として通知するかどうかを設定します。
Bluetooth 5のAdvertiseをスキャンしたい場合にfalseにする必要があります。
デフォルト値はtrueなので、Bluetooth 4.2以前のAdvertiseしか考慮しないのであれば
何も設定する必要はありません。

#### APIレベル
26 (Android 8.0 Oreo)

# setMatchMode(int matchMode)
#### 概要
BLEスキャンフィルタのマッチモードを設定します(ハードウェアマッチ)。
デフォルト値はMATCH_MODE_AGGRESSIVEです。
#### APIレベル
23 (Android 6.0 Marshmallow)
#### パラメータ
| 値          | 説明                 |
|:-----------------|:-------------------------------|
|MATCH_MODE_AGGRESSIVE|Bluetoothの電波が微弱でも検知すればスキャン結果として通知します。|
|MATCH_MODE_STICKY|電波強度が一定以上(HW依存)のスキャン結果だけを通知します。|

### 注意事項
MATCH_MODE_STICKYは電波がしっかりと捉えられたAdvertiseだけをスキャン結果として通知しますが、その判定はハードウェアに依存します。
機種依存を避けたい場合、何も設定せずにデフォルト値のままにしておくのが良さそうです。

# setNumOfMatches(int numOfMatches)
#### 概要
フィルタにマッチした時にスキャン結果として通知する数を設定します(ハードウェアマッチ)
スキャン結果をもらう時に1個だけとか、２、３個あれば十分といった時に使用するものと思われます。
何も設定しない時のデフォルト値はMATCH_NUM_MAX_ADVERTISEMENTです。
#### APIレベル
23 (Android 6.0 Marshmallow)
### パラメータ
| 値          | 説明                 |
|:-----------------|:-------------------------------|
|MATCH_NUM_ONE_ADVERTISEMENT|スキャンフィルタにマッチした1件のスキャン結果を通知します。|
|MATCH_NUM_FEW_ADVERTISEMENT|スキャンフィルタにマッチした数件のスキャン結果を通知します。件数はHWに依存します。|
|MATCH_NUM_MAX_ADVERTISEMENT|ハードウェアリソースの許す限り多くのスキャン結果を通知します。|

### 注意事項
setCallbackType()と似ている設定です。どのように使い分けるのか不明です。
ハードウェア依存する設定なので機種依存を避けたいのであれば設定を変えないほうが良さそうです。

# setPhy(int phy)
#### 概要
BLEスキャンで使用する物理層(PHY)を設定します。
Bluetooth 5仕様のBLEスキャンを使いたい時に設定します。
何も設定しない時のデフォルト値はPHY_LE_ALL_SUPPORTED。

PHYは以下の図の緑色の枠の部分です。
![mm170914_bluetooth3.jpg](https://qiita-image-store.s3.amazonaws.com/0/102273/a24faa74-caba-4087-782d-f12dcc93285b.jpeg)
#### APIレベル
26 (Android 8.0 Oreo)
#### パラメータ
| 値          | 説明                 |
|:-----------------|:-------------------------------|
|BluetoothDevice.PHY_LE_1M|Bluetooth 4.0から使われているPHYです。|
|BluetoothDevice.PHY_LE_CODED|Bluetooth 5で追加されたPHYです。Bluetooth 4に比べて、距離が約4倍になります。 |
|ScanSettings.PHY_LE_ALL_SUPPORTED| 全てのPHYを使ってBLEスキャンをします。|

# setReportDelay(long reportDelayMillis)
#### 概要
BLEスキャン結果が通知されるまでの待ち時間を設定します。
何も設定しない場合の初期値は0です。0の場合、スキャンはすぐに通知されます。
ディレイを設定した場合、設定した時間が経過した後か内部バッファがいっぱいになった時にスキャン結果が通知されます。
#### APIレベル
26 (Android 8.0 Oreo)

# setScanMode(int scanMode)
#### 概要
BLEスキャンモードを設定します。
何も設定しない場合のデフォルト値はSCAN_MODE_LOW_POWERです。
#### APIレベル
21 (Android 5.0 Lollipop)
#### パラメータ
| 値          | 説明                 |
|:-----------------|:-------------------------------|
|SCAN_MODE_LOW_POWER|0.5秒スキャン→4.5秒待つを繰り返します。|
|SCAN_MODE_BALANCED|2秒スキャン→3秒待つを繰り返します。|
|SCAN_MODE_LOW_LATENCY|ずっとスキャンします。|
|SCAN_MODE_OPPORTUNISTIC|特別なモードです。他のアプリがBLEスキャンを実行するとスキャン結果が通知されます。|

#### 動作イメージ
<img width="931" alt="ble_scan.png" src="https://qiita-image-store.s3.amazonaws.com/0/102273/6104b6e9-060b-c810-749f-a806bb4aacdf.png">


スキャン結果をなるべく早く通知して欲しい場合はSCAN_MODE_LOW_LATENCYを使いますが
バッテリーの消費量が多いので要注意です。
ペリフェラル側の仕様にもよりますが、SCAN_MODE_LOW_POWERでも５秒以内には検出できるのでなるべくバッテリー消費量の少ないモードを選択したほうが良いと思います。


# 参考
- https://developer.android.com/about/versions/marshmallow/android-6.0?hl=ja
- https://developer.android.com/reference/android/bluetooth/le/ScanSettings.Builder

- https://devzone.nordicsemi.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-00-04-DZ-1046/2604.BLE_5F00_on_5F00_Android_5F00_v1.0.1.pdf