#include <stdio.h>
#include <stdint.h>
+#include "tds-provider-common.h"
void _tds_bloom_filter_add_string(uint8_t *inFilterPtr, size_t inFilterLen, const char *inStr, size_t inLen);
bool _tds_bloom_filter_contain_hash(const uint8_t *inFilterPtr, size_t inFilterLen, uint64_t inHash);
bool _tds_bloom_filter_contain_string(const uint8_t *inFilterPtr, size_t inFilterLen, const char *inStr, size_t inLen);
-int _bt_tds_start_scan(bt_tds_provider_scan_result_cb cb, void *user_data);
-int _bt_tds_stop_scan();
+int _tds_start_scan(tds_role_e search_role, bt_tds_provider_scan_result_cb cb, void *user_data);
+int _tds_stop_scan();
#endif /* __TDS_API_H__ */
#include <glib.h>
#include <gio/gio.h>
-#define ret_if(expr) \
- do { \
- if (expr) { \
- TDS_ERR("(%s) return", #expr); \
- return; \
- } \
- } while (0)
-
-#define retv_if(expr, val) \
- do { \
- if (expr) { \
- TDS_ERR("(%s) return", #expr); \
- return (val); \
- } \
- } while (0)
-
-#define TDS_CHECK_PARAMETER(arg, func) \
+#define TDS_CHECK_INPUT_PARAMETER(arg) \
do { \
if (arg == NULL) { \
TDS_ERR("%s is NULL", #arg); \
- func BLUETOOTH_ERROR_INVALID_PARAM; \
+ return TDS_ERROR_INVALID_PARAMETER; \
} \
} while (0)
typedef enum {
TDS_ERROR_NONE,
TDS_ERROR_INTERNAL,
+ TDS_ERROR_INVALID_PARAMETER,
} tds_error_e;
typedef enum {
TDS_TRANSPORT_STATE_ON,
} tds_transport_state_e;
+typedef enum {
+ TDS_ROLE_NOT_SPECIFIED,
+ TDS_ROLE_SEEKER,
+ TDS_ROLE_PROVIDER,
+ TDS_ROLE_BOTH,
+} tds_role_e;
+
#endif /* __TDS_PROVIDER_COMMON_H__ */
#ifndef __TDS_PROVIDER_SERVICE_H__
#define __TDS_PROVIDER_SERVICE_H__
+#include <bluetooth_internal.h>
+
int _tds_service_enable();
int _tds_service_disable();
+int _tds_service_handle_transport_block(bt_tds_transport_block_list_s *info);
#endif /* __TDS_PROVIDER_SERVICE_H__ */
#include <dlog.h>
#include <bluetooth.h>
#include <bluetooth_internal.h>
+#include "tds-provider-common.h"
+#include "tds-provider-log.h"
#include "csiphash/csiphash.h"
-uint64_t siphash24(const void *src, unsigned long src_sz, const char key[16]);
+struct tds_scan_data_s {
+ tds_role_e search_role;
+ bt_tds_provider_scan_result_cb callback;
+ void *user_data;
+};
+
+static struct tds_scan_data_s *scan_data = NULL;
#define kTDSSipHashKey ((const uint8_t *) \
"\x00\x00\x00\x00\x00\x00\x00\x00" \
)
#define kTDSSipHashCount 4
+#define TRANSPORT_DISCOVERY_DATA_AD_TYPE 0x26
+
void _tds_bloom_filter_add_string(uint8_t *inFilterPtr, size_t inFilterLen, const char *inStr, size_t inLen)
{
const size_t bitCount = inFilterLen * 8;
return _tds_bloom_filter_contain_hash(inFilterPtr, inFilterLen, hash);
}
-int _bt_tds_start_scan(bt_tds_provider_scan_result_cb cb, void *user_data)
+static int __tds_get_ad_data_by_type(char *in_data, int in_len,
+ char in_type, char **data, int *data_len)
+{
+ if (in_data == NULL || data == NULL || data_len == NULL)
+ return TDS_ERROR_INTERNAL;
+
+ if (in_len < 0)
+ return TDS_ERROR_INTERNAL;
+
+ int i;
+ int len = 0;
+ int type = 0;
+
+ for (i = 0; i < in_len; i++) {
+ len = in_data[i];
+ if (len <= 0 || i + 1 >= in_len) {
+ TDS_ERR("Invalid advertising data");
+ return TDS_ERROR_INTERNAL;
+ }
+
+ type = in_data[i + 1];
+ if (type == in_type) {
+ i = i + 2;
+ len--;
+ break;
+ }
+
+ i += len;
+ len = 0;
+ }
+
+ if (i + len > in_len) {
+ TDS_ERR("Invalid advertising data");
+ return TDS_ERROR_INTERNAL;
+ } else if (len == 0) {
+ //TDS_DBG("AD Type 0x%02x data is not set. skip", in_type);
+ *data = NULL;
+ *data_len = 0;
+ return TDS_ERROR_NONE;
+ }
+
+ *data = g_memdup(&in_data[i], len);
+ if (*data == NULL)
+ return TDS_ERROR_INTERNAL;
+ *data_len = len;
+
+ return TDS_ERROR_NONE;
+}
+
+static int __tds_parse_transport_blocks(tds_role_e search_role,
+ bt_tds_transport_block_list_s **info,
+ char *data, int data_len)
+{
+ int numblocks = 0;
+ int index = 2;
+ uint8_t flags;
+ int k;
+ GSList *info_list = NULL;
+ GSList *l = NULL;
+ tds_transport_data_s *td;
+
+ if (data_len < 3) {
+ TDS_ERR("Invalid TDS data, can not process!!");
+ return TDS_ERROR_INTERNAL;
+ }
+
+ while (index < data_len) {
+ flags = data[index-1];
+
+ TDS_INFO("Transport Block Role: %d, search_role: %d", flags & 0x03, search_role);
+ if (search_role != TDS_ROLE_BOTH && (flags & 0x03) != search_role) {
+ TDS_INFO("Role Not Matched");
+ continue;
+ }
+
+ td = g_malloc(sizeof(tds_transport_data_s));
+ td->length = data[index];
+ td->data = g_malloc0(td->length);
+
+ /* Fill Transport Block Data excluding Flag and Org ID */
+ for (k = 0; k < td->length; k++)
+ td->data[k] = data[k + index + 1];
+
+ /* Get Transport Name */
+ td->transport = data[index -2];
+ if (td->transport == 0x01)
+ td->transport = BT_TDS_TRANSPORT_BT;
+ else if (td->transport == 0x02)
+ td->transport = BT_TDS_TRANSPORT_WIFI_NAN;
+ else if (td->transport == 0x03)
+ td->transport = BT_TDS_TRANSPORT_WIFI_SVC_ADV;
+ else if (td->transport == 0x04)
+ td->transport = BT_TDS_TRANSPORT_CUSTOM;
+ else
+ td->transport = BT_TDS_TRANSPORT_INVALID;
+
+ /* Get Transport Data Block Incomplete status */
+ if (flags & 0x04)
+ td->is_data_complete = false;
+ else
+ td->is_data_complete = true;
+
+ /* Get Transport's current state */
+ if (flags & 0x08)
+ td->state = BT_TDS_TRANSPORT_STATE_ON;
+ else if (flags & 0x10)
+ td->state = BT_TDS_TRANSPORT_STATE_UNAVAILABLE;
+ else
+ td->state = BT_TDS_TRANSPORT_STATE_OFF;
+
+ /* Move to Next Block */
+ index = index + data[index] + 3;
+ info_list = g_slist_append(info_list, td);
+
+ (*info)->num_transport_block = ++numblocks;
+ TDS_DBG("Transport Block data length [%d] Flags [0x%x] Transport Name [0x%x] Block Num [%d]",
+ td->length, flags, td->transport, numblocks);
+
+ }
+
+ if (info_list != NULL) {
+ (*info)->data = (tds_transport_data_s**)g_malloc0(g_slist_length(info_list) * sizeof(tds_transport_data_s*));
+ k = 0;
+ while (info_list) {
+ l = info_list;
+ (*info)->data[k++] = (tds_transport_data_s*)l->data;
+ info_list = g_slist_remove(info_list, l->data);
+ }
+ return TDS_ERROR_NONE;
+ }
+ return TDS_ERROR_INTERNAL;
+}
+
+static void __tds_free_tds_scan_result_info(bt_tds_transport_block_list_s *info)
{
- /* TODO: need to use le_start_scan instead of seeking_provider */
- return bt_tds_start_seeking_providers(cb, user_data);
+ int i;
+
+ if (info == NULL)
+ return;
+
+ for (i = 0; i < info->num_transport_block; i++) {
+ g_free(info->data[i]->data);
+ g_free(info->data[i]);
+ }
+
+ g_free(info);
}
-int _bt_tds_stop_scan()
+static void __tds_scan_result_cb(int result,
+ bt_adapter_le_device_scan_result_info_s *scan_info, void *user_data)
{
- return bt_tds_stop_seeking_providers();
+ bt_tds_transport_block_list_s *info;
+ char *data = NULL;
+ int data_len = 0;
+
+ __tds_get_ad_data_by_type(scan_info->adv_data, scan_info->adv_data_len,
+ TRANSPORT_DISCOVERY_DATA_AD_TYPE,
+ &data, &data_len);
+ if (data == NULL)
+ return;
+
+ info = g_malloc0(sizeof(bt_tds_transport_block_list_s));
+ __tds_parse_transport_blocks(scan_data->search_role, &info, data, data_len);
+
+ if (scan_data->callback)
+ scan_data->callback(TDS_ERROR_NONE, scan_info->remote_address,
+ info, scan_info, scan_data->user_data);
+
+ __tds_free_tds_scan_result_info(info);
+ g_free(data);
+}
+
+int _tds_start_scan(tds_role_e search_role, bt_tds_provider_scan_result_cb callback, void *user_data)
+{
+ TDS_CHECK_INPUT_PARAMETER(callback);
+
+ scan_data = g_malloc0(sizeof(struct tds_scan_data_s));
+ scan_data->search_role = search_role;
+ scan_data->callback = callback;
+ scan_data->user_data = user_data;
+
+ return bt_adapter_le_start_scan(__tds_scan_result_cb, NULL);
+}
+
+int _tds_stop_scan()
+{
+ g_free(scan_data);
+
+ return bt_adapter_le_stop_scan();
}
#include "tds-provider-common.h"
#include "tds-provider-log.h"
#include "tds-provider-service.h"
+#include "tds-provider-advertiser.h"
#include "tds-api.h"
static bt_advertiser_h advertiser;
void *user_data)
{
TDS_INFO("Result: %d, Advertising %s", result,
- adv_state == BT_ADAPTER_LE_ADVERTISING_STARTED ? "started" : "stopped");
+ adv_state == BT_ADAPTER_LE_ADVERTISING_STARTED ? "Started" : "Stopped");
g_adv_started = TRUE;
}
return TDS_ERROR_NONE;
}
+ /* Stop previous advertiser if existing */
+ ret = _tds_advertiser_disable();
+ if (ret != BT_ERROR_NONE) {
+ TDS_ERR("_tds_advertiser_disable() failed. ret: %d", ret);
+ return TDS_ERROR_INTERNAL;
+ }
+
ret = bt_adapter_le_create_advertiser(&advertiser);
if (ret != BT_ERROR_NONE) {
TDS_ERR("bt_adapter_le_create_advertiser() failed. ret: %d", ret);
return TDS_ERROR_INTERNAL;
}
- TDS_INFO("Create bloom filter");
+ TDS_INFO("Create Bloom Filter with 'provider' Operation");
_tds_bloom_filter_add_string(transport_data, sizeof(transport_data),
TDS_BLOOM_FILTER_OPERATION_STR_PROVIDER, strlen(TDS_BLOOM_FILTER_OPERATION_STR_PROVIDER));
/* TODO: actually, below strings are not required. need to check that keep or not */
return TDS_ERROR_INTERNAL;
}
+ TDS_INFO("Advertising Stopped");
+
ret = bt_adapter_le_destroy_advertiser(advertiser);
if (ret != BT_ERROR_NONE) {
TDS_ERR("bt_tds_provider_unregister() failed. ret: %d", ret);
#include "tds-provider-common.h"
#include "tds-provider-log.h"
+static gboolean g_aware_enabled;
+
+static void __aware_enabled(wifi_aware_error_e error, void *user_data)
+{
+ if (error == WIFI_AWARE_ERROR_NONE) {
+ TDS_INFO("wifi-aware enabled");
+ g_aware_enabled = TRUE;
+ }
+}
+
int _tds_aware_enable()
{
+ int ret;
TDS_DBG("Enter");
+
+ if (g_aware_enabled)
+ return TDS_ERROR_NONE;
+
+ ret = wifi_aware_initialize();
+ if (ret != WIFI_AWARE_ERROR_NONE) {
+ TDS_ERR("wifi_aware_initialize() failed. ret: 0x%x", ret);
+ return TDS_ERROR_INTERNAL;
+ }
+
+ ret = wifi_aware_enable(__aware_enabled, NULL);
+ if (ret != WIFI_AWARE_ERROR_NONE) {
+ TDS_ERR("wifi_aware_enable() failed. ret: 0x%x", ret);
+ return TDS_ERROR_INTERNAL;
+ }
+
return TDS_ERROR_NONE;
}
int _tds_aware_disable()
{
+ int ret;
TDS_DBG("Enter");
+
+ ret = wifi_aware_disable();
+ if (ret != WIFI_AWARE_ERROR_NONE) {
+ TDS_ERR("wifi_aware_disable() failed. ret: 0x%x", ret);
+ return TDS_ERROR_INTERNAL;
+ }
+
+ g_aware_enabled = FALSE;
return TDS_ERROR_NONE;
}
#include "tds-provider-common.h"
#include "tds-provider-log.h"
#include "tds-provider-service.h"
+#include "tds-api.h"
+
+static gboolean g_scan_started;
+
+static char *__tds_scanner_convert_transport_to_str(bt_tds_transport_e transport)
+{
+ switch (transport) {
+ case BT_TDS_TRANSPORT_BT:
+ return "BT";
+ case BT_TDS_TRANSPORT_CUSTOM:
+ return "CUSTOM";
+ case BT_TDS_TRANSPORT_WIFI_NAN:
+ return "WIFI_NAN";
+ case BT_TDS_TRANSPORT_WIFI_SVC_ADV:
+ return "WIFI_SVC_ADV";
+ case BT_TDS_TRANSPORT_INVALID:
+ default:
+ return "INVALID";
+ }
+}
+
+static char *__tds_scanner_convert_state_to_str(bt_tds_transport_state_e state)
+{
+ switch (state) {
+ case BT_TDS_TRANSPORT_STATE_OFF:
+ return "OFF";
+ case BT_TDS_TRANSPORT_STATE_ON:
+ return "ON";
+ case BT_TDS_TRANSPORT_STATE_UNAVAILABLE:
+ default:
+ return "UNAVAILABLE";
+ }
+}
+
+static void __tds_scanner_seeker_found_cb(int result,
+ const char *remote_address, bt_tds_transport_block_list_s *info,
+ bt_adapter_le_device_scan_result_info_s *scan_info, void *user_data)
+{
+ int i, j;
+ TDS_DBG("Enter");
+
+ if (info == NULL) {
+ TDS_ERR("info is NULL");
+ return;
+ }
+
+ TDS_INFO("Seeker found. result: %d, remote_address: %s, num of transport block: %d",
+ result, remote_address, info->num_transport_block);
+
+ if (result == BT_ERROR_NONE) {
+ for (i = 0; i < info->num_transport_block; i++) {
+ TDS_INFO("- Block index: %d", i);
+ TDS_INFO("Transport ID: %s", __tds_scanner_convert_transport_to_str(info->data[i]->transport));
+ TDS_INFO("Transport State: %s", __tds_scanner_convert_state_to_str(info->data[i]->state));
+ TDS_INFO("Is Data Complete: %s", info->data[i]->is_data_complete ? "TRUE" : "FALSE");
+ TDS_INFO("Length of Transport Data: %d", info->data[i]->length);
+ for (j = 0; j < info->data[i]->length; j++)
+ TDS_INFO("Transport Data[%d]: 0x%02x", j, info->data[i]->data[j]);
+ }
+
+ _tds_service_handle_transport_block(info);
+ }
+}
int _tds_scanner_enable()
{
+ int ret;
TDS_DBG("Enter");
+
+ ret = _tds_start_scan(TDS_ROLE_SEEKER, __tds_scanner_seeker_found_cb, NULL);
+ if (ret != BT_ERROR_NONE) {
+ TDS_ERR("_tds_start_scan() failed. ret: %d", ret);
+ return TDS_ERROR_INTERNAL;
+ }
+
+ TDS_INFO("Scan Started");
+ g_scan_started = TRUE;
return TDS_ERROR_NONE;
}
int _tds_scanner_disable()
{
+ int ret ;
TDS_DBG("Enter");
+
+ if (!g_scan_started)
+ return TDS_ERROR_NONE;
+
+ ret = _tds_stop_scan();
+ if (ret != BT_ERROR_NONE) {
+ TDS_ERR("_tds_stop_scan() failed. ret: %d", ret);
+ return TDS_ERROR_INTERNAL;
+ }
+
+ TDS_INFO("Scan Stopped");
+ g_scan_started = FALSE;
return TDS_ERROR_NONE;
}
#include "tds-provider-aware.h"
#include "tds-api.h"
+static char *g_local_address;
+
static int __tds_service_start()
{
int ret;
+ ret = bt_adapter_get_address(&g_local_address);
+ if (ret != BT_ERROR_NONE) {
+ TDS_ERR("bt_adapter_get_address() failed. ret: %d", ret);
+ return TDS_ERROR_INTERNAL;
+ }
+
+ TDS_INFO("g_local_address: %s", g_local_address);
+
ret = _tds_scanner_enable();
if (ret != TDS_ERROR_NONE) {
TDS_ERR("_tds_scanner_enable() failed. ret: %d", ret);
TDS_ERR("__tds_service_start() failed. ret: %d", ret);
return TDS_ERROR_INTERNAL;
}
+ } else {
+ TDS_INFO("BT Adapter Disabled");
}
return TDS_ERROR_NONE;
int ret;
TDS_DBG("Enter");
+ if (g_local_address)
+ free(g_local_address);
+
ret = bt_adapter_unset_state_changed_cb();
if (ret != BT_ERROR_NONE) {
TDS_ERR("bt_deinitialize() failed. ret: %d", ret);
return TDS_ERROR_NONE;
}
+
+int _tds_service_handle_transport_block(bt_tds_transport_block_list_s *info)
+{
+ int i;
+ TDS_DBG("Enter");
+
+ for (i = 0; i < info->num_transport_block; i++) {
+ if (info->data[i]->transport != BT_TDS_TRANSPORT_WIFI_NAN) {
+ TDS_INFO("Organization ID not matched. org_id: %d", info->data[i]->transport);
+ continue;
+ }
+
+ if (info->data[i]->state == BT_TDS_TRANSPORT_STATE_OFF) {
+ if (_tds_bloom_filter_contain_string((unsigned char *)info->data[i]->data, info->data[i]->length,
+ TDS_BLOOM_FILTER_OPERATION_STR_SEEK, strlen(TDS_BLOOM_FILTER_OPERATION_STR_SEEK)) == TRUE) {
+ TDS_INFO("Bloom Filter Operation 'seek' contained in Transport Block!!");
+ _tds_advertiser_enable(TDS_TRANSPORT_STATE_OFF);
+ } else if (_tds_bloom_filter_contain_string((unsigned char *)info->data[i]->data, info->data[i]->length,
+ TDS_BLOOM_FILTER_OPERATION_STR_BROWSE, strlen(TDS_BLOOM_FILTER_OPERATION_STR_BROWSE)) == TRUE) {
+ TDS_INFO("Bloom Filter Operation 'browse' contained in Transport Block!!");
+ _tds_advertiser_enable(TDS_TRANSPORT_STATE_ON);
+ _tds_scanner_disable();
+ _tds_aware_enable();
+ /* TODO: Start NAN Service Discovery */
+ _tds_advertiser_disable();
+ } else {
+ TDS_INFO("Unknown Operation");
+ }
+ } else if (info->data[i]->state == BT_TDS_TRANSPORT_STATE_ON) {
+ if (_tds_bloom_filter_contain_string((unsigned char *)info->data[i]->data, info->data[i]->length,
+ TDS_BLOOM_FILTER_OPERATION_STR_ACTIVATION, strlen(TDS_BLOOM_FILTER_OPERATION_STR_ACTIVATION)) == TRUE) {
+ TDS_INFO("Bloom Filter Operation 'activation' contained in Transport Block!!");
+ if (_tds_bloom_filter_contain_string((unsigned char *)info->data[i]->data, info->data[i]->length,
+ g_local_address, strlen(g_local_address)) == TRUE) {
+ TDS_INFO("Address Matched");
+ _tds_advertiser_disable();
+ _tds_scanner_disable();
+ _tds_aware_enable();
+ /* TODO: Start NAN Service Discovery */
+ } else {
+ TDS_ERR("Address Not Matched");
+ }
+ } else {
+ TDS_ERR("Unknown Operation");
+ }
+ } else {
+ TDS_ERR("Transport State Unavailable");
+ }
+ }
+
+ return TDS_ERROR_NONE;
+}