BLE視点でまとめるAndroid OSの違い

Bluetooth Low Energy Advent Calendar 2016 5日目担当の[@ozyozyo](https://twitter.com/ozyozyo)です。
今日はBLE周り担当の開発者視点で、Android OSの違いについて書きます。
多くのAndroid開発者は「BLEは闇だ...」といいますが、明るく楽しく絵文字多めでいきたいと思います:dancers:

## Android 4.3 未満
いきなり未満でくくってしまいますが、この人達はBLE非対応です。
普段は対応端末をつかって開発するのでつい忘れがちですが、
BluetoothAdapter#getDefaultAdapterがnullを返すので、4.3未満ではぬるぽで落ちます!みたいなことにならないように、QA時にはチラ見したい子です:bow:
https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getDefaultAdapter()

## Android 4.3
BLEデビューバージョンです:birthday::tada:
デビューしてすぐだから仕方ないんですが、今となっては慣れ親しんでいるBluetoothLeScannerがまだない世代なので、
BluetoothAdapter#LeScanCallback等を利用しなければならず、4系サポートするのはだいぶ面倒です。

続いて、こちらのstackoverflowにもあるように、端末によってなのかchipsetなのか分かりませんが、#onLeScanが呼ばれるタイミングが異なるようで、advertiseしているdeviceを1度のscanで1回しか見つけられない場合と、1度のscanで何度も見つけられる場合があるようです。
定常的にpacketがadvertiseされているか確認したい場合は、定期的にscannerをstart/stopすると一応なんとかなります...
http://stackoverflow.com/questions/19502853/android-4-3-ble-filtering-behaviour-of-startlescan

続いて定数系ですが、BluetoothGatt#setCharacteristicNotificationできる数の上限が4つまでで、それ以上はエラーになります。Characteristicを増やせないのは地味に困りますね。
あと、connectionを張れる数の上限も4つまでです。~~そもそも4.3ではconnectionを安定的に貼るのは難しいので上限があっても困りませんね~~
これらの定数についての説明は、ドキュメントにはありませんが、こちらの動画の29分頃からにあります。
https://www.youtube.com/watch?feature=player_embedded&v=qx55Sa8UZAQ#t=0

個人的には、メーカーごとの挙動の差が大きいですし、Bluetooth共有が強制終了しまくるし、諸々不安定なので、BLEと聞いたら「 $\color{red}{\rm 5系以上対応にしましょう}$ 」と脊椎反射することを強くお勧めします。
(4系の端末をOSアップデートして5系にしても安定して動かないのではないかと思ってはいます...)

## Android 4.4
4.3よりちょっと扱い安くなります。
なんと、↑の動画にあるように、setCharacteristicNotificationとconnectionの上限が７つに増えます。
それ以上connectionをしようとすると引き続き、133 errorが返ってきます。

## Android 5系
setCharacteristicNotificationの上限が15個に増えます:yum:!!
connectionの上限は7つのまま:smile:!!
BluetoothLeScannerが追加されます:dancers:!!
ScanFilterが使えるようになり、必要なpacketだけにscan結果をfilterできるようになるのと、
ScanModeの設定ができるようになってscanの頻度を設定できるようになります。
centralの5系対応については、こちらのブログに良い知見がまとまっていました。
http://recruit.gmo.jp/engineer/jisedai/blog/android5-scan-ble/

そして、Android 5.0以降ではついにAndroidでAdvertiseができるようになります:tada:
https://developer.android.com/about/versions/android-5.0.html?hl=ja

5系であまり苦しんだ思い出がないので、意外とそんなに書くことがないです。

## Android 6.0
Android Mといえば、Runtime permissionですね。
BLEまわりも例外ではなく、位置情報のpermissionがないと、scanができません。それだけではなく、OSの位置情報設定をONにしないと、scanができません。
これらはtargetSdkを23にしないと影響がないと思いきや、23以下でもOSの位置情報設定をOFFにするとscanしなくなってしまいます:sob:
http://androidxref.com/6.0.0_r1/xref/packages/apps/Bluetooth/src/com/android/bluetooth/gatt/GattService.java#641

続いてbackgroundでも動かしたい場合に問題になるのがDoze modeですが、すれ違い通信だとかBeacon用途であれば、
Android MのDoze modeは端末が移動しているときにはDozeにならないとのことなので、まだ救いようがありますね。

## Android 6.0.1 
Android 6.0.0で、OSの位置情報をONにしないとscanしないと書きましたが、Android 6.0.1では、targetSdkが23未満だと、アプリがforegroundのときのみ位置情報をONにしなくてもscanできるようになる優しさがある様子です。
ちなみに、このチェックはscanの開始時に見ているようなので、foregroundでscanを開始すればbackgroundに行ってもscanは続きます:+1:
http://androidxref.com/6.0.1_r10/xref/packages/apps/Bluetooth/src/com/android/bluetooth/Utils.java#isMApp

まあ、targetSdkなんて、23ですよね...:confused: 

## Android 7
あまり大きな問題にはならないと思いますが、一度に5つ以上のscanができなくなります。[該当のcommit](http://androidxref.com/7.0.0_r1/diff/packages/apps/Bluetooth/src/com/android/bluetooth/gatt/GattService.java?r2=%2Fpackages%2Fapps%2FBluetooth%2Fsrc%2Fcom%2Fandroid%2Fbluetooth%2Fgatt%2FGattService.java%40aef39f16e48cbae0ace8136c3235136c0a096061&r1=%2Fpackages%2Fapps%2FBluetooth%2Fsrc%2Fcom%2Fandroid%2Fbluetooth%2Fgatt%2FGattService.java%40a6ad5869940240d1f40649d9ca23e468f537b065)
寧ろ5つもscannerが走ってるのは大抵バグだと思うので個人的にはログが出て助かってます。

そしてAndroid NのDoze modeは、端末が移動しているときもDoze modeになってしまいます。
screenをOFFにしてから5分後(LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT)に浅いDoze状態になりますが、浅いDozeではpacketのscanは可能のようです。
その後も静止状態でいると、深いDozeになってしまい、scanができなくなってしまいます。
http://androidxref.com/7.0.0_r1/xref/frameworks/base/services/core/java/com/android/server/DeviceIdleController.java

メンテナンスウィンドウではもちろんscanが出来て、それ以外ではAlarmManager#setAndAllowWhileIdleか#setExactAndAllowWhileIdleを使うと9分に1度だけscanができるようになります。

Doze mode対応として、BeaconならNearBy APIを使えばいいのでは？みたいな噂も聞いたのですが、EddyStone id(iBeaconならUUID、major、minorに当たるもの)を事前に登録しなければいけなかったり、登録するとしても挙動がよく分からなくて誰か教えてください:bow:

## iOSとの違い
結局BLEを使うときはおそらくスマホと何らかのdeviceの通信を提供したいときだと思うので、Android/iOSのinterfaceが共通ではない苦しみについても書こうと思っていたのですが、既に長いので別の機会に:+1:

# 最後に
BLE案件をやっているときは、さっきまで動いたのに何故か動かないとか、つらいとか、19と133っていう数字を見るだけで胃が痛くなるとか、この端末だけ不安定だからこの端末燃やそうとか、もうGPSでええやんとか、ネガティブな気持ちでいっぱいになってしまうことがあるかと思うのですが、不安定でも諦めずに頑張れば、ちょっとconnectionは安定します:muscle::muscle::muscle:

困ったときは、心を無にして深呼吸してから、端末のBluetoothをOFFからON!!それでも駄目なら端末再起動しましょう!!
connect後のreadとかwriteとか、1つのcommandが実行中に別のcommandを叩くとfailするからQueueを作りましょう!!
advertiseしてるアプリがbackgroundで動いてると、connectionが不安定になるので止めましょう:pray:
RSSIはOSではなく端末によって全然違う値を出してくるので、複数メーカーのデバイスで動作確認しましょう!!

# まとめ
BLEと聞いたら「 $\color{red}{\rm 5系以上}$ 」と脊椎反射!!BLE最高٩(๑❛ᴗ❛๑)۶

明日はBLE大好きエンジニアのyousukeさんです!!