Android BLE API及びAndroid Beacon Libraryの設計の酷さを技術的に説明する

# **はじめに**

Android 4.3 から、BLE (Bluetooth Low Energy) 用の API が提供されるようになりました。その後、Android 5.0 で `android.bluetooth.le` というパッケージが追加され、BLE 用の API が再定義されました。しかし、輪をかけて酷くなりました。

しばらく BLE の仕事を離れることになりそうなので (そして恐らく戻ってこないので)、Android BLE API に対して言いたいことを忘れないうちに書いておこうと思います。なお、主に BLE アドバータイズメントパケットのペイロードの話になります。BLE API 全体については扱いません。


# **1. ペイロードのフォーマット**

BLE アドバータイズメントパケットのペイロード部のフォーマットが BLE の仕様でどのように定義されているかを知れば、あるべき API の姿が想像できます。そこで、まずはその説明をします。具体的には、[LeScanCallback](http://developer.android.com/intl/ja/reference/android/bluetooth/BluetoothAdapter.LeScanCallback.html) インターフェースで定義されている [onLeScan](http://developer.android.com/reference/android/bluetooth/BluetoothAdapter.LeScanCallback.html#onLeScan(android.bluetooth.BluetoothDevice,%20int,%20byte[])) メソッドの三番目の引数である `byte[] scanRecord` の中身がどのようなフォーマットになっているかの説明をします。


## **1.1. ペイロードの構造**

BLE アドバータイズメントパケットのペイロード部は、**AD ストラクチャー**というもののリストとなっています。個々の AD ストラクチャーは可変長です。下図は三つの AD ストラクチャーを含むペイロードを表しています。

![payload.png](https://qiita-image-store.s3.amazonaws.com/0/106044/714c5a9d-0597-e043-10dd-a25cd8273cbc.png)


## **1.2. AD ストラクチャーの構造**

AD ストラクチャーは、1 バイト目に「AD ストラクチャー全体の長さ - 1」、2 バイト目に AD ストラクチャーのタイプ (**AD タイプ**)、3 バイト目以降はデータ (**AD データ**) となっています。データの長さとフォーマットは、2 バイト目の値が示す AD タイプによって変化します。次の表は AD ストラクチャーの構造を示しています。

| 位置 | 大分類 |
|:--|:--|
| 1 バイト目 | 長さ |
| 2 バイト目 | AD タイプ |
| 3 バイト目以降 | AD データ |

例えば、AD タイプが `0x01` (Flags) である AD ストラクチャーは、データ部の長さは 1 バイトになるので <sup>(ただし仕様拡張により今後変化する可能性はあります)</sup>、全体としては 3 バイトになります。次の表は AD タイプが `0x01` のときの AD ストラクチャーの構造を示しています。

| 位置 | 大分類 | 中分類 | 値 |
|:--|:--|:--|:--|
| 1 バイト目 | 長さ | | `0x02` |
| 2 バイト目 | AD タイプ | Flags | `0x01` |
| 3 バイト目 | AD データ | フラグ群 | `0x??` |

なお、AD ストラクチャーの構造については、「Bluetooth Core Specification 4.2」の「11 ADVERTISING AND SCAN RESPONSE DATA FORMAT」で次のように説明されています。

> Each **AD structure** shall have a **Length** field of one octet, which contains the Length value, and a Data field of Length octets. The first octet of the Data field contains the **AD type** field. The content of the remaining Length - 1 octet in the Data field depends on the value of the AD type field and is called the **AD data**.


## **1.3. AD タイプの種類**

定義済みの AD タイプの種類は、「[Generic Access Profile](https://www.bluetooth.org/en-us/specification/assigned-numbers/generic-access-profile)」ページにリストアップされています。下表は、当該ページ内の表から「Reference for Definition」欄を取り除いて見やすくしたものです。

| 値 | データタイプ名 |
|:--:|:--|
| `0x01` | Flags |
| `0x02` | Incomplete List of 16-bit Service Class UUIDs |
| `0x03` | Complete List of 16-bit Service Class UUIDs |
| `0x04` | Incomplete List of 32-bit Service Class UUIDs |
| `0x05` | Complete List of 32-bit Service Class UUIDs |
| `0x06` | Incomplete List of 128-bit Service Class UUIDs |
| `0x07` | Complete List of 128-bit Service Class UUIDs |
| `0x08` | Shortened Local Name |
| `0x09` | Complete Local Name |
| `0x0A` | Tx Power Level |
| `0x0D` | Class of Device |
| `0x0E` | Simple Pairing Hash C |
| `0x0E` | Simple Pairing Hash C-192 |
| `0x0F` | Simple Pairing Randomizer R |
| `0x0F` | Simple Pairing Randomizer R-192 |
| `0x10` | Device ID |
| `0x10` | Security Manager TK Value |
| `0x11` | Security Manager Out of Band Flags |
| `0x12` | Slave Connection Interval Range |
| `0x14` | List of 16-bit Service Solicitation UUIDs |
| `0x1F` | List of 32-bit Service Solicitation UUIDs |
| `0x15` | List of 128-bit Service Solicitation UUIDs |
| `0x16` | Service Data |
| `0x16` | Service Data - 16-bit UUID |
| `0x20` | Service Data - 32-bit UUID |
| `0x21` | Service Data - 128-bit UUID |
| `0x22` | ​LE Secure Connections Confirmation Value |
| `0x23` | LE Secure Connections Random Value |
| `0x24` | URI |
| `0x25` | Indoor Positioning |
| `0x26` | Transport Discovery Data |
| `0x17` | Public Target Address |
| `0x18` | Random Target Address |
| `0x19` | Appearance |
| `0x1A` | Advertising Interval |
| `0x1B` | ​LE Bluetooth Device Address |
| `0x1C` | ​LE Role |
| `0x1D` | Simple Pairing Hash C-256 |
| `0x1E` | Simple Pairing Randomizer R-256 |
| `0x3D` | 3D Information Data |
| `0xFF` | Manufacturer Specific Data |


## **1.4. 製造者固有データ**

AD タイプ一覧表の末尾に、値が `0xFF`、データタイプ名が「Manufacturer Specific Data」(製造者固有データ) というエントリーがあります。これは、AD データのフォーマットを自由に拡張できるようにするためのものです。

AD タイプが `0xFF` の場合、AD データ部の先頭の 2 バイトには、企業を識別するための番号がリトルエンディアンで格納されます。AD データ部の 3 バイト目以降の内容は、その企業が自由に定義することができます。次の表は、AD タイプが `0xFF` のときの AD ストラクチャーの構造を示しています。

| 位置 | 大分類 | 中分類 |
|:--|:--|:--|
| 1 バイト目 | 長さ | |
| 2 バイト目 | AD タイプ | 製造者固有 |
| 3 ～ 4 バイト目 | AD データ | 企業 ID |
| 5 バイト目以降 | AD データ | 企業固有データ |

例えば、Apple 社が独自にデータ構造を定義する場合、Apple 社の企業 ID は `0x004C` ですので、AD ストラクチャーは次のようになります。企業 ID はリトルエンディアンで格納されるので注意してください。

| 位置 | 大分類 | 中分類 | 小分類 | 値 |
|:--|:--|:--|:--|:--|
| 1 バイト目 | 長さ | | | ? |
| 2 バイト目 | AD タイプ | 製造者固有 | | `0xFF` |
| 3 バイト目 | AD データ | 企業 ID | Apple 社 | `0x4C` |
| 4 バイト目 | AD データ | 企業 ID | Apple 社 | `0x00` |
| 5 バイト目以降 | AD データ | 企業固有データ | Apple 社固有データ | ? |


## **1.5. 企業 ID**

登録済み企業 ID は「[Company Identifiers](https://www.bluetooth.com/specifications/assigned-numbers/company-Identifiers)」ページにリストされています。Apple 社の企業 ID が `0x004C` であることも、このページを見ると分かります。


## **1.6. iBeacon**

製造者固有データの一例として、**iBeacon** を紹介します。

iBeacon は、Apple 社が定めた独自フォーマットです。AD データ部は、先頭 2 バイトに Apple 社の企業 ID、次の 2 バイトが `0x02` と `0x015` で固定、以降、16 バイトの Proximity UUID、2 バイトのメジャー番号、2 バイトのマイナー番号、1 バイトの送信出力が続きます。Proximity UUID、メジャー番号、マイナー番号はビッグエンディアンで格納されます。

iBeacon フォーマットを図にすると次のようになります。

![ibeacon-format.png](https://qiita-image-store.s3.amazonaws.com/0/106044/31644ccc-6991-5d87-8479-6c7dfb966597.png)

表形式で表現すると次のようになります。

| 位置 | 大分類 | 中分類 | 小分類 | 値 |
|:--|:--|:--|:--|:--|
| 1 バイト目 | 長さ | | | 26 |
| 2 バイト目 | AD タイプ | 製造者固有 | | `0xFF` |
| 3 バイト目 | AD データ | 企業 ID | Apple 社 | `0x4C` |
| 4 バイト目 | AD データ | 企業 ID | Apple 社 | `0x00` |
| 5 バイト目 | AD データ | 企業固有データ | 固定値 | `0x02` |
| 6 バイト目 | AD データ | 企業固有データ | 固定値 | `0x15` |
| 7 ～ 22 バイト目 | AD データ | 企業固有データ | Proximity UUID | ? |
| 23 ～ 24 バイト目 | AD データ | 企業固有データ | メジャー番号 | ? |
| 25 ～ 26 バイト目 | AD データ | 企業固有データ | マイナー番号 | ? |
| 27 バイト目 | AD データ | 企業固有データ | 送信出力 | ? |

「iBeacon パケット」という言葉を耳にすることがあると思いますが、技術的に正確に表現すると、「iBeacon フォーマットの AD ストラクチャーを含むペイロードを持つ BLE アドバータイズメントパケット」となります。


## **1.7. Eddystone**

iBeacon を紹介したので、**[Eddystone](https://github.com/google/eddystone)** も紹介します。

Eddystone は Google 社が旗振り役なので、Google 社の企業 ID を持つ製造者固有データとして定義されていると思われるかもしれませんが、実はそうではなく、**サービスデータ**の一種として定義されています。

サービスデータを表す AD タイプは複数あるのですが、Eddystone は `0x16` を使用します。この AD タイプの場合、AD データ部の先頭に置くサービス識別子が 2 バイトで表現されます。ちなみに、AD タイプ `0x20` と `0x21` もサービスデータを意味しますが、サービス識別子をそれぞれ 4 バイト、16 バイトで表現する点が異なります。

次の表は、AD タイプが `0x16` で、サービス識別番号に Eddystone を表す `0xFEAA` がセットされている AD ストラクチャーの構造を示しています。サービス識別番号はリトルエンディアンで格納されます。

| 位置 | 大分類 | 中分類 | 値 |
|:--|:--|:--|:--|
| 1 バイト目 | 長さ | | ? |
| 2 バイト目 | AD タイプ | サービスデータ | `0x16` |
| 3 バイト目 | AD データ | サービス識別番号 | `0xAA` |
| 4 バイト目 | AD データ | サービス識別番号 | `0xFE` |
| 5 バイト目以降 | AD データ | サービス固有データ | ? |

5 バイト目以降のサービス固有データ部は、Eddystone 独自のフォーマットとなります。

まず、サービス固有データ部の 1 バイト目の上位 4 ビットで Eddystone のフレーム種別を指定します。Eddystone には次の三つのフレーム種別があり、その種別をこの 4 ビットで指定します。

1. **[Eddystone UID](https://github.com/google/eddystone/tree/master/eddystone-uid)**
2. **[Eddystone URL](https://github.com/google/eddystone/blob/master/eddystone-url)**
3. **[Eddystone TLM](https://github.com/google/eddystone/blob/master/eddystone-tlm)**

サービス固有データ部の 2 バイト目以降のデータフォーマットは、フレーム種別毎に異なります。Eddystone の仕様は GitHub の [google/eddystone](https://github.com/google/eddystone) で公開されているので、ここで詳細に解説はしませんが、図にまとめておきます。

![eddystone-format.png](https://qiita-image-store.s3.amazonaws.com/0/106044/9165508d-ee1d-18de-cd51-d54bd9e7ead2.png)


## **1.8. ucode**

iBeacon と Eddystone を知っておけば十分ですが、せっかく日本の組込業界の [T-Engine フォーラム](http://www.t-engine.org/)が定義した「[Bluetooth LE ucode マーカーパケット仕様](http://www.uidcenter.org/ja/wp-content/themes/wp.vicuna/pdf/UID-00049-01.A0.01.pdf)」というものがあるので、紹介しておきます。

この仕様は、T-Engine フォーラムが定義したグローバル ID である「**[ucode](http://en.wikipedia.org/wiki/Ucode_system)**」の値を BLE アドバータイズメントパケットに埋め込むフォーマットを、製造者固有データの一種として定義しています。ITU-T H.642 としても採択されています。詳細は[仕様書](http://www.uidcenter.org/ja/wp-content/themes/wp.vicuna/pdf/UID-00049-01.A0.01.pdf)を参照してください。


# **2. 理想と現実**

## **2.1. 理想**

ここまで、BLE アドバータイズメントパケットのペイロード部のフォーマットについて見てきました。得られた知識をもとに、あるべき API の姿を考えてみます。

ペイロード部の値がバイト配列として与えられたとき、それをパースするメソッドは、結果として何を返せばよいでしょうか？ ペイロード部は AD ストラクチャーのリストなので、それをそのまま表現する結果を返せばよいでしょう。例えば、Java で表現するとしたら、次のような形です。

```java
// BLE アドバータイズメントパケットのペイロード部
byte[] payload = ...;

// ペイロードをパースして、AD ストラクチャーのリストとして受け取る。
List<ADStructure> list = parse(payload);
```

個々の AD ストラクチャーは、実際には、Flags を表しているかもしれませんし、iBeacon かもしれませんし、Eddystone URL かもしれません。この関係は、継承ツリーとして表現するのが自然でしょう。例えば、ここまでに紹介した、AD ストラクチャー、Flags、iBeacon、Eddystone、ucode の関係は、次のような継承ツリーを構成するのが望ましいと思われます。

![inheritance-tree.png](https://qiita-image-store.s3.amazonaws.com/0/106044/1268573b-49c9-bc86-5f89-2e2ea2b7b6a2.png)

このような継承ツリーが API でも表現されていれば、例えば Eddystone URL の情報を表示するプログラムは次のように書けるでしょう (階層構造を示すために、わざと `if` を深くネストさせて書いています)。

```java
// AD ストラクチャー
ADStructure ads = ...;

// どの AD ストラクチャーにも、長さとタイプがある。
System.out.println("長さ = " + ads.getLength());
System.out.println("タイプ = " + ads.getType());

// もしもサービスデータであれば、
if (ads instanceof ServiceData)
{
    // サービスデータとして扱うことができる。
    ServiceData sd = (ServiceData)ads;

    // どのサービスデータにも、サービスを一意に特定するサービス UUID がある。
    System.out.println("サービス UUID = " + sd.getServiceUUID());

    // サービスデータの中でも、特に Eddystone であれば、
    if (sd instanceof Eddystone)
    {
        // Eddystone データとして扱うことができる。
        Eddystone es = (Eddystone)sd;

        // どの Eddystone データにも、フレーム種別がある。
        System.out.println("フレーム種別 = " + es.getFrameType());

        // Eddystone URL であれば、
        if (es instanceof EddystoneURL)
        {
            // Eddystone URL として扱うことができる。
            EddystoneURL esurl = (EddystoneURL)es;

            // Eddystone URL には、送信出力と URL の情報が含まれている。
            System.out.println("送信出力 = " + esurl.getTxPower());
            System.out.println("URL = " + esurl.getURL());
        }
    }
}
```

さらに、独自のデータ構造を定義し、それをペイロードをパースした結果のリストに含ませることを可能とするため、何かしら独自パーサーを定義して登録できるとよいでしょう。例えば、AD ストラクチャーの構成要素である「(1) 長さ、(2) AD タイプ、(3) AD データ」から AD ストラクチャーのサブクラスのインスタンスを生成して返すメソッドを持つインターフェースを次のように定義し、

```java
public interface ADStructureBuilder
{
    /**
     * AD ストラクチャーを構築する。
     *
     * @param length
     *         AD ストラクチャーの長さ。 AD ストラクチャーの
     *         バイト配列の一番目のバイトの値。
     *
     * @param type
     *         AD タイプ。 AD ストラクチャーのバイト配列の
     *         二番目のバイトの値。
     *
     * @param data
     *         AD データ。 AD ストラクチャーのバイト配列の
     *         三番目以降のバイト群。
     *
     * @return
     *         ADStructure もしくはそのサブクラスのインスタンス。
     *         インスタンスを生成できないときは null を返す。
     */
    ADStructure build(int length, int type, byte[] data);
}
```

このインターフェースを実装したクラスのインスタンスを何らかの方法で登録できるようなっていればよいでしょう。

## **2.2. 現実**

さて、現実の世界ではどのような API となっているのでしょうか？

[API リファレンス](http://developer.android.com/reference/packages.html)には、「<code>[BluetoothAdapter](http://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html)</code> クラスの <code>[startLeScan(LeScanCallback)](http://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#startLeScan(android.bluetooth.BluetoothAdapter.LeScanCallback))</code> メソッドは API level 21 (Android 5.0) で deprecated 扱いとなったので、かわりに <code>[startScan(List, ScanSettings, ScanCallback)](http://developer.android.com/reference/android/bluetooth/le/BluetoothLeScanner.html#startScan(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.bluetooth.le.ScanCallback))</code> メソッドを使うように」と書いてあります。この新しいメソッドの三番目の引数は <code>[ScanCallback](http://developer.android.com/reference/android/bluetooth/le/ScanCallback.html)</code> クラスのインスタンスです。クラスの名称から、アドバータイズメントパケットを受信したときに呼び出されるコールバックだということが分かります。Android 4.X から Android 5.X にバージョンアップしたときに、コールバックが <code>[LeScanCallback](http://developer.android.com/reference/android/bluetooth/BluetoothAdapter.LeScanCallback.html)</code> から <code>[ScanCallback](http://developer.android.com/reference/android/bluetooth/le/ScanCallback.html)</code> に変更になったようです。

この <code>[ScanCallback](http://developer.android.com/reference/android/bluetooth/le/ScanCallback.html)</code> クラスには次のような三つのメソッドが定義されています。

```java
void onBatchScanResults(List<ScanResult> results) { ... }
void onScanFailed(int errorCode) { ... }
void onScanResult(int callbackType, ScanResult result) { ... }
```

メソッド名が <code>on</code> で始まることから、何らかのイベントを受け取るためにはこれらのメソッドをオーバーライドすればいいだろうということが推測できます。ただ、これら以外のメソッドが定義されていないことから、「なぜインターフェースではなくクラスとして定義したのか」という疑問は当然湧いてきて、早くも怪しげですが、次に進みます。

<code>[ScanCallback](http://developer.android.com/reference/android/bluetooth/le/ScanCallback.html)</code> クラスに定義されている <code>[onBatchScanResults](http://developer.android.com/reference/android/bluetooth/le/ScanCallback.html#onBatchScanResults(java.util.List<android.bluetooth.le.ScanResult>))</code> メソッドも <code>[onScanResult](http://developer.android.com/reference/android/bluetooth/le/ScanCallback.html#onScanResult(int, android.bluetooth.le.ScanResult))</code> メソッドも、データ構造として <code>[ScanResult](http://developer.android.com/reference/android/bluetooth/le/ScanResult.html)</code> クラスのインスタンスを受け取ることが分かります。そこで、この <code>[ScanResult](http://developer.android.com/reference/android/bluetooth/le/ScanResult.html)</code> クラスの定義を見ると、幾つかメソッドがありますが、アドバータイズメントパケットをパースした結果は、<code>[getScanRecord()](http://developer.android.com/reference/android/bluetooth/le/ScanResult.html#getScanRecord())</code> メソッドにより <code>[ScanRecord](http://developer.android.com/reference/android/bluetooth/le/ScanRecord.html)</code> クラスのインスタンスとして取り出すことができることが分かります。

```java
ScanRecord getScanRecord() { ... }
```

ではここで、<code>[ScanRecord](http://developer.android.com/reference/android/bluetooth/le/ScanRecord.html)</code> クラスのメソッド一覧を紹介します。

| 戻り値 | メソッド |
|:--|:--|
| `int` | `getAdvertiseFlags()` |
| `byte[]` | `getBytes()` |
| `String` | `getDeviceName()` |
| `SparseArray<byte[]>` | `getManufacturerSpecificData()` |
| `byte[]` | `getManufacturerSpecificData(int)` |
| `byte[]` | `getServiceData(ParcelUuid)` |
| `Map<ParcelUuid,byte[]>` | `getServiceData()` |
| `List<ParcelUuid>` | `getServiceUuids()` |
| `int` | `getTxPowerLevel()` |
| `String` | `toString()` |

アドバータイズメントパケットのペイロードのフォーマットを知っていれば、これらのメソッドがそれぞれ何の情報を返そうというしているのかが分かります。`getBytes()` メソッドと `toString()` メソッドを除く他のメソッドは、ある特定の AD タイプを持つ AD ストラクチャーのデータを返そうとしているのです。表にまとめると次のようになります。

| メソッド | AD タイプ |
|:--|:--|
| `getAdvertiseFlags` | `0x01` |
| `getDeviceName` | `0x08`, `0x09` |
| `getManufacturerSpecificData` | `0xFF` |
| `getServiceData` | `0x16`, `0x20`, `0x21` |
| `getServiceUuids` | `0x02` ～ `0x07` |
| `getTxPowerLevel` | `0x0A` |

ペイロードのフォーマットをちゃんと理解していれば、ここで <code>[ScanRecord](http://developer.android.com/reference/android/bluetooth/le/ScanRecord.html)</code> クラスの設計の酷さに気付くはずです。

まず、1 つのペイロードに、上記表内に挙げられている全ての AD ストラクチャーが同時に含まれていることは、まずありません。また逆に、上記のいずれの AD ストラクチャーも含まないペイロードというのも当然ありえます。ですので、`ScanRecord` インスタンスを受け取っても、上記のメソッド群が全て `null` もしくは空のコレクションを返す場合があります。

次に、選択された AD タイプの恣意性です。なぜ、`0x01` ～ `0x0A`, `0x16`, `0x20`, `0x21`, `0xFF` は選ばれ、他は選ばれなかったのか。この非対称性はなんなのでしょうか。

`ScanRecord` の設計を、動物を例に例えます。今、動物に関するデータが並んでいて、それをパースするとします。パースの結果を受け取るとき、受け手は普通、`List<動物>` というデータ構造を期待します。しかし、`ScanRecord` と同じ構造とするならば、受け手が受け取るのは、次のようなデータ構造です。

```java
public class 動物レコード
{
    getロシアンブルー() { ... }
    getポメラニアン() { ... }
    get鳥類タカ目() { ... }
    get両生類() { ... }
    get元データ() { ... }
}
```

&nbsp;
&nbsp; &nbsp; &nbsp;![why.png](https://qiita-image-store.s3.amazonaws.com/0/106044/54f623e4-b497-1530-e6c9-b7f456c0ec99.png)
&nbsp;

この `ScanRecord` クラスの設計は、Android API の中でも最悪レベルで、罵倒されてもやむをえません。この他、Android の BLE 関連の API は全般的に設計ミスの宝庫です。Android 4.X から 5.X にバージョンアップする際に BLE API を再設計したのにこの低品質で、かつそれが公式 API に採用されてしまったということは、Android の BLE 界隈はかなりの人材不足なのだと推測せざるをえません。


# **3. Android Beacon Library**

## **3.1. Android Beacon Library の紹介**

「アドバータイズメントパケットのペイロードをパースしたい」と、みんな思います。iBeacon や Eddystone のデータをアドバータイズメントパケットから取り出したいのです。独自フォーマットのアドバータイズメントパケットを投げるビーコンを作りたい企業もあるでしょう。

Android BLE API は前述の通り酷いので、ペイロードをパースしたい人は解を求めて Stack Overflow に行きます。するとすぐに **[Android Beacon Library](http://altbeacon.github.io/android-beacon-library/)** というライブラリに辿りつきます。このライブラリはオープンソースで ([AltBeacon/android-beacon-library](https://github.com/AltBeacon/android-beacon-library))、**[Radius Networks](http://www.radiusnetworks.com/)** という会社がサポートしています。Radius Networkds 社の関係者が積極的に回答していることもあり、Stack Overflow は Android Beacon Library の回答で溢れ返っています。

このライブラリにアドバータイズメントパケットをパースさせるには、対象となる AD ストラクチャーのレイアウトをパーサーに登録する必要があります。例えば iBeacon データを取り出すには、パーサーに次のようにレイアウトを登録します。

```java
// iBeacon のレイアウトを登録する。
setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24");
```

レイアウトの書式の説明は、<code>[BeaconParser](https://altbeacon.github.io/android-beacon-library/javadoc/org/altbeacon/beacon/BeaconParser.html)</code> クラスの <code>[setBeaconLayout](https://altbeacon.github.io/android-beacon-library/javadoc/org/altbeacon/beacon/BeaconParser.html#setBeaconLayout%28java.lang.String%29)</code> メソッドの JavaDoc に書かれています。だいたい想像がつく通り、フォーマットとバイト位置の組を列挙しています。

Android Beacon Library を使って Eddystone データを扱う方法は「[How to detect Eddystone-Compatible Beacons](https://altbeacon.github.io/android-beacon-library/eddystone-how-to.html)」に記述されています。例えば Eddystone URL を検出するには、まず、パーサーに Eddystone URL のレイアウトを登録し、

```java
mBeaconManager.getBeaconParsers().add(new BeaconParser().
    setBeaconLayout("s:0-1=feaa,m:2-2=10,p:3-3:-41,i:4-20v"));
```

アドバータイズメントパケットのペイロードのパース結果を得た時に、次のようにして Eddystone URL だということを判定して、情報を取り出します。

```java
for (Beacon beacon: beacons) {
    if (beacon.getServiceUuid() == 0xfeaa && beacon.getBeaconTypeCode() == 0x10) {
        // This is a Eddystone-URL frame
        String url = UrlBeaconUrlCompressor.uncompress(beacon.getId1().toByteArray());
        Log.d(TAG, "I see a beacon transmitting a url: " + url +
                " approximately " + beacon.getDistance() + " meters away.");
    }
}
```

このコードは前出の[ドキュメント](https://altbeacon.github.io/android-beacon-library/eddystone-how-to.html)から転載したものですが、(1) Eddystone のサービス番号が `0xFEAA` ということ、及び (2) Eddystone URL のフレーム種別を示す値が `0x1?` であるということを知らないと、このコードは書けません。なお、正確には

```java
beacon.getBeaconType() == 0x10
```

となっている箇所は

```java
(beacon.getBeaconType() & 0xF0) == 0x10
```

と書くべきです。


## **3.2. Android Beacon Library の問題点**

Android Beacon Library の設計は、AD ストラクチャーのレイアウトを登録するという原始的なものなので、パースされた AD ストラクチャーは全て <code>[Beacon](https://altbeacon.github.io/android-beacon-library/javadoc/org/altbeacon/beacon/Beacon.html)</code> という汎用のデータ構造で返ってきます。逆に言うと、例えば下記のような便利なクラスのインスタンスが返ってくることはありません。

```java
public class EddystoneURL extends Eddystone
{
    /**
     * Eddystone URL の AD ストラクチャーが保持する URL 情報を
     * java.net.URL クラスのインスタンスとして返す。
     */
    public URL getURL() { ... }
    ...
}
```

加えて、AD ストラクチャー内の構造が特殊になると、レイアウト書式では対応しきれなくなります。例えば [Eddystone URL](https://github.com/google/eddystone/blob/master/eddystone-url) のエンコーディング方法は特殊なので、次のように色々複雑なことをやらないと欲しい情報が取り出せません。

```java
String url = UrlBeaconUrlCompressor.uncompress(beacon.getId1().toByteArray());
```

[Eddystone TLM](https://github.com/google/eddystone/blob/master/eddystone-tlm) が持つ温度情報も固定小数点フォーマット ([Cornell University, Electrical Engineering 476, Fixed Point mathematical functions in GCC and assembler](https://courses.cit.cornell.edu/ee476/Math/) 参照) で格納されていて、この形式は Android Beacon Library のレイアウト書式が対応していないものなので、こちらも情報を得るには次のようなコードを書かないといけません。このコードは、Radius Networks 社の関係者による [Stack Overflow の回答](http://stackoverflow.com/a/33393867/1174054)からの転載です。

```java
long unsignedTemp = (beacon.getExtraDataFields().get(2) >> 8);
double temperature = unsignedTemp > 128 ? 
    unsignedTemp - 256 : 
    unsignedTemp +(beacon.getExtraDataFields().get(2) & 0xff)/256.0;
```

もしも Eddystone TLM を、次のような便利なクラスのインスタンスとして受け取ることができるのであれば、(1) 温度情報が固定小数点数として格納されていること、及び (2) 固定小数点数を `float` に直す方法を知らずとも、簡単に温度情報を取り出すことができるでしょう。

```java
public class EddystoneTLM extends Eddystone
{
    /**
     * Eddystone TLM の AD ストラクチャーが保持する、固定小数点
     * フォーマットで表現された温度情報をパースして float として返す。
     */
    public float getBeaconTemperature() { ... }
    ...
}
```

レイアウト登録というアプローチは一見汎用的で良い解のように思えますが、この設計は意外と安っぽいものであり、結局使い勝手の悪さとして跳ね返ってきます。さらに現実のデータ構造を正しく表現しきれないのです。「それでは」と言って使い勝手を求めると、汎用のはずの <code>[Beacon](https://altbeacon.github.io/android-beacon-library/javadoc/org/altbeacon/beacon/Beacon.html)</code> クラスに [mServiceUuid](https://altbeacon.github.io/android-beacon-library/javadoc/org/altbeacon/beacon/Beacon.html#mServiceUuid) や [mTxPower](https://altbeacon.github.io/android-beacon-library/javadoc/org/altbeacon/beacon/Beacon.html#mTxPower) といった、本来サブクラスで持つべきフィールドが追加されていき、設計がどんどん汚くなっていきます。

いちいちレイアウトを登録しなければならない、パース処理の結果得られた <code>[Beacon](https://altbeacon.github.io/android-beacon-library/javadoc/org/altbeacon/beacon/Beacon.html)</code> から取り出したデータをさらに自分でパースしなければならない、という問題点もさることながら、私がそれ以上に **Android Beacon Library の設計上の問題として指摘したいのは、Android Beacon Library では AD ストラクチャーの階層構造を絶対に表現できないという点です。** この問題は、レイアウト登録というアプローチをとっている限り解消できません。


# **4. 繋ぎのソリューション**

将来まともな Android BLE API が再々設計・実装されるまでの繋ぎのソリューションとして、手前味噌ではありますが **[nv-bluetooth](https://github.com/TakahikoKawasaki/nv-bluetooth)** ライブラリをお勧めします。 このライブラリを使うと、先に述べた理想の形でのプログラミングが可能となります。使用方法は下記のリンク先をご参照ください。

- **[GitHub] [nv-bluetooth README](https://github.com/TakahikoKawasaki/nv-bluetooth/blob/master/README.md)**
- **[ブログ] [iBeacon as a kind of AD structures](http://darutk-oboegaki.blogspot.jp/2015/03/ibeacon-as-kind-of-ad-structures.html)**
- **[ブログ] [Eddystone パケットをパースする (Android)](http://darutk-oboegaki.blogspot.jp/2015/08/eddystone-android.html)**

また、下記の Stack Overflow の回答群もご参照ください。 Android Beacon Library を用いる方法での回答が同時についている質問もあるので、比較してみると違いがはっきり分かると思います。

- 「Programmatically, How to identify if a beacon belongs to Eddystone or iBeacon?」に対する[回答](http://stackoverflow.com/a/32794478/1174054)
- 「How to read UDID, Major, Minor of beacon on android devices?」に対する[回答](http://stackoverflow.com/a/29171384/1174054)
- 「How to identify Eddystone URL and uid?」に対する[回答](http://stackoverflow.com/a/32871789/1174054)
- 「How to identify a Eddystone via scanRecord」に対する[回答](http://stackoverflow.com/a/32767712/1174054)
- 「Android Bluetooth Low Energy - iBeacon」に対する[回答](http://stackoverflow.com/a/29174997/1174054)
- 「Finding iBeacon using AltBeacon library」に対する[回答](http://stackoverflow.com/a/29172086/1174054)


# **おわりに**

Android BLE API の設計があまりにも酷いこと、Android Beacon Library が流行ってしまっていることは、Android を用いた BLE ソリューション発展の阻害要因になっていると考えています。 正しい設計・実装ができるエンジニアを集めて新しいチームを作った上で、ネイティブレイヤーから Android BLE API の再設計・再実装がおこなわれることを希望します。