AndoridのBluetooth通信

# 1. GATT

## 1.1 説明

GATT (Generic Attribute Profile) は、BLE (Bluetooth Low Energy) デバイス間でデータをやり取りするためのプロファイルです。

* **GATTサーバー**: データを保持し、クライアントからの要求に応じる役割（Peripheral）
* **GATTクライアント**: データを読み書きする側の役割（Central）
* サービス（Service）とキャラクタリスティック（Characteristic）を中心に通信が行われます。

## 1.2 サンプルコード

* **GATTサーバーのセットアップ例**

  ```kotlin
  val gattServer = bluetoothManager.openGattServer(context, gattServerCallback)

  val service = BluetoothGattService(
      UUID.fromString("0000180D-0000-1000-8000-00805f9b34fb"),
      BluetoothGattService.SERVICE_TYPE_PRIMARY
  )

  val characteristic = BluetoothGattCharacteristic(
      UUID.fromString("00002A37-0000-1000-8000-00805f9b34fb"),
      BluetoothGattCharacteristic.PROPERTY_READ or BluetoothGattCharacteristic.PROPERTY_NOTIFY,
      BluetoothGattCharacteristic.PERMISSION_READ
  )

  service.addCharacteristic(characteristic)
  gattServer.addService(service)
  ```

* **クライアント側の接続例**

  ```kotlin
  val device = bluetoothAdapter.getRemoteDevice(remoteDeviceAddress)
  val gatt = device.connectGatt(context, false, gattCallback)
  ```

## 1.3 Macアドレスが変更される件

* Android 6.0以降、BLEスキャン時に取得できるMacアドレスはプライバシー保護のために一定時間ごとにランダム化される場合があります。
* これにより、スキャン結果のMacアドレスが実際の固定アドレスとは異なることがあります。
* アプリでは、デバイス名やサービスUUIDなど、他の情報も併用して対象デバイスを特定する必要があります。

## 1.4 AdvertiseSettingsに関する注意点

* Peripheralとしてアドバタイズする際、`AdvertiseSettings` で動作モードや電力レベルなどを調整できます。
* 高速接続を優先する場合は `ADVERTISE_MODE_LOW_LATENCY` を使用しますが、バッテリー消費が増えます。
* 通常はバランス型の `ADVERTISE_MODE_BALANCED` が推奨されます。

  ```kotlin
  val settings = AdvertiseSettings.Builder()
      .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
      .setConnectable(true)
      .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
      .build()
  ```

## 1.5 serviceUuidとcharacteristicUuid

* BLE通信ではサービスとCharacteristicはUUIDで識別されます。
* **serviceUuid**: サービス単位の識別子
* **characteristicUuid**: サービス内の具体的なデータ項目を示す識別子
* Androidでは `UUID.fromString()` を用いて文字列からUUIDを生成します。

以下は、例として「Heart Rate Service（心拍数サービス）」のUUIDです。

* **Heart Rate Service UUID**
  `0000180D-0000-1000-8000-00805f9b34fb`

* **Heart Rate Measurement Characteristic UUID**
  `00002A37-0000-1000-8000-00805f9b34fb`

```kotlin
val serviceUuid = UUID.fromString("0000180D-0000-1000-8000-00805f9b34fb")
val characteristicUuid = UUID.fromString("00002A37-0000-1000-8000-00805f9b34fb")
```

# 2. RFCOMM ServerとClient

## 2.1 説明

RFCOMMは、Bluetoothで「シリアルポートエミュレーション」を提供するプロトコルです。

* **SPP（Serial Port Profile）** とも呼ばれ、従来の有線シリアル通信の置き換えとして利用されます。
* GATTよりも単純な構造で、接続は「ソケット通信」を利用します。
* SPPに対応したデバイス同士でデータの送受信を行う用途に向いています。

## 2.2 サンプルコード

* **RFCOMMサーバーのセットアップ例**

  ```kotlin
  val serverSocket = bluetoothAdapter.listenUsingRfcommWithServiceRecord(
      "MyAppService",
      UUID.fromString("00001101-0000-1000-8000-00805F9B34FB") // SPPの一般的なUUID
  )

  val socket = serverSocket.accept() // クライアントからの接続を待機
  val inputStream = socket.inputStream
  val outputStream = socket.outputStream
  ```

* **クライアント側の接続例**

  ```kotlin
  val device = bluetoothAdapter.getRemoteDevice(remoteDeviceAddress)
  val socket = device.createRfcommSocketToServiceRecord(
      UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
  )
  socket.connect()
  val inputStream = socket.inputStream
  val outputStream = socket.outputStream
  ```

## 2.3 注意点

* SPPは古い仕様ですが、通信が常時接続型になるためバッテリー消費は大きくなる傾向があります。
* サーバーとクライアントが同じUUIDを使わないと接続ができません。通常はSPPの標準UUID (`00001101-0000-1000-8000-00805F9B34FB`) が利用されますが、アプリ専用のUUIDを使うことも可能です。
* 接続の信頼性（接続失敗・切断など）のために、再接続処理を適切に実装することが重要です。
* 使用後は必ず socket.close() を呼び出して、リソースを解放することを忘れないようにしましょう。

# 3. その他の大事なこと

## 3.1 BluetoothAdapterの取得

* Bluetooth接続には、まず `BluetoothAdapter` を取得する必要があります。
* `BluetoothManager` から取得する方法が一般的です。

```kotlin
val bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
val bluetoothAdapter = bluetoothManager.adapter
```

* 端末のBluetooth機能が有効か確認するには、以下を使用します。

```kotlin
if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled) {
    // Bluetooth未対応または無効
}
```

## 3.2 パーミッション

* Android 11以下: `BLUETOOTH`, `BLUETOOTH_ADMIN`, `ACCESS_FINE_LOCATION`（スキャン時）
* Android 12以降: `BLUETOOTH_CONNECT`, `BLUETOOTH_SCAN`, `BLUETOOTH_ADVERTISE`（役割に応じて必要）
* Android 12以降では、Manifestに加えてRuntime Permissionのリクエストも必要です。

```kotlin
if (ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
    // Runtime Permissionをリクエストする
}
```

## 3.3 注意点

* BLE/GATTとRFCOMM/SPPは通信方式が異なりますが、BluetoothAdapterやパーミッション周りは共通です。
* socketやGATT接続後は必ず close() を呼び出し、リソース解放を忘れないようにしましょう。

# 4. 接続前の検証と注意点

* BLE/GATTやRFCOMMの接続コードをテストする際は、**必ずデバイスが接続されていない状態で行う**ことを確認しましょう。
* 既に接続済みの状態で同じ接続処理を行うと、APIの戻り値が意図しない動作を示すことがあります。
* 特に、再接続時やスキャン開始前には `close()` などでリソースを解放し、クリーンな状態に戻してから実行することが重要です。

# まとめ
ちなみに、Bluetoothって「青い歯」って意味ですけど、もしBluetoothの接続に失敗しても、歯医者には行かないでくださいね！