static DBusHandlerResult bluez_change(DBusConnection *c, DBusMessage *msg, void *data) { char *name, *before, *after; (void)c; (void)data; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &before, DBUS_TYPE_STRING, &after, DBUS_TYPE_INVALID) || strcmp(name, BLUEZ_DBUS_NAME)) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; if (!after[0]) { /* bluez gone */ OHM_INFO("BlueZ is down."); bt_delete_all_facts(); dres_all(); } else OHM_INFO("BlueZ is up."); return DBUS_HANDLER_RESULT_HANDLED; }
gboolean bluetooth_deinit(OhmPlugin *plugin) { (void) plugin; if (sys_conn) { watch_dbus_addr(BLUEZ_DBUS_NAME, FALSE, bluez_change, NULL); dbus_connection_unref(sys_conn); sys_conn = NULL; } if (bt_delete_all_facts()) dres_all(); return TRUE; }
DBusHandlerResult bt_device_removed(DBusConnection *c, DBusMessage * msg, void *data) { /* This is called apparently anytime a device does not tell that it * has been removed itself. We somehow need to ensure that this * device actually is a HSP or A2DP device. */ OhmFactStore *fs = ohm_fact_store_get_fact_store(); gchar *path = NULL; (void) data; (void) c; if (!msg) goto end; if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) { OhmFact *bt_connected = bt_get_connected(path); if (bt_connected) { gboolean disconnect_a2dp = disconnect_device(bt_connected, BT_TYPE_A2DP); gboolean disconnect_hsp = disconnect_device(bt_connected, BT_TYPE_HSP); ohm_fact_store_remove(fs, bt_connected); g_object_unref(bt_connected); if (disconnect_a2dp || disconnect_hsp) dres_all(); } /* else a bt device disconnected but there were no known bt headsets * connected, just disregard */ } end: return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; }
static DBusHandlerResult info(DBusConnection *c, DBusMessage * msg, void *data) { int driver = -1; int connected = -1; int *valueptr = &driver; int value = -1; DBusMessageIter msgit; DBusMessageIter devit; char *string; char *end; char *device; gboolean is_info; (void) c; (void) data; /* This is an example of what we should get: string "connected" string "1" array [ string "fmtx" ] */ is_info = dbus_message_is_signal(msg, "com.nokia.policy", "info"); if (!is_info) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; OHM_DEBUG(DBG_INFO, "received an info message"); dbus_message_iter_init(msg, &msgit); for (;;) { if (dbus_message_iter_get_arg_type(&msgit) != DBUS_TYPE_STRING) goto done; dbus_message_iter_get_basic(&msgit, (void *)&string); if (!strcmp(string, "media")) goto not_our_signal; if (!strcmp(string, "driver")) { valueptr = &driver; if (!dbus_message_iter_next(&msgit)) goto done; } else if (!strcmp(string, "connected")) { valueptr = &connected; if (!dbus_message_iter_next(&msgit)) goto done; } else { value = strtol(string, &end, 10); if (*end == '\0' && (value == 0 || value == 1)) { *valueptr = value; break; } goto done; } } if (!dbus_message_iter_next(&msgit) || dbus_message_iter_get_arg_type(&msgit) != DBUS_TYPE_ARRAY) goto done; dbus_message_iter_recurse(&msgit, &devit); do { if (dbus_message_iter_get_arg_type(&devit) != DBUS_TYPE_STRING) continue; dbus_message_iter_get_basic(&devit, (void *)&device); OHM_DEBUG(DBG_INFO, "device: '%s', driver: '%d', connected: '%d'", device ? device : "NULL", driver, connected); if (!is_spurious_event(device, driver, connected)) dres_accessory_request(device, driver, connected); } while (dbus_message_iter_next(&devit)); dres_all(); done: not_our_signal: return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; }
static void get_properties_update_fact_cb (DBusPendingCall *pending, void *user_data) { DBusMessage *reply = NULL; DBusMessageIter iter, array_iter, dict_iter, variant_iter, uuid_iter; gchar **dbus_data = user_data; gchar *path = dbus_data[0]; gchar *interface = dbus_data[1]; gboolean is_hfp = FALSE, is_hsp = FALSE; OhmFact *bt_connected = NULL; g_free(dbus_data); if (pending == NULL) goto error; reply = dbus_pending_call_steal_reply(pending); dbus_pending_call_unref(pending); pending = NULL; if (reply == NULL) { goto error; } if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) { goto error; } dbus_message_iter_init(reply, &iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { goto error; } dbus_message_iter_recurse(&iter, &array_iter); while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_DICT_ENTRY) { /* the arg type will be DBUS_TYPE_INVALID at the end of the * array */ gchar *key = NULL; int type; /* process the dicts */ dbus_message_iter_recurse(&array_iter, &dict_iter); /* key must be string */ if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) { goto error; } dbus_message_iter_get_basic(&dict_iter, &key); /* go on to the value */ dbus_message_iter_next(&dict_iter); dbus_message_iter_recurse(&dict_iter, &variant_iter); type = dbus_message_iter_get_arg_type(&variant_iter); if (strcmp(key, "UUIDs") == 0) { if (type == DBUS_TYPE_ARRAY) { dbus_message_iter_recurse(&variant_iter, &uuid_iter); while (dbus_message_iter_get_arg_type(&uuid_iter) == DBUS_TYPE_STRING) { gchar *uuid = NULL; dbus_message_iter_get_basic(&uuid_iter, &uuid); if (!uuid) break; else if (strcmp(uuid, HFP_UUID) == 0) { is_hfp = TRUE; } else if (strcmp(uuid, HSP_UUID) == 0) { is_hsp = TRUE; } dbus_message_iter_next(&uuid_iter); } } else { OHM_DEBUG(DBG_BT, "Error: type '%u'\n", dbus_message_iter_get_arg_type(&dict_iter)); } } dbus_message_iter_next(&array_iter); } /* get the BT fact */ OHM_DEBUG(DBG_BT, "Device %s %s HFP support", path, is_hfp ? "has" : "has not"); OHM_DEBUG(DBG_BT, "Device %s %s HSP support", path, is_hsp ? "has" : "has not"); if ((bt_connected = bt_get_connected(path)) != NULL) { GValue *gval_state = ohm_fact_get(bt_connected, BT_TYPE_HSP); GValue *gval_prev_state = ohm_fact_get(bt_connected, "bthsp_prev_state"); const gchar *state = NULL, *prev_state = NULL; gboolean run_dres; define_hfp_status(bt_connected, is_hfp); define_hsp_status(bt_connected, is_hsp); if (gval_state != NULL && G_VALUE_TYPE(gval_state) == G_TYPE_STRING) { state = g_value_get_string(gval_state); } if (gval_prev_state != NULL && G_VALUE_TYPE(gval_prev_state) == G_TYPE_STRING) { prev_state = g_value_get_string(gval_prev_state); } OHM_DEBUG(DBG_BT, "running state transition from %s to %s from HFP/HSP status cb", prev_state ? prev_state : "NULL", state ? state : "NULL"); run_dres = bt_state_transition(BT_TYPE_HSP, path, map_to_state(prev_state), map_to_state(state)); dres_all(); } error: if (reply) dbus_message_unref (reply); g_free(path); g_free(interface); return; }
static gboolean bt_state_changed(const gchar *type, const gchar *path, const gchar *state) { OhmFactStore *fs = ohm_fact_store_get_fact_store(); gchar *prev_state = NULL; OhmFact *bt_connected = bt_get_connected(path); gboolean run_dres = FALSE; GValue *gval_state; gboolean bt_audio_connected = FALSE; //OHM_DEBUG(DBG_BT, "type: %s, state: %s", type, state); if (strcmp(type, BT_TYPE_AUDIO) == 0) { if (bt_connected) { gval_state = ohm_value_from_string(state); ohm_fact_set(bt_connected, type, gval_state); } if (strcmp(state, BT_STATE_CONNECTED_S) == 0) { /* Get a2dp and hsp status if AudioSink or Headset changed to * connected or playing before Audio state changed to connected. */ get_properties(path, BT_INTERFACE_A2DP, get_properties_cb); get_properties(path, BT_INTERFACE_HSP, get_properties_cb); } return TRUE; } if(bt_connected) { gval_state = ohm_fact_get(bt_connected, BT_TYPE_AUDIO); if (gval_state != NULL && G_VALUE_TYPE(gval_state) == G_TYPE_STRING) { bt_audio_connected = strcmp(g_value_get_string(gval_state), BT_STATE_CONNECTED_S) == 0 ? TRUE : FALSE; } } /* In pulseaudio module-bluetooth-device is loaded after BT Audio interface * gets connected. Need to wait until then to be able to route audio. */ if ((strcmp(state, BT_STATE_CONNECTED_S) == 0 || strcmp(state, BT_STATE_PLAYING_S) == 0) && !bt_audio_connected) { OHM_DEBUG(DBG_BT, "type: %s, state: %s transition not allowed.", type, state); return TRUE; } /* Type is either HSP or A2DP. HFP is distinguished from HSW by a * flag in the BT fact. */ if (!bt_connected) { GValue *gval = NULL; /* first time: create a new fact */ /* TODO: check that this doesn't leak memory! */ bt_connected = ohm_fact_new(BT_DEVICE); /* TODO: set the bthsp and bta2dp fields to "na" or "unknown" * values */ if (bt_connected == NULL) { OHM_DEBUG(DBG_BT, "could not create the BT fact!"); goto error; } else { /* add the object path to the bluetooth fact in order to * remember the device */ gval = ohm_value_from_string(path); ohm_fact_set(bt_connected, "bt_path", gval); ohm_fact_store_insert(fs, bt_connected); } } else { gval_state = ohm_fact_get(bt_connected, type); if (gval_state != NULL && G_VALUE_TYPE(gval_state) == G_TYPE_STRING) { /* copy the value so that we can overwrite the one in the * fact */ prev_state = g_strdup(g_value_get_string(gval_state)); } } OHM_DEBUG(DBG_BT, "type: %s, prev_state: %s, state: %s", type, prev_state ? prev_state : "NULL", state); gval_state = ohm_value_from_string(state); ohm_fact_set(bt_connected, type, gval_state); if (strcmp(type, BT_TYPE_HSP) == 0) { /* check if we already have the information about the accurate * mono profile status */ if (!hfp_status_defined(bt_connected) || !hsp_status_defined(bt_connected)) { /* We don't know the HFP or HSP status yet. Process the dres * only after we know the status. */ OHM_DEBUG(DBG_BT, "querying HFP/HSP state for device %s", path); if (prev_state) { GValue *gval_prev_state = ohm_value_from_string(prev_state); ohm_fact_set(bt_connected, "bthsp_prev_state", gval_prev_state); } if (get_properties(path, BT_INTERFACE_DEVICE, get_properties_update_fact_cb)) { /* continue processing in the callback */ goto end; } } } OHM_DEBUG(DBG_BT, "running state transition from %s to %s from BT status_changed cb", prev_state ? prev_state : "NULL", state ? state : "NULL"); if (prev_state && state && strcmp(prev_state, BT_STATE_CONNECTING_S) == 0 && strcmp(state, BT_STATE_PLAYING_S) == 0) { /* When state transition is not allowed state might change to playing. * In this case state change is from connecting to playing, and connected state * transition is not done. We need manually do it first */ run_dres = bt_state_transition(type, path, map_to_state(prev_state), map_to_state(BT_STATE_CONNECTED_S)); if (run_dres) dres_all(); } run_dres = bt_state_transition(type, path, map_to_state(prev_state), map_to_state(state)); if (run_dres) dres_all(); end: g_free(prev_state); return TRUE; error: return FALSE; }