Initially, I had to invoke a Bluez method that needs a dictionary as parameter. But how could I do it? It not easy at all to find a detailed documentation about it and I had to look for a solution at BlueZ source code.
In this case, I'm using the newest BlueZ Health API (support for HDP/MCAP). The following piece of code shows
static char *start_health_session(DBusConnection *conn)
{
  DBusMessage *msg, *reply;
  DBusMessageIter args;
  DBusError err;
  const char *reply_path;
  char *path;
  msg = dbus_message_new_method_call("org.bluez", 
                                     "/org/bluez", 
                                     "org.bluez.HealthManager",
                                     "CreateApplication");
  if (!msg) {
      printf(" network:dbus Can't allocate new method call\n");
      return NULL;
  }
  // append arguments
  dbus_message_iter_init_append(msg, &args);
  if ( !iter_append_dictionary(&args, DATA_TYPE_VALUE, 
                                          ROLE_VALUE,
                                          DESCRIPTION_VALUE, 
                                          CHANNEL_TYPE_VALUE) ) {
      printf(" network:dbus Can't append parameters\n");
      dbus_message_unref(msg);
      return NULL;
  }
  dbus_error_init(&err);
....
}
A DBus dict type needs a message iterator, which is properly initialised before it is used.
Once the message iterator is properly created, let's open it and add tuples
static int iter_append_dictionary(DBusMessageIter *iter, 
                                  dbus_uint16_t dataType,
                                  const char *role,
                                  const char *description,
                                  const char *channelType)
{
  DBusMessageIter dict;
  dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
            DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
            DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
            DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
  dict_append_entry(&dict, "DataType", DBUS_TYPE_UINT16, &dataType);
  dict_append_entry(&dict, "Role", DBUS_TYPE_STRING, &role);
  dict_append_entry(&dict, "Description", DBUS_TYPE_STRING, &description);
  dict_append_entry(&dict, "ChannelType", DBUS_TYPE_STRING, &channelType);
  dbus_message_iter_close_container(iter, &dict);
}
At first, you have to open the container and specify the data type of each tuple. In this case, the dictionary consists of tuples <"DataType",uint16>, <"Role",string>, <"Description",string>, and <"ChannelType",string>. Once the value data type for each tuple varies (uint16 or string), we declare it as a variant. Therefore, the dictionary data type definition is:
DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
DBUS_DICT_ENTRY_END_CHAR_AS_STRING
Finally, you simply add the basic data type to message iterator (the dictionary itself).
static void append_variant(DBusMessageIter *iter, int type, void *val)
{
  DBusMessageIter value;
  char sig[2] = { type, '\0' };
  dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, sig, &value);
  dbus_message_iter_append_basic(&value, type, val);
  dbus_message_iter_close_container(iter, &value);
}
static void dict_append_entry(DBusMessageIter *dict,
   const char *key, int type, void *val)
{
  DBusMessageIter entry;
  if (type == DBUS_TYPE_STRING) {
    const char *str = *((const char **) val);
    if (str == NULL)
      return;
  }
  dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
                                   NULL, &entry);
  dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
  append_variant(&entry, type, val);
  dbus_message_iter_close_container(dict, &entry);
}
1 comment:
Thanks, still useful!
Post a Comment