+static int __bt_get_ad_data_by_type(const char *in_data, int in_len,
+ char in_type, char **data, int *data_len)
+{
+ if (in_data == NULL || data == NULL || data_len == NULL)
+ return BLUETOOTH_ERROR_INTERNAL;
+
+ if (in_len <= 0)
+ return BLUETOOTH_ERROR_INTERNAL;
+
+ int i;
+ unsigned char len = 0;
+ int type = 0;
+
+ for (i = 0; i < in_len && i < BLUETOOTH_ADVERTISING_DATA_LENGTH_MAX - 1; i++) {
+ len = in_data[i];
+ if (len <= 0 || i + 1 >= in_len) {
+ BT_ERR("Invalid advertising data");
+ return BLUETOOTH_ERROR_INTERNAL;
+ }
+
+ type = in_data[i + 1];
+ if (type == in_type) {
+ i = i + 2;
+ len--;
+ break;
+ }
+
+ i += len;
+ len = 0;
+ }
+
+ if (i > BLUETOOTH_ADVERTISING_DATA_LENGTH_MAX - 1
+ || i + len > in_len
+ || i + len > BLUETOOTH_ADVERTISING_DATA_LENGTH_MAX) {
+ BT_ERR("Invalid advertising data");
+ return BLUETOOTH_ERROR_INTERNAL;
+ } else if (len == 0) {
+ BT_DBG("AD Type 0x%02x data is not set", in_type);
+ *data = NULL;
+ *data_len = 0;
+ return BLUETOOTH_ERROR_NONE;
+ }
+
+ *data = g_memdup(&in_data[i], len);
+ if (*data == NULL)
+ return BLUETOOTH_ERROR_OUT_OF_MEMORY;
+ *data_len = len;
+
+ return BLUETOOTH_ERROR_NONE;
+}
+
+static gboolean __bt_check_scan_result_uuid(const char *adv_data,
+ int adv_data_len, const char *svc_uuid, int uuid_len,
+ const char *uuid_mask, char ad_type)
+{
+ char *data = NULL;
+ int data_len = 0;
+ int i;
+
+ __bt_get_ad_data_by_type(adv_data, adv_data_len,
+ ad_type, &data, &data_len);
+ if (data != NULL) {
+ for (i = 0; i < data_len; i += uuid_len) {
+ if (uuid_len > (data_len - i))
+ break;
+
+ if (_bt_byte_arr_cmp_with_mask(data + i,
+ svc_uuid, uuid_mask, uuid_len) == 0) {
+ g_free(data);
+ return TRUE;
+ }
+ }
+ g_free(data);
+ }
+
+ return FALSE;
+}
+
+static gboolean __bt_check_scan_result_with_filter(const char *device_address,
+ const char *adv_data, int adv_data_len,
+ const char *scan_data, int scan_data_len,
+ const bt_adapter_le_scanner_t *scanner)
+{
+ GSList *l;
+ bluetooth_le_scan_filter_t *filter_data = NULL;
+ char *data = NULL;
+ int data_len = 0;
+ gboolean is_matched = FALSE;
+ int idx;
+
+ if (scanner->filter_list == NULL) {
+ BT_DBG("This scanner is on Full Scan.");
+ return TRUE;
+ }
+
+ for (l = scanner->filter_list; l != NULL; l = g_slist_next(l)) {
+ filter_data = l->data;
+
+ if (filter_data->added_features &
+ BLUETOOTH_LE_SCAN_FILTER_FEATURE_DEVICE_ADDRESS) {
+ char address[BT_ADDRESS_STRING_SIZE] = { 0 };
+
+ _bt_convert_addr_type_to_string(address,
+ filter_data->device_address.addr);
+ if (strncmp(address, device_address,
+ BT_ADDRESS_STRING_SIZE) != 0)
+ continue;
+ }
+
+ /* Check service uuid filter */
+ if (filter_data->added_features &
+ BLUETOOTH_LE_SCAN_FILTER_FEATURE_SERVICE_UUID) {
+ is_matched = FALSE;
+ const int ad_type_uuids[] = {
+ BT_LE_AD_TYPE_INCOMP_LIST_16_BIT_SERVICE_CLASS_UUIDS,
+ BT_LE_AD_TYPE_COMP_LIST_16_BIT_SERVICE_CLASS_UUIDS,
+ BT_LE_AD_TYPE_INCOMP_LIST_128_BIT_SERVICE_CLASS_UUIDS,
+ BT_LE_AD_TYPE_COMP_LIST_128_BIT_SERVICE_CLASS_UUIDS
+ };
+
+ for (idx = 0; idx < sizeof(ad_type_uuids) / sizeof(bt_le_ad_type_t); idx++) {
+ /* Check service uuid in advertising data */
+ if (__bt_check_scan_result_uuid(adv_data, adv_data_len,
+ (const char *)filter_data->service_uuid.data.data,
+ filter_data->service_uuid.data_len,
+ (const char *)filter_data->service_uuid_mask.data.data,
+ ad_type_uuids[idx]) == TRUE) {
+ BT_INFO("Service UUID is matched in adv data.");
+ is_matched = TRUE;
+ break;
+ }
+
+ /* Check service uuid in scan response data */
+ if (__bt_check_scan_result_uuid(scan_data, scan_data_len,
+ (const char *)filter_data->service_uuid.data.data,
+ filter_data->service_uuid.data_len,
+ (const char *)filter_data->service_uuid_mask.data.data,
+ ad_type_uuids[idx]) == TRUE) {
+ BT_INFO("Service UUID is matched in scan data.");
+ is_matched = TRUE;
+ break;
+ }
+ }
+
+ /* Service UUID is NOT matched. Continue to next filter */
+ if (is_matched == FALSE)
+ continue;
+ }
+
+ /* Check solicitation uuid filter */
+ if (filter_data->added_features &
+ BLUETOOTH_LE_SCAN_FILTER_FEATURE_SERVICE_SOLICITATION_UUID) {
+ is_matched = FALSE;
+ const int ad_type_solicit_uuids[] = {
+ BT_LE_AD_TYPE_LIST_16_BIT_SERVICE_SOLICITATION_UUIDS,
+ BT_LE_AD_TYPE_LIST_128_BIT_SERVICE_SOLICITATION_UUIDS
+ };
+
+ for (idx = 0; idx < sizeof(ad_type_solicit_uuids) / sizeof(bt_le_ad_type_t); idx++) {
+ /* Check solicit uuid in advertising data */
+ if (__bt_check_scan_result_uuid(adv_data, adv_data_len,
+ (const char *)filter_data->service_solicitation_uuid.data.data,
+ filter_data->service_solicitation_uuid.data_len,
+ (const char *)filter_data->service_solicitation_uuid_mask.data.data,
+ ad_type_solicit_uuids[idx]) == TRUE) {
+ BT_INFO("Service Solicitation UUID is matched in adv data.");
+ is_matched = TRUE;
+ break;
+ }
+
+ /* Check solicit uuid in scan response data */
+ if (__bt_check_scan_result_uuid(scan_data, scan_data_len,
+ (const char *)filter_data->service_solicitation_uuid.data.data,
+ filter_data->service_solicitation_uuid.data_len,
+ (const char *)filter_data->service_solicitation_uuid_mask.data.data,
+ ad_type_solicit_uuids[idx]) == TRUE) {
+ BT_INFO("Service Solicitation UUID is matched in scan data.");
+ is_matched = TRUE;
+ break;
+ }
+ }
+
+ /* Service Solicitation UUID is NOT matched. Continue to next filter */
+ if (is_matched == FALSE)
+ continue;
+ }
+
+ /* Check device name filter */
+ if (filter_data->added_features &
+ BLUETOOTH_LE_SCAN_FILTER_FEATURE_DEVICE_NAME) {
+ char name[BLUETOOTH_ADVERTISING_DATA_LENGTH_MAX] = {0, };
+ data = NULL;
+ data_len = 0;
+ is_matched = FALSE;
+
+ /* Check device name in advertising data */
+ __bt_get_ad_data_by_type(adv_data, adv_data_len,
+ BT_LE_AD_TYPE_COMPLETE_LOCAL_NAME,
+ &data, &data_len);
+ if (data != NULL) {
+ if (data_len >= BLUETOOTH_ADVERTISING_DATA_LENGTH_MAX)
+ data_len = BLUETOOTH_ADVERTISING_DATA_LENGTH_MAX - 1;
+ memcpy(name, data, data_len);
+ name[data_len] = '\0';
+ g_free(data);
+ data = NULL;
+ if (g_strrstr(name, filter_data->device_name) != NULL) {
+ BT_INFO("Device Name is matched in adv data.");
+ is_matched = TRUE;
+ }
+ }
+
+ /* Check device name in scan response data */
+ __bt_get_ad_data_by_type(scan_data, scan_data_len,
+ BT_LE_AD_TYPE_COMPLETE_LOCAL_NAME,
+ &data, &data_len);
+ if (data != NULL) {
+ if (data_len >= BLUETOOTH_ADVERTISING_DATA_LENGTH_MAX)
+ data_len = BLUETOOTH_ADVERTISING_DATA_LENGTH_MAX - 1;
+ memcpy(name, data, data_len);
+ name[data_len] = '\0';
+ g_free(data);
+ data = NULL;
+ if (g_strrstr(name, filter_data->device_name) != NULL) {
+ BT_INFO("Device Name is matched in scan data.");
+ is_matched = TRUE;
+ }
+ }
+
+ /* Device Name is NOT matched. Continue to next filter */
+ if (is_matched == FALSE)
+ continue;
+ }
+
+ /* Check manufacturer data filter */
+ if (filter_data->added_features &
+ BLUETOOTH_LE_SCAN_FILTER_FEATURE_MANUFACTURER_DATA) {
+ data = NULL;
+ data_len = 0;
+ is_matched = FALSE;
+
+ /* Check manufacturer data in advertising data */
+ __bt_get_ad_data_by_type(adv_data, adv_data_len,
+ BT_LE_AD_TYPE_MANUFACTURER_SPECIFIC_DATA,
+ &data, &data_len);
+ if (data != NULL) {
+ int manufacturer_id;
+ manufacturer_id = (data[1] << 8) + data[0];
+
+ if (filter_data->manufacturer_id == manufacturer_id) {
+ if (filter_data->manufacturer_data.data_len == 0) {
+ is_matched = TRUE;
+ } else {
+ if (data_len >= BLUETOOTH_ADVERTISING_DATA_LENGTH_MAX)
+ data_len = BLUETOOTH_ADVERTISING_DATA_LENGTH_MAX - 1;
+ if (_bt_byte_arr_cmp_with_mask(data + 2,
+ (const char *)filter_data->manufacturer_data.data.data,
+ (const char *)filter_data->manufacturer_data_mask.data.data,
+ data_len - 2) == 0) {
+ BT_INFO("Manufacturer Data is matched in adv data.");
+ is_matched = TRUE;
+ }
+ }
+ }
+ g_free(data);
+ data = NULL;
+ }
+
+ /* Check manufacturer data in scan response data */
+ __bt_get_ad_data_by_type(scan_data, scan_data_len,
+ BT_LE_AD_TYPE_MANUFACTURER_SPECIFIC_DATA,
+ &data, &data_len);
+ if (data != NULL) {
+ int manufacturer_id;
+ manufacturer_id = (data[1] << 8) + data[0];
+
+ if (filter_data->manufacturer_id == manufacturer_id) {
+ if (filter_data->manufacturer_data.data_len == 0) {
+ is_matched = TRUE;
+ } else {
+ if (data_len >= BLUETOOTH_ADVERTISING_DATA_LENGTH_MAX)
+ data_len = BLUETOOTH_ADVERTISING_DATA_LENGTH_MAX - 1;
+ if (_bt_byte_arr_cmp_with_mask(data + 2,
+ (const char *)filter_data->manufacturer_data.data.data,
+ (const char *)filter_data->manufacturer_data_mask.data.data,
+ data_len - 2) == 0) {
+ BT_INFO("Manufacturer Data is matched in scan data.");
+ is_matched = TRUE;
+ }
+ }
+ }
+ g_free(data);
+ data = NULL;
+ }
+
+ /* Manufacturer Data is NOT matched. Continue to next filter */
+ if (is_matched == FALSE)
+ continue;
+ }
+
+ /* Check service data filter */
+ if (filter_data->added_features &
+ BLUETOOTH_LE_SCAN_FILTER_FEATURE_SERVICE_DATA) {
+ data = NULL;
+ data_len = 0;
+ is_matched = FALSE;
+
+ /* Check service data in advertising data */
+ __bt_get_ad_data_by_type(adv_data,
+ adv_data_len,
+ BT_LE_AD_TYPE_SERVICE_DATA,
+ &data, &data_len);
+ if (data != NULL) {
+ if (data_len >= BLUETOOTH_ADVERTISING_DATA_LENGTH_MAX)
+ data_len = BLUETOOTH_ADVERTISING_DATA_LENGTH_MAX - 1;
+ if (_bt_byte_arr_cmp_with_mask(data,
+ (const char *)filter_data->service_data.data.data,
+ (const char *)filter_data->service_data_mask.data.data,
+ data_len) == 0) {
+ BT_INFO("Service Data is matched in adv data.");
+ is_matched = TRUE;
+ }
+ g_free(data);
+ data = NULL;
+ }
+
+ /* Check service data in scan response data */
+ __bt_get_ad_data_by_type(scan_data,
+ scan_data_len,
+ BT_LE_AD_TYPE_SERVICE_DATA,
+ &data, &data_len);
+ if (data != NULL) {
+ if (data_len >= BLUETOOTH_ADVERTISING_DATA_LENGTH_MAX)
+ data_len = BLUETOOTH_ADVERTISING_DATA_LENGTH_MAX - 1;
+ if (_bt_byte_arr_cmp_with_mask(data,
+ (const char *)filter_data->service_data.data.data,
+ (const char *)filter_data->service_data_mask.data.data,
+ data_len) == 0) {
+ BT_INFO("Service Data is matched in scan data.");
+ is_matched = TRUE;
+ }
+ g_free(data);
+ data = NULL;
+ }
+
+ /* Service Data is NOT matched. Continue to next filter */
+ if (is_matched == FALSE)
+ continue;
+ }
+
+ BT_INFO("The scan result is conformable.");
+ return TRUE;
+ }
+
+ BT_INFO("The scan result is NOT conformable.");
+ return FALSE;
+}
+