Androidからイヤフォンやヘルス機器とBluetooth通信するにはAndroidで動く携帯Javaアプリ作成入門(36)

ヘッドフォンの再生状態の確認やヘッドセットからの音声の認識、ヘルス機器との通信などのやり方をサンプルを交えて解説します

» 2012年10月17日 18時00分 公開
[緒方聡イーフロー]

BluetoothProfileを使用したデバイスの操作方法

 前回の「Bluetoothを使ってAndroidアプリ同士で通信するには」では、Androidで使えるBluetoothの種類や設定の仕方、ペアリングや通信の行い方などを通信対戦ゲームのサンプルを交えて解説しました。今回はBluetoothを使用してデバイスに接続し、デバイスを操作する方法を解説します。

 Android API Level 16では、デバイスとの接続のために以下の定数とクラスが用意されています。

定数 クラス
BluetoothProfile.A2DP BluetoothA2dp
BluetoothProfile.HEADSET BluetoothHeadset
BluetoothProfile.HEALTH BluetoothHelth

 上記のクラスは、すべてBluetoothProfileインターフェイスを実装していて、以下のメソッドが実装されています。

  • getConnectedDevices(): List
    このプロファイルで接続されているデバイスをリストで取得
  • etConnectedState(BluetoothDevice): int
    デバイスの接続状態をSTATE_CONNECTED、STATE_CONNECTING、STATE_DISCONNECTED、STATE_DISCONNECTINGのいずれかで取得
  • getDeviceMatchingConnectionStates(int[]): List
    配列で指定した接続状態(STATE_CONNECTED、STATE_CONNECTING、STATE_DISCONNECTED、STATE_DISCONNECTING)にマッチするデバイスをリストで取得

 すべての専用クラスはBluetoothProfileインターフェイスを備えており、同様の操作を提供するとともに、同様の方法で取得も可能です。

// リスナを定義
ServiceListener listener = new ServiceListener() {
    @Override
    public void onServiceConnected(int profile, BluetoothProfile proxy) {
        // それぞれの型にキャスト
        if (profile == BluetoothProfile.HEADSET) {
            mBluetoothHeadset = (BluetoothHeadset) proxy;
        } else if (profile == BluetoothProfile.A2DP) {
            mBluetoothA2dp = (BluetoothA2dp) proxy;
        } else if (profile == BluetoothProfile.HEALTH) {
            mBluetoothHealth= (BluetoothHealth) proxy;
        }
    }
    @Override
    public void onServiceDisconnected(int profile) {
        // それぞれのフィールドの参照を削除
        if (profile == BluetoothProfile.HEADSET) {
            mBluetoothHeadset = null;
        } else if (profile == BluetoothProfile.A2DP) {
            mBluetoothA2dp = null;
        } else if (profile == BluetoothProfile.HEALTH) {
            mBluetoothHealth = null;
        }
    }
};
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
// それぞれのプリファイルでリスナが呼び出されるように登録
adapter.getProfileProxy(this, listener, BluetoothProfile.HEADSET);
adapter.getProfileProxy(this, listener, BluetoothProfile.A2DP);
adapter.getProfileProxy(this, listener, BluetoothProfile.HEALTH);

 今回もサンプルアプリを用意したので、詳細なコードとその動作は、以下よりダウンロードして確認してください。

 以下は、それぞれの専用クラスで提供する機能の説明です。

イヤフォン/ヘッドフォンなどの再生状態を確認する「BluetoothA2dp」

 前回も説明しましたが、A2DPとは、「Advanced Audio Distribution Profile」の略で、ステレオ音質のオーディオ配信を行います。Androidとイヤフォン/ヘッドフォンなど音を聞くデバイスとの間の通信プロファイルです。BluetotohA2dpは以下のメソッドを提供します。

  • isA2dpPlaying(BluetoohDevice): boolean
    デバイスが再生中かどうかを返します。

 また、ブロードキャストレシーバでIntentを取得することで、接続状態変化、再生状態変化をリアルタイムに取得可能です。

 以下はIntentFilter設定の抜粋です。

IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
filter.addAction(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED);
registerReceiver(mReceiver, filter);

 以下は、BroadcastReceiver実装の抜粋です。

String action = intent.getAction();
Bundle extras = intent.getExtras();
int state = extras.getInt(BluetoothProfile.EXTRA_STATE);
int prevState = extras.getInt(BluetoothProfile.EXTRA_PREVIOUS_STATE);
BluetoothDevice device = extras.getParcelable(BluetoothDevice.EXTRA_DEVICE);
if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
    String stateStr = state == BluetoothProfile.STATE_CONNECTED ? "CONNECTED"
        : state == BluetoothProfile.STATE_CONNECTING ? "CONNECTING"
        : state == BluetoothProfile.STATE_DISCONNECTED ? "DISCONNECTED"
        : state == BluetoothProfile.STATE_DISCONNECTING ? "DISCONNECTING"
        : "Unknown";
    String prevStateStr = prevState == BluetoothProfile.STATE_CONNECTED ? "CONNECTED"
        : prevState == BluetoothProfile.STATE_CONNECTING ? "CONNECTING"
        : prevState == BluetoothProfile.STATE_DISCONNECTED ? "DISCONNECTED"
        : prevState == BluetoothProfile.STATE_DISCONNECTING ? "DISCONNECTING"
        : "Unknown";
    Log.d(TAG, "BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED:"
        + " EXTRA_DEVICE=" + device.getName()
        + " EXTRA_STATE=" + stateStr
        + " EXTRA_PREVIOUS_STATE=" + prevStateStr);
} else if (action.equals(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED)) {
    String stateStr = state == BluetoothA2dp.STATE_NOT_PLAYING ? "NOT_PLAYING"
        : state == BluetoothA2dp.STATE_PLAYING ? "PLAYING"
        : "Unknown";
    String prevStateStr = prevState == BluetoothA2dp.STATE_NOT_PLAYING ? "NOT_PLAYING"
        : prevState == BluetoothA2dp.STATE_PLAYING ? "PLAYING"
        : "Unknown";
    Log.d(TAG, "BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED:"
        + "EXTRA_DEVICE=" + device.getName()
        + " EXTRA_STATE=" + stateStr
        + " EXTRA_PREVIOUS_STATE=" + prevStateStr);
}

 BroadcastReceiverで状態を取得する際に、接続状態(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)、再生状態(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED)どちらに対してもBluetoothProfile.EXTRA_STATEとBluetoothProfile.EXTRA_PREVIOUS_STATEで現在の状態と直前の状態を取得しますが、それぞれで値の意味が異なるということに注意してください。

