Linux
Dbus
Bluez

send-dbus で bluez とおしゃべり

More than 1 year has passed since last update.

Linux の Bluetooth ドライバ bluez には、色々なツールが付属しているが、対話的に使う正式な方法は bluetoothctl コマンドで、プログラム的には bluetoothd の提供する DBus API を使うのが正しい方法らしい。なので BT の動作を確認するために send-dbus を使ってみた。

まず、BT Adapter があるかどうか調べる。dbus-send がターミナルで dbus と対話するコマンドだが、オプションの意味は以下の通り。

  • --print-reply: 応答を待って返す。=literal を付けると応答をそのまま表示するので整形したりするのに便利。--type=method_call が指定されたとみなされる。
  • --system: DBus には全ユーザ共通の system bus と各ユーザごとの session bus という二つのバスがある。bluez は system bus しかないので --system を指定する。
  • --dest=org.bluez: 接続先
  • /org/bluez: オブジェクトパス
  • org.freedesktop.DBus.Introspectable.Introspect: インタフェース.メンバ (メソッドやシグナルの名前)
  • | xmllint --format -: 読みやすく整形します。

オブジェクト指向的に考えると、接続先とオブジェクトパスの組み合わせでレシーバを指定して、インタフェース.メンバがメッセージという事になる。

# dbus-send --print-reply=literal --system --dest=org.bluez /org/bluez org.freedesktop.DBus.Introspectable.Introspect | xmllint --format -
<?xml version="1.0"?>
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
  <interface name="org.freedesktop.DBus.Introspectable">
    <method name="Introspect">
      <arg name="xml" type="s" direction="out"/>
    </method>
  </interface>
  <interface name="org.bluez.AgentManager1">
    <method name="RegisterAgent">
      <arg name="agent" type="o" direction="in"/>
      <arg name="capability" type="s" direction="in"/>
    </method>
    <method name="UnregisterAgent">
      <arg name="agent" type="o" direction="in"/>
    </method>
    <method name="RequestDefaultAgent">
      <arg name="agent" type="o" direction="in"/>
    </method>
  </interface>
  <interface name="org.bluez.ProfileManager1">
    <method name="RegisterProfile">
      <arg name="profile" type="o" direction="in"/>
      <arg name="UUID" type="s" direction="in"/>
      <arg name="options" type="a{sv}" direction="in"/>
    </method>
    <method name="UnregisterProfile">
      <arg name="profile" type="o" direction="in"/>
    </method>
  </interface>
  <node name="hci0"/>
</node>

最後の <node name="hci0"/> が BT Adapter を表す。

ここで使ったメソッド org.freedesktop.DBus.Introspectable.Introspect は DBus で共通に使われるメソッドで、オブジェクトパスの内容を調べるのに使う。この応答を見ると、/org/bluez/ では org.bluez.AgentManager1 などのインタフェースが使えたり、子要素 hci0 がある事が分かる。API については bluez のドキュメントに解説がある。例えば org.bluez.AgentManager1 なら agent-api.txt に書いてある。

次に、ペアリングされたデバイスを探す。device-api.txt によると、デバイスを表すオブジェクトは /org/bluez/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX に出てくるので /org/bluez/hci0 を Introspect する。

# dbus-send --print-reply=literal --system --dest=org.bluez /org/bluez/hci0 org.freedesktop.DBus.Introspectable.Introspect | xmllint --format -
...
  <node name="dev_B8_8A_60_E6_20_61"/>
  <node name="dev_D4_0B_1A_F2_FE_DC"/>
</node>

ここで表示されるデバイスのうち、ペアリングされているかどうかを示す情報は Properties として実装されている

boolean Paired [readonly]

ので、とりあえず全部のプロパティを表示する org.freedesktop.DBus.Properties.GetAll を使う。

# dbus-send --print-reply=literal --system --dest=org.bluez /org/bluez/hci0/dev_D4_0B_1A_F2_FE_DC org.freedesktop.DBus.Properties.GetAll string:org.bluez.Device1
   array [
      dict entry(
         Address         variant             D4:0B:1A:F2:FE:DC      )
      dict entry(
         Name         variant             M00213      )
      dict entry(
         Alias         variant             M00213      )
      dict entry(
         Class         variant             uint32 5898764
      )
      dict entry(
         Icon         variant             phone      )
      dict entry(
         Paired         variant             boolean true
      )
...

これで、このデバイスが接続中である事が分かる。DBus.Properties.html によると、GetAll を使うには引数としてインターフェース名を指定する。Introspect すると分かるが、dev_XX_XX_XX_XX_XX_XX で使えるインタフェースには org.freedesktop.DBus.Introspectable、org.bluez.Device1、org.freedesktop.DBus.Properties、org.bluez.Network1、org.bluez.MediaControl1 があってそれぞれにプロパティがあるので、必要なインタフェースを指定しなくてはならない。

ちなみに dbus-send の引数を指定する時は string:org.bluez.Device1 のように型を指定する。

実際のプログラムでは GetAll では無く freedesktop.DBus.Properties.Get でプロパティの一つを取ってくる機会が多いと思う。

# dbus-send --print-reply=literal --system --dest=org.bluez /org/bluez/hci0/dev_D4_0B_1A_F2_FE_DC org.freedesktop.DBus.Properties.Get string:org.bluez.Device1 string:Paired
   variant       boolean true

参考