| /* |
| * |
| * Copyright (c) 2021 Project CHIP Authors |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * @file |
| * Provides an implementation of the Matter Device Scanner |
| * for the Tizen platforms |
| */ |
| |
| #include "ChipDeviceScanner.h" |
| |
| #include <cstdint> |
| #include <cstring> |
| #include <utility> |
| |
| #include <bluetooth.h> |
| #include <bluetooth_internal.h> |
| |
| #include <lib/support/CodeUtils.h> |
| #include <lib/support/Span.h> |
| #include <lib/support/logging/CHIPLogging.h> |
| #include <platform/PlatformManager.h> |
| |
| namespace chip { |
| namespace DeviceLayer { |
| namespace Internal { |
| |
| // CHIPoBLE UUID strings |
| const char * chip_service_uuid = "0000FFF6-0000-1000-8000-00805F9B34FB"; |
| const char * chip_service_uuid_short = "FFF6"; |
| |
| ChipDeviceScanner::ChipDeviceScanner(ChipDeviceScannerDelegate * delegate) : mDelegate(delegate) {} |
| |
| ChipDeviceScanner::~ChipDeviceScanner() |
| { |
| // In case scan is ongoing |
| StopChipScan(); |
| mDelegate = nullptr; |
| } |
| |
| std::unique_ptr<ChipDeviceScanner> ChipDeviceScanner::Create(ChipDeviceScannerDelegate * delegate) |
| { |
| return std::make_unique<ChipDeviceScanner>(delegate); |
| } |
| |
| static void __CleanupServiceData(bt_adapter_le_service_data_s * dataList, size_t count) |
| { |
| VerifyOrReturn(dataList != nullptr); |
| VerifyOrReturn(count != 0); |
| |
| for (size_t i = 0; i < count; i++) |
| { |
| g_free(dataList[i].service_uuid); |
| g_free(dataList[i].service_data); |
| } |
| g_free(dataList); |
| } |
| |
| static void __PrintLEScanData(bt_adapter_le_service_data_s * dataList, size_t idx) |
| { |
| VerifyOrReturn(dataList != nullptr); |
| |
| // Print Service UUID in the Service Data |
| ChipLogDetail(DeviceLayer, "======Service UUID========"); |
| ChipLogDetail(DeviceLayer, "Service UUID::[%s]", dataList[idx].service_uuid); |
| |
| // Print Service Data |
| ChipLogDetail(DeviceLayer, "======Service Data========"); |
| ChipLogDetail(DeviceLayer, "Service Data Length::[%d]", dataList[idx].service_data_len); |
| ChipLogByteSpan(DeviceLayer, ByteSpan(reinterpret_cast<uint8_t *>(dataList[idx].service_data), dataList[idx].service_data_len)); |
| } |
| |
| static bool __IsChipThingDevice(bt_adapter_le_device_scan_result_info_s * info, |
| chip::Ble::ChipBLEDeviceIdentificationInfo & aDeviceInfo) |
| { |
| VerifyOrReturnError(info != nullptr, false); |
| |
| int count = 0; |
| bt_adapter_le_service_data_s * dataList = nullptr; |
| bool isChipDevice = false; |
| |
| ChipLogProgress(DeviceLayer, "Is [%s] ChipThingDevice ?: Check now", info->remote_address); |
| |
| if (bt_adapter_le_get_scan_result_service_data_list(info, BT_ADAPTER_LE_PACKET_ADVERTISING, &dataList, &count) == BT_ERROR_NONE) |
| { |
| for (int i = 0; i < count; i++) |
| { |
| if (g_strcmp0(dataList[i].service_uuid, chip_service_uuid) == 0 || |
| g_strcmp0(dataList[i].service_uuid, chip_service_uuid_short) == 0) |
| { |
| ChipLogProgress(DeviceLayer, "CHIP Thing Device Found! [Service Data UUID] = %s", dataList[i].service_uuid); |
| // Print full Service Data |
| __PrintLEScanData(dataList, i); |
| memcpy(&aDeviceInfo, dataList[i].service_data, dataList[i].service_data_len); |
| isChipDevice = true; |
| break; |
| } |
| } |
| } |
| __CleanupServiceData(dataList, count); |
| return isChipDevice; |
| } |
| |
| void ChipDeviceScanner::LeScanResultCb(int result, bt_adapter_le_device_scan_result_info_s * info, void * userData) |
| { |
| VerifyOrReturn(info != nullptr); |
| |
| auto self = reinterpret_cast<ChipDeviceScanner *>(userData); |
| chip::Ble::ChipBLEDeviceIdentificationInfo deviceInfo; |
| |
| ChipLogProgress(DeviceLayer, "LE Device Reported!! remote addr [%s]", info->remote_address); |
| |
| if (__IsChipThingDevice(info, deviceInfo)) |
| { |
| // Looks like a CHIP Thing Device: Service UUID matched |
| ChipLogProgress(DeviceLayer, "Looks Like Got a CHIP Thing Device: Process further"); |
| // Report probable CHIP Thing Device to BLEMgrImp class |
| self->mDelegate->OnChipDeviceScanned(info, deviceInfo); |
| } |
| else |
| ChipLogProgress(DeviceLayer, "Does not Look like a CHIP Device, Skip....."); |
| } |
| |
| gboolean ChipDeviceScanner::TimerExpiredCb(gpointer userData) |
| { |
| auto self = reinterpret_cast<ChipDeviceScanner *>(userData); |
| ChipLogProgress(DeviceLayer, "Scan Timer expired!!"); |
| self->StopChipScan(); |
| return G_SOURCE_REMOVE; |
| } |
| |
| CHIP_ERROR ChipDeviceScanner::TriggerScan(ChipDeviceScanner * self) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| GSource * idleSource; |
| int ret; |
| |
| // Trigger LE Scan |
| ret = bt_adapter_le_start_scan(LeScanResultCb, self); |
| VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_adapter_le_start_scan() failed: %s", get_error_message(ret)); |
| err = CHIP_ERROR_INTERNAL); |
| self->mIsScanning = true; |
| |
| // Setup timer for scan timeout |
| idleSource = g_timeout_source_new(self->mScanTimeoutMs); |
| g_source_set_callback(idleSource, TimerExpiredCb, self, nullptr); |
| g_source_set_priority(idleSource, G_PRIORITY_HIGH_IDLE); |
| g_source_attach(idleSource, g_main_context_get_thread_default()); |
| g_source_unref(idleSource); |
| |
| exit: |
| return err; |
| } |
| |
| static bool __IsScanFilterSupported() |
| { |
| bool is_supported; |
| int ret = bt_adapter_le_is_scan_filter_supported(&is_supported); |
| VerifyOrReturnValue(ret == BT_ERROR_NONE, false, |
| ChipLogError(DeviceLayer, "bt_adapter_le_is_scan_filter_supported() failed: %s", get_error_message(ret))); |
| return is_supported; |
| } |
| |
| void ChipDeviceScanner::CheckScanFilter(ScanFilterType filterType, ScanFilterData & filterData) |
| { |
| int ret = BT_ERROR_NONE; |
| |
| // Scan Filter check |
| if (!__IsScanFilterSupported()) |
| return; |
| |
| ret = CreateLEScanFilter(filterType, filterData); |
| VerifyOrExit(ret == BT_ERROR_NONE, |
| ChipLogError(DeviceLayer, "BLE scan filter creation failed: %s. Do Normal Scan", get_error_message(ret))); |
| |
| ret = RegisterScanFilter(filterType, filterData); |
| VerifyOrExit(ret == BT_ERROR_NONE, |
| ChipLogError(DeviceLayer, "BLE scan filter registration failed: %s. Do Normal Scan", get_error_message(ret))); |
| |
| return; |
| |
| exit: |
| UnRegisterScanFilter(); |
| } |
| |
| CHIP_ERROR ChipDeviceScanner::StartChipScan(System::Clock::Timeout timeout, ScanFilterType filterType, ScanFilterData & filterData) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| ReturnErrorCodeIf(mIsScanning, CHIP_ERROR_INCORRECT_STATE); |
| |
| // Scan Filter Setup if supported: silently bypass error & do filterless scan in case of error |
| CheckScanFilter(filterType, filterData); |
| |
| mScanTimeoutMs = System::Clock::Milliseconds32(timeout).count(); |
| |
| // All set to trigger LE Scan |
| ChipLogProgress(DeviceLayer, "Start CHIP BLE scan: timeout=%ums", mScanTimeoutMs); |
| err = PlatformMgrImpl().GLibMatterContextInvokeSync(TriggerScan, this); |
| SuccessOrExit(err); |
| |
| return CHIP_NO_ERROR; |
| |
| exit: |
| ChipLogError(DeviceLayer, "Start CHIP Scan could not succeed fully! Stop Scan..."); |
| StopChipScan(); |
| UnRegisterScanFilter(); |
| return err; |
| } |
| |
| CHIP_ERROR ChipDeviceScanner::StopChipScan() |
| { |
| int ret = BT_ERROR_NONE; |
| ReturnErrorCodeIf(!mIsScanning, CHIP_ERROR_INCORRECT_STATE); |
| |
| ret = bt_adapter_le_stop_scan(); |
| if (ret != BT_ERROR_NONE) |
| { |
| ChipLogError(DeviceLayer, "bt_adapter_le_stop_scan() failed: %s", get_error_message(ret)); |
| } |
| |
| ChipLogProgress(DeviceLayer, "CHIP Scanner Async Thread Quit Done..Wait for Thread Windup...!"); |
| |
| UnRegisterScanFilter(); |
| |
| // Report to Impl class |
| mDelegate->OnScanComplete(); |
| |
| mIsScanning = false; |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| void ChipDeviceScanner::UnRegisterScanFilter() |
| { |
| if (mScanFilter) |
| { |
| bt_adapter_le_scan_filter_unregister(mScanFilter); |
| mScanFilter = nullptr; |
| } |
| } |
| |
| int ChipDeviceScanner::RegisterScanFilter(ScanFilterType filterType, ScanFilterData & filterData) |
| { |
| int ret = BT_ERROR_NONE; |
| |
| switch (filterType) |
| { |
| case ScanFilterType::kAddress: { |
| ChipLogProgress(DeviceLayer, "Register BLE scan filter: Address"); |
| ret = bt_adapter_le_scan_filter_set_device_address(mScanFilter, filterData.address); |
| VerifyOrExit( |
| ret == BT_ERROR_NONE, |
| ChipLogError(DeviceLayer, "bt_adapter_le_scan_filter_set_device_address() failed: %s", get_error_message(ret))); |
| break; |
| } |
| case ScanFilterType::kServiceUUID: { |
| ChipLogProgress(DeviceLayer, "Register BLE scan filter: Service UUID"); |
| ret = bt_adapter_le_scan_filter_set_service_uuid(mScanFilter, filterData.service_uuid); |
| VerifyOrExit(ret == BT_ERROR_NONE, |
| ChipLogError(DeviceLayer, "bt_adapter_le_scan_filter_set_service_uuid() failed: %s", get_error_message(ret))); |
| break; |
| } |
| case ScanFilterType::kServiceData: { |
| ChipLogProgress(DeviceLayer, "Register BLE scan filter: Service Data"); |
| ret = bt_adapter_le_scan_filter_set_service_data(mScanFilter, filterData.service_uuid, filterData.service_data, |
| filterData.service_data_len); |
| VerifyOrExit(ret == BT_ERROR_NONE, |
| ChipLogError(DeviceLayer, "bt_adapter_le_scan_filter_set_service_data() failed: %s", get_error_message(ret))); |
| break; |
| } |
| case ScanFilterType::kNoFilter: |
| default: |
| goto exit; |
| } |
| |
| ret = bt_adapter_le_scan_filter_register(mScanFilter); |
| VerifyOrExit(ret == BT_ERROR_NONE, |
| ChipLogError(DeviceLayer, "bt_adapter_le_scan_filter_register() failed: %s", get_error_message(ret))); |
| |
| exit: |
| return ret; |
| } |
| |
| int ChipDeviceScanner::CreateLEScanFilter(ScanFilterType filterType, ScanFilterData & filterData) |
| { |
| int ret = bt_adapter_le_scan_filter_create(&mScanFilter); |
| VerifyOrExit(ret == BT_ERROR_NONE, |
| ChipLogError(DeviceLayer, "bt_adapter_le_scan_filter_create() failed: %s", get_error_message(ret))); |
| ChipLogProgress(DeviceLayer, "BLE scan filter created successfully"); |
| exit: |
| return ret; |
| } |
| |
| } // namespace Internal |
| } // namespace DeviceLayer |
| } // namespace chip |