diff options
-rw-r--r-- | hcid/dbus-adapter.c | 9 | ||||
-rw-r--r-- | hcid/dbus-sdp.c | 180 | ||||
-rw-r--r-- | hcid/dbus-sdp.h | 2 |
3 files changed, 189 insertions, 2 deletions
diff --git a/hcid/dbus-adapter.c b/hcid/dbus-adapter.c index 0f6d680a..b32569f6 100644 --- a/hcid/dbus-adapter.c +++ b/hcid/dbus-adapter.c @@ -1265,6 +1265,13 @@ static DBusHandlerResult adapter_get_remote_svc_handles(DBusConnection *conn, return get_remote_svc_handles(conn, msg, data); } +static DBusHandlerResult adapter_get_remote_svc_identifiers(DBusConnection *conn, + DBusMessage *msg, + void *data) +{ + return get_remote_svc_identifiers(conn, msg, data); +} + static DBusHandlerResult adapter_finish_sdp_transact(DBusConnection *conn, DBusMessage *msg, void *data) @@ -3138,6 +3145,8 @@ static DBusMethodVTable adapter_methods[] = { "su", "s" }, { "GetRemoteServiceHandles", adapter_get_remote_svc_handles, "ss", "au" }, + { "GetRemoteServiceIdentifiers", adapter_get_remote_svc_identifiers, + "s", "as" }, { "FinishRemoteServiceTransaction", adapter_finish_sdp_transact, "s", "" }, diff --git a/hcid/dbus-sdp.c b/hcid/dbus-sdp.c index d6dd16ef..8fb8dbf6 100644 --- a/hcid/dbus-sdp.c +++ b/hcid/dbus-sdp.c @@ -676,7 +676,7 @@ static void remote_svc_handles_completed_cb(uint8_t type, uint16_t err, reply = dbus_message_new_method_return(ctxt->rq); dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_TYPE_UINT32_AS_STRING, &array_iter); + DBUS_TYPE_UINT32_AS_STRING, &array_iter); pdata = rsp; @@ -697,7 +697,7 @@ static void remote_svc_handles_completed_cb(uint8_t type, uint16_t err, pdata += sizeof(uint32_t); dbus_message_iter_append_basic(&array_iter, - DBUS_TYPE_UINT32, &handle); + DBUS_TYPE_UINT32, &handle); } while (--tsrc); @@ -709,6 +709,123 @@ failed: transaction_context_free(ctxt, TRUE); } +static const char *extract_service_class(sdp_data_t *d) +{ + sdp_data_t *seq; + uuid_t *uuid; + static char uuid_str[37]; + + /* Expected sequence of UUID16 */ + if (d->attrId != SDP_ATTR_SVCLASS_ID_LIST || d->dtd != SDP_SEQ8) + return NULL; + + if (!d->val.dataseq) + return NULL; + + seq = d->val.dataseq; + if (!SDP_IS_UUID(seq->dtd)) + return NULL; + + uuid = &seq->val.uuid; + if (uuid->type != SDP_UUID16) + return NULL; + + sprintf(uuid_str, "0000%04X-0000-1000-8000-00805F9B34FB", + uuid->value.uuid16); + + return uuid_str; +} + +static void remote_svc_identifiers_completed_cb(uint8_t type, uint16_t err, + uint8_t *rsp, size_t size, void *udata) +{ + struct transaction_context *ctxt = udata; + DBusMessage *reply; + DBusMessageIter iter, array_iter; + int scanned, n, attrlen, extracted = 0, len = 0; + uint8_t dtd = 0; + + if (!ctxt) + return; + + if (err == 0xffff) { + /* Check for protocol error or I/O error */ + int sdp_err = sdp_get_error(ctxt->session); + if (sdp_err < 0) { + error("search failed: Invalid session!"); + error_failed(ctxt->conn, ctxt->rq, EINVAL); + goto failed; + } + + error("search failed: %s (%d)", strerror(sdp_err), sdp_err); + error_failed(ctxt->conn, ctxt->rq, sdp_err); + goto failed; + } + + if (type == SDP_ERROR_RSP) { + error_sdp_failed(ctxt->conn, ctxt->rq, err); + goto failed; + } + + /* Check response PDU ID */ + if (type != SDP_SVC_SEARCH_ATTR_RSP) { + error("SDP error: %s (%d)", strerror(EPROTO), EPROTO); + error_failed(ctxt->conn, ctxt->rq, EPROTO); + goto failed; + } + + reply = dbus_message_new_method_return(ctxt->rq); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_STRING_AS_STRING, &array_iter); + + /* Expected sequence of service class id list */ + scanned = sdp_extract_seqtype(rsp, &dtd, &len); + if (!scanned || !len) + goto done; + + rsp += scanned; + while (extracted < len) { + const char *puuid; + sdp_data_t *d; + int seqlen; + uint16_t attr; + + seqlen = 0; + scanned = sdp_extract_seqtype(rsp, &dtd, &seqlen); + if (!scanned || !seqlen) + goto done; + + extracted += (seqlen + scanned); + + n = sizeof(uint8_t); + attrlen = 0; + + rsp += scanned; + attr = ntohs(bt_get_unaligned((uint16_t *) (rsp + n))); + n += sizeof(uint16_t); + d = sdp_extract_attr(rsp + n, &attrlen, NULL); + if (!d) + break; + + d->attrId = attr; + puuid = extract_service_class(d); + if (puuid) + dbus_message_iter_append_basic(&array_iter, + DBUS_TYPE_STRING, &puuid); + sdp_data_free(d); + + n += attrlen; + rsp += n; + } +done: + dbus_message_iter_close_container(&iter, &array_iter); + send_message_and_unref(ctxt->conn, reply); + +failed: + transaction_context_free(ctxt, TRUE); +} + static gboolean sdp_client_connect_cb(GIOChannel *chan, GIOCondition cond, void *udata) { @@ -941,6 +1058,39 @@ static int remote_svc_handles_conn_cb(struct transaction_context *ctxt) return 0; } +static int remote_svc_identifiers_conn_cb(struct transaction_context *ctxt) +{ + sdp_list_t *attrids, *search; + uuid_t uuid; + uint16_t attr; + + if (sdp_set_notify(ctxt->session, + remote_svc_identifiers_completed_cb, ctxt) < 0) + return -EINVAL; + + sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP); + search = sdp_list_append(0, &uuid); + + attr = SDP_ATTR_SVCLASS_ID_LIST; + attrids = sdp_list_append(NULL, &attr); + + /* + * Create/send the search request and set the + * callback to indicate the request completion + */ + if (sdp_service_search_attr_async(ctxt->session, search, + SDP_ATTR_REQ_INDIVIDUAL, attrids) < 0) { + sdp_list_free(search, NULL); + sdp_list_free(attrids, NULL); + return -sdp_get_error(ctxt->session); + } + + sdp_list_free(search, NULL); + sdp_list_free(attrids, NULL); + + return 0; +} + DBusHandlerResult get_remote_svc_handles(DBusConnection *conn, DBusMessage *msg, void *data) { struct adapter *adapter = data; @@ -977,6 +1127,32 @@ DBusHandlerResult get_remote_svc_handles(DBusConnection *conn, DBusMessage *msg, return DBUS_HANDLER_RESULT_HANDLED; } +DBusHandlerResult get_remote_svc_identifiers(DBusConnection *conn, DBusMessage *msg, void *data) +{ + struct adapter *adapter = data; + const char *dst; + int err; + + if (!adapter->up) + return error_not_ready(conn, msg); + + if (!dbus_message_get_args(msg, NULL, + DBUS_TYPE_STRING, &dst, + DBUS_TYPE_INVALID)) + return error_invalid_arguments(conn, msg); + + if (find_pending_connect(dst)) + return error_service_search_in_progress(conn, msg); + + if (!connect_request(conn, msg, adapter->dev_id, + dst, remote_svc_identifiers_conn_cb, &err)) { + error("Search request failed: %s (%d)", strerror(err), err); + return error_failed(conn, msg, err); + } + + return DBUS_HANDLER_RESULT_HANDLED; +} + DBusHandlerResult finish_remote_svc_transact(DBusConnection *conn, DBusMessage *msg, void *data) { diff --git a/hcid/dbus-sdp.h b/hcid/dbus-sdp.h index d34f2fa1..c1a7382d 100644 --- a/hcid/dbus-sdp.h +++ b/hcid/dbus-sdp.h @@ -35,6 +35,8 @@ typedef enum { DBusHandlerResult get_remote_svc_handles(DBusConnection *conn, DBusMessage *msg, void *data); +DBusHandlerResult get_remote_svc_identifiers(DBusConnection *conn, DBusMessage *msg, void *data); + DBusHandlerResult get_remote_svc_rec(DBusConnection *conn, DBusMessage *msg, void *data, sdp_format_t format); DBusHandlerResult finish_remote_svc_transact(DBusConnection *conn, DBusMessage *msg, void *data); |