ヘッドセットの音声認識を行う「BluetoothHeadset」

 BluetoothHeadsetは以下のメソッドを提供します。

  • isAudioConnected(BluetoohDevice): boolean
    デバイスが接続中かどうかを返す
  • startVoiceRecognition(BluetoohDevice): boolean
    音声認識を開始します。デバイスが接続されていないか音声認識をサポートしていなかエラーが発生した場合はfalse、そうでない場合はtrueを返す
  • stopVoiceRecognition(BluetoohDevice): boolean
    音声認識を終了します。デバイスが接続されていないかエラーが発生した場合はfalse、そうでない場合はtrueを返す

 HeadsetもA2DPと同様、ブロードキャストレシーバでIntentを取得することで、接続状態変化、音声状態変化をリアルタイムに取得することも可能です(詳細はサンプルアプリのソースコードを参照ください)。

 また、Headset固有のIntentとして、ベンダ独自のコマンドも取得可能です。ただし、ベンダ独自なので、取得したコマンドがどのような意味を持つのかは機器の仕様書がなければ知りえないため、一般の開発者向けの機能ではありません。

 BluetoothHeadset向けの機能をアプリに実装する際には、スマホのSIMを抜いておくことをお勧めします。ヘッドセットの操作や音声認識で、思わぬ電話発信が行われる可能性があります。

 筆者は真夜中にアドレス帳内の番号に何度か電話発信をしてしまいました。皆さんはお気を付けください。

ヘルス機器と通信する「BluetoothHealth」

 Healthは以下のメソッドを提供します。

  • connectChannelToSource(BluetoothDevice, BluetoothHealthApConfiguration): boolean
    ヘルス機器に接続する。このメソッドがtrueを返した場合、コンフィギュレーションに関連付けされたコールバックが呼び出される
  • disconnectChannel(BluetoothDevice, BluetoothHealthApConfiguration, int): boolean
    ヘルス機器から切断する。このメソッドがtrueを返した場合、コンフィギュレーションに関連付けされたコールバックが呼び出される
  • getMainChannelFd(BluetoothDevice, BluetoothHealthApConfiguration): ParcelFileDescriptor
    ヘルス機器とコンフィギュレーションに関連付けされたファイルディスクリプタを取得
  • registerSinkAppConfiguration(String, int, BluetoothHealthCallback): boolean
    ヘルス受信機器として機能するアプリケーションコンフィギュレーションを登録する。このメソッドがtrueを返した場合、コンフィギュレーションに関連付けされたコールバックが呼び出される
  • unregisterAppConfiguration(BluetoothHealthAppConfiguration): boolean
    アプリケーションコンフィギュレーションを登録解除。このメソッドがtrueを返した場合、コンフィギュレーションに関連付けされたコールバックが呼び出される

 AndroidがAPIを提供することで、ヘルス機器とは接続してデータの取得までは簡単に行えますが、実際に取得したデータを解析するには、IEEE 11073の仕様に従った解析処理を実装する必要があります。

 例えば、心拍計ならIEEE 11073-10407、歩数計ならIEEE 11073-10441という具合です。本連載でIEEE 11073の内容まで扱うのは範囲を大きく超えてしまうため、BluetoothHealthに関しては、今回はサンプルアプリへの実装は省略させていただきました。

 同様にIEEE 11073の実装は省略されていますが、Andoird SDKに「BluetoothHDP」という名前でHealth機器からデータを読み出すサンプルが含まれているので、必要な方はそちらを参照してみてください。

AndroidでBluetoothを使う際の制限

 Androidでサポートされているプロファイルを使用して、高レベルなAPIを使用してデバイスの情報を取得できることが分かりました。ただし、制限も存在します。

ペアリングを行うには

 まず、ペアリングを行う仕組みが提供されていません。もし、本格的なBluetooth管理アプリを作成したとしても、ペアリングはAndroid標準の設定アプリに頼らざるを得ません。特定のアクションを使用することで、Bluetooth設定画面を開けるので、アプリからペアリングを行ってもらいたい場合は、以下のコードを実行してください。

Intent intent = new Intent();
intent.setAction(android.provider.Settings.ACTION_BLUETOOTH_SETTINGS);
startActivity(intent);

デバイスを操作するには

 次に、デバイスを操作する仕組みもほとんど提供されていません。A2DPなら状態取得が提供されるのみで、ヘッドセットは状態取得に加え音声認識が提供されます。しかし、音声認識はユーザーの音声入力が必要であるため、直接操作ではなく、ヘルス機器からも情報取得しかできません。

 HDPはAndroid APIとして簡単にヘルス機器のデータを取得可能ですが、実際にはIEEE 11073-104xxのデータ解析処理の実装がヘルス機器種別ごとに必要で、これはかなりハードルが高いです。Google PlayでHDPに対応したアプリは現段階で数えるほどしかありません。

「Androidで動く携帯Javaアプリ作成入門」バックナンバー

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。