Bluetooth低エネルギーデバイスは、そのアドレスによって一意に識別されます(Android APIでは、これをMACアドレスと呼び、コロンで区切られた16進値として示します。例:11:aa:22:bb:33: cc)。
ただし、BLEアドレスを一意に識別するには、それがパブリックアドレスかプライベートアドレスかを知る必要があります。要するに、アドレスを識別するために48ではなく49ビットが必要です。
ランダムアドレスは、静的なランダム、解決不可能なプライベート、または解決可能なプライベートのいずれかであり、これらのタイプは2つの最上位バイト(それぞれ11、00、10)のビットパターンで区切られます。
しかし、アドレスの48ビットを調べるだけでパブリックアドレスとランダムアドレスを分離できる場所はどこにもありません。
Android APIではどのように機能しますか?指定したアドレスがパブリックであるかランダムであるかがわからない場合、接続先のデバイスをどのようにして知るのですか?
問題のAPIは、たとえば getRemoteDevice 関数です。それは言う:
Valid Bluetooth hardware addresses must be upper case, in a format such as
"00:11:22:33:AA:BB". The helper checkBluetoothAddress(String) is available
to validate a Bluetooth address.
A BluetoothDevice will always be returned for a valid hardware address,
even if this adapter has never seen that device.
したがって、関数に48ビットのデータを与え、アドレスがパブリックであるかプライベートであるかを伝える方法はありません。これは、デバイスが一意に識別されないことを意味します。
他にだれも答えるように思わないので、私は自分でテストを始めました。
アドレスの文字列表現からデバイスを作成するアプリを作成し、48ビットアドレスでパブリックビットまたはプライベートビットを交互に使用してデバイスをセットアップして、Androidスタックの機能を確認しました。
private final BluetoothGattCallback leGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.i("Fisken", "Gatt connected " + gatt.getDevice().getAddress() + " status " + status);
if (status != BluetoothGatt.GATT_SUCCESS) {
Log.w("Fisken", "Disconnect and close");
gatt.disconnect();
gatt.close();
}
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i("Fisken", "Gatt disconnected " + gatt.getDevice().getAddress() + " status " + status);
if (status != BluetoothGatt.GATT_SUCCESS) {
Log.w("Fisken", "Disconnect and close");
gatt.disconnect();
}
gatt.close();
}
}
};
BluetoothAdapter mBluetoothAdapter = ((BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE)).getAdapter();
BluetoothDevice d = mBluetoothAdapter.getRemoteDevice("FF:55:44:33:22:11");
d.connectGatt(this, false, leGattCallback);
このコードを使用して、ランダムアドレスでBLEペリフェラルを開始すると、すべてが期待どおりに機能します。ただし、公開ビットが設定された同じアドレスで実行しようとすると、logcatは「Gatt connected」と表示しますが、それは正しくありません。そして、私は切断することができません。
更新:これを理解するためにさらにテストを行いました。私が取得するonConnectionStateChangeイベントは、接続試行のタイムアウトだけです。ステータスは133(STATE_CONNECTEDを取得した場合)または257(STATE_DISCONNECTEDを取得した場合)に設定されており、両方が表示されています。どちらの場合でも、接続の試行をキャンセルしてクライアントを閉じる必要があります(今はサンプルコードで行います)。
また、最初にスキャンを実行すると、接続しようとしているデバイスが最近表示され、、次に実行することもわかりましたデバイスのMACアドレスのみに基づいて接続すると、問題なくランダムアドレスとパブリックアドレスの両方に接続できます。
Android APIのバグまたは不足している機能のようです。最初にスキャンしないとパブリックアドレスに接続できません。ただし、ランダムに機能しますアドレス。
アドレスがパブリックであるかランダムであるかを推測することは可能ですが、すべての場合で機能するわけではありません。
上記のように、ランダムなアドレスの場合、両方のMSBは00xx、01xx、または11xxのいずれかです。したがって、10xxの場合、それはパブリックアドレスです(OUIが8,9、AまたはB)
また、登録されているOUIの数は既存のものと比べて非常に限られているため、IEEEデータベースでOUIの候補を検索することで、一致する結果はおそらく公のアドレスになります。
登録済みOUIカウント:〜20500、つまり2 ^ 24ビットのうち0.12%、2 ^ 22ビットのうち0.48%。
IEEEデータベースがなければ、OUIの最初のLSBは常に0であり、2番目のLSBはほとんど常に0であるという事実に依存する可能性があります(実際には、これらのアドレスは普遍的に管理されるため、常に0でなければなりません)。
また、他の統計分析を使用することもできます。たとえば、OUIの60%は00で始まります。一方、解決できないプライベートアドレスは、1.60%の確率で00で始まります(均一ランダムジェネレーターを使用)。
元の「パブリックアドレスとランダムアドレスを区別するために49ビットが必要」は正しいと思います。 MSBを「10」に制限するIEEEパブリックアドレスのエンコーディングには何も見つかりません。これは、trueであれば問題を解決します。
したがって、使用できるのは、ペリフェラルのアドバタイズメントの「ランダムアドレス」ビット設定、またはセントラルの接続開始パケットの同等のビット設定のみです。これらのビットが設定されていない場合、そのエンドポイントが公開するアドレスはパブリックです。
追加します:コア仕様第6巻パートBセクション1.3からデバイスのアドレス:MSの呼び出し=最も重要
Static random address: two MB bits of MS byte are 1 1 such that the MS byte is 11xxxxxx & with 0xC0
Non-resolvable private address: two MB bits of MS byte are 0 0 such that the MS byte is 00xxxxxx & with 0x00
Resolvable private address: two MB bits of MS byte are 0 1 such that the MS byte is 01xxxxxx & with 0x40
パブリックアドレスを上記のタイプのアドレスのいずれかと区別する方法はなく、アドレスタイプフラグも必要です。したがって、49番目のビット(追加のフラグ)の必要性。アドレスだけではそれはできません!
広告アドレスがパブリックであるかプライベートであるかは、広告メッセージのヘッダーで設定されます。アプリケーションレイヤーのアドレスタイプをパブリックに設定すると、BLEリンクレイヤーが実際の「MAC」アドレスを送信します。アドレスタイプをstatic/private resolvableに設定すると、BLEリンクレイヤーがID解決キー(IRK)でアドレスをスクランブルすることを示します。
上位2ビットを調べることにより、パブリックアドレスとプライベートアドレスを区別できます。アドレスは49ビットではなく48ビット長です。CoreBluetooth仕様v4.2、Vol 6、Part B、セクション1.3を参照してください。