最近、もっぱらBluedroidのソースコードを読んでいるのですが、気になったことがあるのでまとめてみました。(BluedroidをRTOS等に移植したい方がいましたらご連絡ください。)
#結論
まず結論から言うと、Android 5.0でBLE advertisingをするための要件は、Broadcomの独自仕様であるmultiple advertisementのHCIコマンドを使用できるチップである必要があります。
Googleが提供したBluedroidをそのまま使った場合ですが...
#解説
Android 4.4 までの(正確にはPreview-Lまでの)BluedroidではBT-SIGで定めたHCIコマンドのみを使う実装でした。ベンダ独自コマンドは別ライブラリ(libbt-vendor)として使うこともできました。
Android 5.0のBluedroidでは、GoogleがBroadcomのベンダ独自コマンドを積極的に使用するように方針を変えました。その結果、Broadcomの独自HCIコマンドが有効でないと、Android 5.0で追加されたBLE peripheral関連のAPIが動作しないような実装になっています。
Android 5.0ではmultiple advertisementのHCIコマンドが有効でないと、BLEのadvertisingができない実装になっています。
multi advertisementのHCIコマンドはBroadcomの独自仕様で、詳しい事は良くわかりませんが、ソースコードを見たところ、いくつかのパターンのadvertisementを時分割に切り替えて送信する
機能のようです。(あってるかどうかは未確認です。)
CDDでも以下の記載になっており、Multiple advertisementのサポートが要求されています。
SHOULD support multi advertisement with at least 4 slots, but if not supported, MUST report ‘false’ whenever
queried via the android.bluetooth.BluetoothAdapter.isMultipleAdvertisementSupported()
method
今回の独自仕様ゴリ押しで、既存端末でBLE peripheralが動作しないので、AndroidのBLE peripheralの普及が数年単位で遅延しそうです。
#今後の予想
今後どうなるかを何パターンか予想してみました。
- Broadcom以外のチップベンダーが独自HCIコマンドに対応する。
- Broadcom以外のチップベンダーが、Bluedroidにソフトウェアでmulti advertisementに対応する修正を行う。
- Googleが折れて、GoogleがBluedroidにソフトウェアでmulti advertisementに対応する修正を行う。
この他にも、BluezとかCyanogenModが頑張るという可能性もあります。
個人的には、中国でのシェアが多いWi-Fi/BTコンボチップ MT6630を持つMediatekがどう対応するのかを注目しています。
#個人的な感想
BT-SIGの規格に無いコマンドを推奨する事は、かなり性悪な感じです。Broadcomが特許の罠を仕掛けていて、他社から儲けようという魂胆なのでしょうか。
Multi advertiseに対応していなくても通常のアドバタイズができるように互換性を維持することは考えなかったのはどんな意図があるのか。(Bluedroid自体にはset_adv_data()等のBT-SIGのHCIコマンドでアドバタイズするAPIはあります。(
BroadcomのチップセットでもNexus9とNexus6に載っている最新のチップでしかこのHCIコマンドは対応していません。古いチップでもBT部分のファームウェアを更新して使えるようにはしてくれないのでしょうか
もしBluezを使っていたらコミュニティから反発があったのでこの仕様にはならなかった可能性が高いなぁ。
以前の機種でBLE peripheralをサポートしたくないのは、BT−SIG認証とか電波法的なリスクがあるという可能性もあるのかも知れません。
まぁ、オープンソースなので、なんとかしたい人がなんとかして進んでいくと思います。
#ソースコード
上記の根拠となるソースコードについて下から説明してみます。
BluedroidはBroadcomの独自HCIコマンドで、Multi Advertiseに対応しているかを調べます。
HCI_BLE_VENDOR_CAPを送信している箇所
BTM_API extern void BTM_BleReadControllerFeatures(tBTM_BLE_CTRL_FEATURES_CBACK *p_vsc_cback)
{
if (TRUE == btm_cb.cmn_ble_vsc_cb.values_read)
return;
#if BLE_VND_INCLUDED == TRUE
BTM_TRACE_DEBUG("BTM_BleReadControllerFeatures");
p_ctrl_le_feature_rd_cmpl_cback = p_vsc_cback;
if ( BTM_VendorSpecificCommand (HCI_BLE_VENDOR_CAP_OCF,
0,
NULL,
btm_ble_vendor_capability_vsc_cmpl_cback)
!= BTM_CMD_STARTED)
{
BTM_TRACE_ERROR("LE Get_Vendor Capabilities Command Failed.");
}
#else
UNUSED(p_vsc_cback);
#endif
return ;
}
HCI_BLE_VENDOR_CAPの応答を解析している箇所
btm_cb.cmn_ble_vsc_cb.adv_inst_maxにmulti advertisementのインスタンス数が入ります。この応答が帰ってこない場合は、btm_cb.cmn_ble_vsc_cb.adv_inst_maxは初期値0のままです。
static void btm_ble_vendor_capability_vsc_cmpl_cback (tBTM_VSC_CMPL *p_vcs_cplt_params)
{
UINT8 status = 0xFF, *p;
BTM_TRACE_DEBUG("btm_ble_vendor_capability_vsc_cmpl_cback");
/* Check status of command complete event */
if ((p_vcs_cplt_params->opcode == HCI_BLE_VENDOR_CAP_OCF) &&(p_vcs_cplt_params->param_len > 0))
{
p = p_vcs_cplt_params->p_param_buf;
STREAM_TO_UINT8 (status, p);
}
if (status == HCI_SUCCESS)
{
STREAM_TO_UINT8 (btm_cb.cmn_ble_vsc_cb.adv_inst_max, p);
STREAM_TO_UINT8 (btm_cb.cmn_ble_vsc_cb.rpa_offloading, p);
STREAM_TO_UINT16 (btm_cb.cmn_ble_vsc_cb.tot_scan_results_strg, p);
STREAM_TO_UINT8 (btm_cb.cmn_ble_vsc_cb.max_irk_list_sz, p);
STREAM_TO_UINT8 (btm_cb.cmn_ble_vsc_cb.filter_support, p);
STREAM_TO_UINT8 (btm_cb.cmn_ble_vsc_cb.max_filter, p);
STREAM_TO_UINT8 (btm_cb.cmn_ble_vsc_cb.energy_support, p);
btm_cb.cmn_ble_vsc_cb.values_read = TRUE;
}
このmulti advertisementのインスタンス数はbluedroidのAPIにあるコールバック関数経由で上位レイヤーに通知されます。
void adapterPropertyChangedCallback(int[] types, byte[][] values) {
Intent intent;
int type;
byte[] val;
for (int i = 0; i < types.length; i++) {
val = values[i];
type = types[i];
infoLog("adapterPropertyChangedCallback with type:" + type + " len:" + val.length);
synchronized (mObject) {
switch (type) {
...
case AbstractionLayer.BT_PROPERTY_LOCAL_LE_FEATURES:
updateFeatureSupport(val);
break;
}
}
}
}
javaのbtserviceのクラス変数にセットします。
void updateFeatureSupport(byte[] val) {
mNumOfAdvertisementInstancesSupported = (0xFF & ((int)val[1]));
このクラス変数の値を元にMulti Advertise対応可否を決定します。
MIN_ADVT_INSTANCES_FOR_MAは5です。
public boolean isMultiAdvertisementSupported() {
AdapterService service = getService();
if (service == null) return false;
int val = service.getNumOfAdvertisementInstancesSupported();
return (val >= MIN_ADVT_INSTANCES_FOR_MA);
}
AndroidのAPIではさらにもう一段経由してサポート可否を取得できます。
public boolean isMultipleAdvertisementSupported() {
if (getState() != STATE_ON) return false;
try {
return mService.isMultiAdvertisementSupported();
} catch (RemoteException e) {
Log.e(TAG, "failed to get isMultipleAdvertisementSupported, error: ", e);
}
return false;
}
ということで、Broadcomの独自ベンターコマンドでmulti advertisementのインスタンス数が5以上を取得できないとisMultipleAdvertisementSupported()がfalseになるのでBLE advertisingができません。
#追記
2015-03になって上記のBroadcomの独自HCI仕様を、Android 5.0 Bluetooth HCI Requirementsとして開示しました。
Broadcom以外のチップベンダも今後このHCI仕様を実装していくことになるみたいです。
#参考
AndroidのBluetoothアーキテクチャー説明
https://source.android.com/devices/bluetooth.html
Issue 1570: BLE advertise mode not working
この件に関するチケット。Googleの見解が載っている。
https://code.google.com/p/android-developer-preview/issues/detail?id=1570
Android 5.0 Compatibility Definition Document
https://source.android.com/compatibility/android-5.0-cdd.pdf
Android 5.0 Bluetooth HCI Requirements
https://source.android.com/devices/Android-5.0-Bluetooth-HCI-Reqs.pdf