| /* |
| * |
| * Copyright (c) 2020-2022 Project CHIP Authors |
| * Copyright (c) 2018 Nest Labs, Inc. |
| * |
| * 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 BLEManager singleton object |
| * for webOS platforms. |
| */ |
| #include <platform/internal/CHIPDeviceLayerInternal.h> |
| |
| #include <ble/Ble.h> |
| #include <lib/support/CodeUtils.h> |
| #include <lib/support/SafeInt.h> |
| #include <new> |
| #include <platform/CommissionableDataProvider.h> |
| #include <platform/internal/BLEManager.h> |
| |
| #include <cassert> |
| #include <iomanip> |
| #include <string> |
| #include <type_traits> |
| #include <utility> |
| |
| #include "MainLoop.h" |
| #include <pbnjson.h> |
| #include <pbnjson.hpp> |
| |
| using namespace pbnjson; |
| |
| #if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE |
| |
| using namespace ::nl; |
| using namespace ::chip::Ble; |
| |
| namespace chip { |
| namespace DeviceLayer { |
| namespace Internal { |
| |
| namespace { |
| |
| static constexpr unsigned kNewConnectionScanTimeoutMs = 10000; |
| static constexpr System::Clock::Timeout kConnectTimeout = System::Clock::Seconds16(10); |
| |
| const ChipBleUUID ChipUUID_CHIPoBLEChar_RX = { { 0x18, 0xEE, 0x2E, 0xF5, 0x26, 0x3D, 0x45, 0x59, 0x95, 0x9F, 0x4F, 0x9C, 0x42, 0x9F, |
| 0x9D, 0x11 } }; |
| const ChipBleUUID ChipUUID_CHIPoBLEChar_TX = { { 0x18, 0xEE, 0x2E, 0xF5, 0x26, 0x3D, 0x45, 0x59, 0x95, 0x9F, 0x4F, 0x9C, 0x42, 0x9F, |
| 0x9D, 0x12 } }; |
| #define CHIP_BLE_GATT_SERVICE "0000fff6-0000-1000-8000-00805f9b34fb" |
| #define CHIP_BLE_GATT_CHAR_WRITE "18ee2ef5-263d-4559-959f-4f9c429f9d11" |
| #define CHIP_BLE_GATT_CHAR_READ "18ee2ef5-263d-4559-959f-4f9c429f9d12" |
| |
| } // namespace |
| |
| BLEManagerImpl BLEManagerImpl::sInstance; |
| |
| void BLEManagerImpl::InitConnectionData() |
| { |
| /* Initialize Hashmap */ |
| if (!mConnectionMap) |
| { |
| mConnectionMap = g_hash_table_new(g_str_hash, g_str_equal); |
| ChipLogProgress(DeviceLayer, "GATT Connection HashMap created"); |
| } |
| } |
| |
| gboolean BLEManagerImpl::_BleInitialize(void * userData) |
| { |
| if (sInstance.mFlags.Has(Flags::kWebOSBLELayerInitialized)) |
| { |
| ChipLogProgress(DeviceLayer, "BLE Already Initialized"); |
| return true; |
| } |
| |
| sInstance.InitConnectionData(); |
| |
| // Should add BT callback |
| |
| sInstance.mFlags.Set(Flags::kWebOSBLELayerInitialized); |
| ChipLogProgress(DeviceLayer, "BLE Initialized"); |
| sInstance.mMainContext = g_main_context_get_thread_default(); |
| return true; |
| } |
| |
| CHIP_ERROR BLEManagerImpl::_Init() |
| { |
| CHIP_ERROR err; |
| bool ret; |
| |
| err = BleLayer::Init(this, this, this, &DeviceLayer::SystemLayer()); |
| SuccessOrExit(err); |
| |
| mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Enabled; |
| mFlags.ClearAll().Set(Flags::kAdvertisingEnabled, CHIP_DEVICE_CONFIG_CHIPOBLE_ENABLE_ADVERTISING_AUTOSTART && !mIsCentral); |
| mFlags.Set(Flags::kFastAdvertisingEnabled, true); |
| |
| memset(mDeviceName, 0, sizeof(mDeviceName)); |
| |
| ret = MainLoop::Instance().Init(_BleInitialize); |
| VerifyOrExit(ret != false, err = CHIP_ERROR_INTERNAL); |
| |
| ret = MainLoop::Instance().StartLSMainLoop(); |
| VerifyOrExit(ret != false, err = CHIP_ERROR_INTERNAL); |
| |
| PlatformMgr().ScheduleWork(DriveBLEState, 0); |
| |
| exit: |
| return err; |
| } |
| |
| void BLEManagerImpl::_Shutdown() |
| { |
| // ensure scan resources are cleared (e.g. timeout timers) |
| mDeviceScanner.reset(); |
| } |
| |
| CHIP_ERROR BLEManagerImpl::_SetAdvertisingEnabled(bool val) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| if (mFlags.Has(Flags::kAdvertisingEnabled) != val) |
| { |
| mFlags.Set(Flags::kAdvertisingEnabled, val); |
| } |
| |
| PlatformMgr().ScheduleWork(DriveBLEState, 0); |
| |
| return err; |
| } |
| |
| CHIP_ERROR BLEManagerImpl::_SetAdvertisingMode(BLEAdvertisingMode mode) |
| { |
| switch (mode) |
| { |
| case BLEAdvertisingMode::kFastAdvertising: |
| mFlags.Set(Flags::kFastAdvertisingEnabled, true); |
| break; |
| case BLEAdvertisingMode::kSlowAdvertising: |
| mFlags.Set(Flags::kFastAdvertisingEnabled, false); |
| break; |
| default: |
| return CHIP_ERROR_INVALID_ARGUMENT; |
| } |
| mFlags.Set(Flags::kAdvertisingRefreshNeeded); |
| PlatformMgr().ScheduleWork(DriveBLEState, 0); |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR BLEManagerImpl::_GetDeviceName(char * buf, size_t bufSize) |
| { |
| if (strlen(mDeviceName) >= bufSize) |
| { |
| return CHIP_ERROR_BUFFER_TOO_SMALL; |
| } |
| strcpy(buf, mDeviceName); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR BLEManagerImpl::_SetDeviceName(const char * deviceName) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| VerifyOrExit(mServiceMode != ConnectivityManager::kCHIPoBLEServiceMode_NotSupported, err = CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); |
| |
| if (deviceName != nullptr && deviceName[0] != 0) |
| { |
| VerifyOrExit(strlen(deviceName) < kMaxDeviceNameLength, err = CHIP_ERROR_INVALID_ARGUMENT); |
| strcpy(mDeviceName, deviceName); |
| mFlags.Set(Flags::kUseCustomDeviceName); |
| } |
| else |
| { |
| uint16_t discriminator; |
| SuccessOrExit(err = GetCommissionableDataProvider()->GetSetupDiscriminator(discriminator)); |
| snprintf(mDeviceName, sizeof(mDeviceName), "%s%04u", CHIP_DEVICE_CONFIG_BLE_DEVICE_NAME_PREFIX, discriminator); |
| mDeviceName[kMaxDeviceNameLength] = 0; |
| mFlags.Clear(Flags::kUseCustomDeviceName); |
| } |
| |
| exit: |
| return err; |
| } |
| |
| uint16_t BLEManagerImpl::_NumConnections() |
| { |
| uint16_t numCons = 0; |
| return numCons; |
| } |
| |
| CHIP_ERROR BLEManagerImpl::ConfigureBle(uint32_t aAdapterId, bool aIsCentral) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| mIsCentral = aIsCentral; |
| |
| return err; |
| } |
| |
| CHIP_ERROR BLEManagerImpl::StartBLEAdvertising() |
| { |
| return CHIP_ERROR_NOT_IMPLEMENTED; |
| } |
| |
| CHIP_ERROR BLEManagerImpl::StopBLEAdvertising() |
| { |
| return CHIP_ERROR_NOT_IMPLEMENTED; |
| } |
| |
| void BLEManagerImpl::_OnPlatformEvent(const ChipDeviceEvent * event) |
| { |
| switch (event->Type) |
| { |
| case DeviceEventType::kCHIPoBLESubscribe: |
| HandleSubscribeReceived(event->CHIPoBLESubscribe.ConId, &CHIP_BLE_SVC_ID, &ChipUUID_CHIPoBLEChar_TX); |
| { |
| ChipDeviceEvent connectionEvent; |
| connectionEvent.Type = DeviceEventType::kCHIPoBLEConnectionEstablished; |
| PlatformMgr().PostEventOrDie(&connectionEvent); |
| } |
| break; |
| |
| case DeviceEventType::kCHIPoBLEUnsubscribe: |
| HandleUnsubscribeReceived(event->CHIPoBLEUnsubscribe.ConId, &CHIP_BLE_SVC_ID, &ChipUUID_CHIPoBLEChar_TX); |
| break; |
| |
| case DeviceEventType::kCHIPoBLEWriteReceived: |
| HandleWriteReceived(event->CHIPoBLEWriteReceived.ConId, &CHIP_BLE_SVC_ID, &ChipUUID_CHIPoBLEChar_RX, |
| PacketBufferHandle::Adopt(event->CHIPoBLEWriteReceived.Data)); |
| break; |
| |
| case DeviceEventType::kCHIPoBLEIndicateConfirm: |
| HandleIndicationConfirmation(event->CHIPoBLEIndicateConfirm.ConId, &CHIP_BLE_SVC_ID, &ChipUUID_CHIPoBLEChar_TX); |
| break; |
| |
| case DeviceEventType::kCHIPoBLEConnectionError: |
| HandleConnectionError(event->CHIPoBLEConnectionError.ConId, event->CHIPoBLEConnectionError.Reason); |
| break; |
| case DeviceEventType::kServiceProvisioningChange: |
| // Force the advertising configuration to be refreshed to reflect new provisioning state. |
| mFlags.Clear(Flags::kAdvertisingConfigured); |
| |
| DriveBLEState(); |
| break; |
| default: |
| HandlePlatformSpecificBLEEvent(event); |
| break; |
| } |
| } |
| |
| void BLEManagerImpl::HandlePlatformSpecificBLEEvent(const ChipDeviceEvent * apEvent) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| bool controlOpComplete = false; |
| ChipLogDetail(DeviceLayer, "HandlePlatformSpecificBLEEvent %d", apEvent->Type); |
| switch (apEvent->Type) |
| { |
| case DeviceEventType::kPlatformWebOSBLECentralConnected: |
| if (mBLEScanConfig.mBleScanState == BleScanState::kConnecting) |
| { |
| BleConnectionDelegate::OnConnectionComplete(mBLEScanConfig.mAppState, |
| apEvent->Platform.BLECentralConnected.mConnection); |
| CleanScanConfig(); |
| } |
| break; |
| case DeviceEventType::kPlatformWebOSBLECentralConnectFailed: |
| if (mBLEScanConfig.mBleScanState == BleScanState::kConnecting) |
| { |
| BleConnectionDelegate::OnConnectionError(mBLEScanConfig.mAppState, apEvent->Platform.BLECentralConnectFailed.mError); |
| CleanScanConfig(); |
| } |
| break; |
| case DeviceEventType::kPlatformWebOSBLEWriteComplete: |
| HandleWriteConfirmation(apEvent->Platform.BLEWriteComplete.mConnection, &CHIP_BLE_SVC_ID, &ChipUUID_CHIPoBLEChar_RX); |
| break; |
| case DeviceEventType::kPlatformWebOSBLESubscribeOpComplete: |
| if (apEvent->Platform.BLESubscribeOpComplete.mIsSubscribed) |
| HandleSubscribeComplete(apEvent->Platform.BLESubscribeOpComplete.mConnection, &CHIP_BLE_SVC_ID, |
| &ChipUUID_CHIPoBLEChar_TX); |
| else |
| HandleUnsubscribeComplete(apEvent->Platform.BLESubscribeOpComplete.mConnection, &CHIP_BLE_SVC_ID, |
| &ChipUUID_CHIPoBLEChar_TX); |
| break; |
| case DeviceEventType::kPlatformWebOSBLEIndicationReceived: |
| HandleIndicationReceived(apEvent->Platform.BLEIndicationReceived.mConnection, &CHIP_BLE_SVC_ID, &ChipUUID_CHIPoBLEChar_TX, |
| PacketBufferHandle::Adopt(apEvent->Platform.BLEIndicationReceived.mData)); |
| break; |
| case DeviceEventType::kPlatformWebOSBLEPeripheralAdvConfiguredComplete: |
| VerifyOrExit(apEvent->Platform.BLEPeripheralAdvConfiguredComplete.mIsSuccess, err = CHIP_ERROR_INCORRECT_STATE); |
| sInstance.mFlags.Set(Flags::kAdvertisingConfigured).Clear(Flags::kControlOpInProgress); |
| controlOpComplete = true; |
| ChipLogProgress(DeviceLayer, "CHIPoBLE advertising config complete"); |
| break; |
| case DeviceEventType::kPlatformWebOSBLEPeripheralAdvStartComplete: |
| VerifyOrExit(apEvent->Platform.BLEPeripheralAdvStartComplete.mIsSuccess, err = CHIP_ERROR_INCORRECT_STATE); |
| sInstance.mFlags.Clear(Flags::kControlOpInProgress).Clear(Flags::kAdvertisingRefreshNeeded); |
| |
| if (!sInstance.mFlags.Has(Flags::kAdvertising)) |
| { |
| sInstance.mFlags.Set(Flags::kAdvertising); |
| } |
| |
| break; |
| case DeviceEventType::kPlatformWebOSBLEPeripheralAdvStopComplete: |
| VerifyOrExit(apEvent->Platform.BLEPeripheralAdvStopComplete.mIsSuccess, err = CHIP_ERROR_INCORRECT_STATE); |
| |
| sInstance.mFlags.Clear(Flags::kControlOpInProgress).Clear(Flags::kAdvertisingRefreshNeeded); |
| |
| // Transition to the not Advertising state... |
| if (sInstance.mFlags.Has(Flags::kAdvertising)) |
| { |
| sInstance.mFlags.Clear(Flags::kAdvertising); |
| ChipLogProgress(DeviceLayer, "CHIPoBLE advertising stopped"); |
| } |
| break; |
| case DeviceEventType::kPlatformWebOSBLEPeripheralRegisterAppComplete: |
| VerifyOrExit(apEvent->Platform.BLEPeripheralRegisterAppComplete.mIsSuccess, err = CHIP_ERROR_INCORRECT_STATE); |
| mFlags.Set(Flags::kAppRegistered); |
| controlOpComplete = true; |
| break; |
| default: |
| break; |
| } |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(DeviceLayer, "Disabling CHIPoBLE service due to error: %s", ErrorStr(err)); |
| mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Disabled; |
| sInstance.mFlags.Clear(Flags::kControlOpInProgress); |
| } |
| |
| if (controlOpComplete) |
| { |
| mFlags.Clear(Flags::kControlOpInProgress); |
| DriveBLEState(); |
| } |
| } |
| |
| uint16_t BLEManagerImpl::GetMTU(BLE_CONNECTION_OBJECT conId) const |
| { |
| return 20; |
| } |
| |
| bool BLEManagerImpl::gattMonitorCharateristicsCb(LSHandle * sh, LSMessage * message, void * userData) |
| { |
| BLEConnection * conn = nullptr; |
| conn = (BLEConnection *) userData; |
| |
| jvalue_ref parsedObj = { 0 }; |
| jschema_ref input_schema = jschema_parse(j_cstr_to_buffer("{}"), DOMOPT_NOOPT, NULL); |
| |
| if (!input_schema) |
| return false; |
| |
| JSchemaInfo schemaInfo; |
| jschema_info_init(&schemaInfo, input_schema, NULL, NULL); |
| parsedObj = jdom_parse(j_cstr_to_buffer(LSMessageGetPayload(message)), DOMOPT_NOOPT, &schemaInfo); |
| jschema_release(&input_schema); |
| |
| if (jis_null(parsedObj)) |
| return true; |
| |
| const char * payload = jvalue_tostring(parsedObj, input_schema); |
| |
| pbnjson::JValue jvalue = pbnjson::JDomParser::fromString(std::string(payload)); |
| |
| if (jvalue.hasKey("returnValue")) |
| { |
| ChipLogProgress(DeviceLayer, "gattMonitorCharateristicsCb payload returnValue is %d", jvalue["returnValue"].asBool()); |
| |
| if (jvalue["changed"]["value"].hasKey("bytes") == true) |
| { |
| ChipLogProgress(DeviceLayer, "received read value is %s", jvalue["changed"]["value"].stringify().c_str()); |
| |
| pbnjson::JValue value = jvalue["changed"]["value"]; |
| |
| uint8_t * values = (uint8_t *) malloc(sizeof(uint8_t) * value["bytes"].arraySize()); |
| |
| for (int i = 0; i < value["bytes"].arraySize(); i++) |
| { |
| values[i] = value["bytes"][i].asNumber<int32_t>(); |
| } |
| |
| sInstance.HandleTXCharChanged(conn, values, value["bytes"].arraySize()); |
| } |
| } |
| |
| return true; |
| } |
| |
| bool BLEManagerImpl::gattWriteDescriptorValueCb(LSHandle * sh, LSMessage * message, void * userData) |
| { |
| BLEConnection * conn = nullptr; |
| conn = (BLEConnection *) userData; |
| |
| jvalue_ref parsedObj = { 0 }; |
| jschema_ref input_schema = jschema_parse(j_cstr_to_buffer("{}"), DOMOPT_NOOPT, NULL); |
| |
| if (!input_schema) |
| return false; |
| |
| JSchemaInfo schemaInfo; |
| jschema_info_init(&schemaInfo, input_schema, NULL, NULL); |
| parsedObj = jdom_parse(j_cstr_to_buffer(LSMessageGetPayload(message)), DOMOPT_NOOPT, &schemaInfo); |
| jschema_release(&input_schema); |
| |
| if (jis_null(parsedObj)) |
| return false; |
| |
| const char * payload = jvalue_tostring(parsedObj, input_schema); |
| |
| ChipLogProgress(DeviceLayer, "gattWriteDescriptorValueCb payload is %s", payload); |
| sInstance.HandleSubscribeOpComplete(conn, true); |
| |
| return true; |
| } |
| |
| bool BLEManagerImpl::SubscribeCharacteristicToWebOS(void * bleConnObj, const uint8_t * svcId, const uint8_t * charId) |
| { |
| pbnjson::JValue valueForMonitor = pbnjson::JObject(); |
| |
| valueForMonitor.put("clientId", std::string(mClientId)); |
| valueForMonitor.put("service", std::string(CHIP_BLE_GATT_SERVICE)); |
| pbnjson::JValue bytesJArray = pbnjson::JArray(); |
| bytesJArray.append(std::string(CHIP_BLE_GATT_CHAR_READ)); |
| |
| valueForMonitor.put("characteristics", bytesJArray); |
| valueForMonitor.put("subscribe", true); |
| |
| int ret = 0; |
| |
| if (mLSHandle == nullptr) |
| { |
| ChipLogError(DeviceLayer, "LS handle is null"); |
| return false; |
| } |
| |
| ret = LSCall(mLSHandle, "luna://com.webos.service.bluetooth2/gatt/monitorCharacteristics", valueForMonitor.stringify().c_str(), |
| gattMonitorCharateristicsCb, bleConnObj, NULL, NULL); |
| |
| sleep(2); |
| |
| pbnjson::JValue valueForDescriptor = pbnjson::JObject(); |
| valueForDescriptor.put("clientId", std::string(mClientId)); |
| valueForDescriptor.put("service", std::string(CHIP_BLE_GATT_SERVICE)); |
| valueForDescriptor.put("characteristic", std::string(CHIP_BLE_GATT_CHAR_READ)); |
| |
| valueForDescriptor.put("descriptor", std::string("00002902-0000-1000-8000-00805f9b34fb")); |
| |
| bool subscribe = true; // current is true. |
| |
| pbnjson::JValue valueParam = pbnjson::JObject(); |
| pbnjson::JValue bytesForDescriptorJArray = pbnjson::JArray(); // "bytes": [ ] |
| if (subscribe) |
| { |
| bytesForDescriptorJArray.append(2); |
| bytesForDescriptorJArray.append(0); |
| } |
| else |
| { |
| bytesForDescriptorJArray.append(0); |
| bytesForDescriptorJArray.append(0); |
| } |
| |
| valueParam.put("bytes", bytesForDescriptorJArray); |
| valueForDescriptor.put("value", valueParam); |
| |
| ChipLogProgress(Ble, "SubscribeCharacteristicToWebOS Param : valueForDescriptor %s", valueForDescriptor.stringify().c_str()); |
| |
| ret = LSCall(mLSHandle, "luna://com.webos.service.bluetooth2/gatt/writeDescriptorValue", valueForDescriptor.stringify().c_str(), |
| gattWriteDescriptorValueCb, bleConnObj, NULL, NULL); |
| |
| if (ret != 1) |
| return false; |
| |
| return true; |
| } |
| |
| bool BLEManagerImpl::SubscribeCharacteristic(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId) |
| { |
| bool result = false; |
| result = SubscribeCharacteristicToWebOS(conId, static_cast<const uint8_t *>(svcId->bytes), |
| static_cast<const uint8_t *>(charId->bytes)); |
| return result; |
| } |
| |
| bool BLEManagerImpl::UnsubscribeCharacteristic(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId) |
| { |
| ChipLogError(Ble, "UnsubscribeCharacteristic: Not implemented"); |
| return true; |
| } |
| |
| bool BLEManagerImpl::CloseConnection(BLE_CONNECTION_OBJECT conId) |
| { |
| ChipLogError(Ble, "CloseConnection: Not implemented"); |
| return true; |
| } |
| |
| bool BLEManagerImpl::SendIndication(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const Ble::ChipBleUUID * charId, |
| chip::System::PacketBufferHandle pBuf) |
| { |
| ChipLogError(Ble, "SendIndication: Not implemented"); |
| return true; |
| } |
| |
| bool BLEManagerImpl::gattWriteValueCb(LSHandle * sh, LSMessage * message, void * userData) |
| { |
| BLEConnection * conn = nullptr; |
| conn = (BLEConnection *) userData; |
| |
| jvalue_ref parsedObj = { 0 }; |
| jschema_ref input_schema = jschema_parse(j_cstr_to_buffer("{}"), DOMOPT_NOOPT, NULL); |
| |
| if (!input_schema) |
| return false; |
| |
| JSchemaInfo schemaInfo; |
| jschema_info_init(&schemaInfo, input_schema, NULL, NULL); |
| parsedObj = jdom_parse(j_cstr_to_buffer(LSMessageGetPayload(message)), DOMOPT_NOOPT, &schemaInfo); |
| jschema_release(&input_schema); |
| |
| if (jis_null(parsedObj)) |
| return true; |
| |
| const char * payload = jvalue_tostring(parsedObj, input_schema); |
| |
| ChipLogProgress(DeviceLayer, "gattWriteValueCb payload is %s", payload); |
| sInstance.HandleWriteComplete(conn); |
| |
| return true; |
| } |
| |
| bool BLEManagerImpl::SendWriteRequestToWebOS(void * bleConnObj, const uint8_t * svcId, const uint8_t * charId, const uint8_t * pBuf, |
| uint32_t pBufDataLen) |
| { |
| BLEConnection * conn = (BLEConnection *) bleConnObj; |
| std::ostringstream cvt; |
| |
| for (int i = 0; i < (int) pBufDataLen; i++) |
| { |
| cvt << std::hex << std::setfill('0') << std::setw(2) << (int) pBuf[i]; |
| } |
| |
| pbnjson::JValue values = pbnjson::JObject(); |
| values.put("clientId", std::string(mClientId)); |
| values.put("data", cvt.str().c_str()); |
| |
| ChipLogProgress(Ble, "SendWriteRequestToWebOS Param : value %s", values.stringify().c_str()); |
| |
| std::string clientId = values["clientId"].asString(); |
| std::string valueType = "bytes"; |
| std::string value = values["data"].asString(); |
| |
| pbnjson::JValue param = pbnjson::JObject(); |
| pbnjson::JValue valueParam = pbnjson::JObject(); |
| param.put("clientId", clientId); |
| param.put("service", std::string(CHIP_BLE_GATT_SERVICE)); |
| param.put("characteristic", std::string(CHIP_BLE_GATT_CHAR_WRITE)); |
| |
| if (valueType == "byte") |
| { |
| valueParam.put("bytes", pbnjson::JArray{ std::stoi(value) }); |
| } |
| else if (valueType == "bytes") |
| { |
| pbnjson::JValue bytesJArray = JArray(); // "bytes": [ ] |
| std::vector<char> bytes; |
| for (int i = 0; i < (int) value.length(); i += 2) |
| { |
| std::string byteString = value.substr(i, 2); |
| char c = (char) strtol(byteString.c_str(), NULL, 16); |
| bytesJArray.append(c); |
| } |
| valueParam.put("bytes", bytesJArray); |
| } |
| else if (valueType == "string") |
| { |
| valueParam.put("string", value); |
| } |
| else |
| { |
| } |
| param.put("value", valueParam); |
| |
| ChipLogProgress(Ble, "SendWriteRequestToWebOS Param : param %s", param.stringify().c_str()); |
| |
| int ret = 0; |
| |
| if (mLSHandle == nullptr) |
| { |
| ChipLogError(DeviceLayer, "LS handle is null"); |
| return false; |
| } |
| |
| ret = LSCall(mLSHandle, "luna://com.webos.service.bluetooth2/gatt/writeCharacteristicValue", param.stringify().c_str(), |
| gattWriteValueCb, conn, NULL, NULL); |
| |
| VerifyOrExit(ret == 1, ChipLogError(DeviceLayer, "Failed to write characteristic . ret [%d]", ret)); |
| |
| exit: |
| if (ret != 1) |
| return false; |
| |
| return true; |
| } |
| |
| bool BLEManagerImpl::SendWriteRequest(BLE_CONNECTION_OBJECT conId, const Ble::ChipBleUUID * svcId, const Ble::ChipBleUUID * charId, |
| chip::System::PacketBufferHandle pBuf) |
| { |
| bool result = false; |
| result = SendWriteRequestToWebOS(conId, static_cast<const uint8_t *>(svcId->bytes), static_cast<const uint8_t *>(charId->bytes), |
| pBuf->Start(), pBuf->DataLength()); |
| |
| return result; |
| } |
| |
| bool BLEManagerImpl::SendReadRequest(BLE_CONNECTION_OBJECT conId, const Ble::ChipBleUUID * svcId, const Ble::ChipBleUUID * charId, |
| chip::System::PacketBufferHandle pBuf) |
| { |
| ChipLogError(Ble, "SendReadRequest: Not implemented"); |
| return true; |
| } |
| |
| bool BLEManagerImpl::SendReadResponse(BLE_CONNECTION_OBJECT conId, BLE_READ_REQUEST_CONTEXT requestContext, |
| const Ble::ChipBleUUID * svcId, const Ble::ChipBleUUID * charId) |
| { |
| ChipLogError(Ble, "SendReadRBluezonse: Not implemented"); |
| return true; |
| } |
| |
| void BLEManagerImpl::AddConnectionData(const char * remoteAddr) |
| { |
| BLEConnection * conn; |
| ChipLogProgress(DeviceLayer, "AddConnectionData for [%s]", remoteAddr); |
| |
| if (!g_hash_table_lookup(mConnectionMap, remoteAddr)) |
| { |
| ChipLogProgress(DeviceLayer, "Not Found in Map"); |
| conn = (BLEConnection *) g_malloc0(sizeof(BLEConnection)); |
| conn->peerAddr = g_strdup(remoteAddr); |
| |
| if (sInstance.mIsCentral) |
| { |
| g_hash_table_insert(mConnectionMap, (gpointer) conn->peerAddr, conn); |
| ChipLogProgress(DeviceLayer, "New Connection Added for [%s]", remoteAddr); |
| HandleNewConnection(conn); |
| } |
| else |
| { |
| /* Local Device is BLE Peripheral Role, assume remote is CHIP Central */ |
| conn->isChipDevice = true; |
| g_hash_table_insert(mConnectionMap, (gpointer) conn->peerAddr, conn); |
| ChipLogProgress(DeviceLayer, "New Connection Added for [%s]", remoteAddr); |
| } |
| } |
| } |
| |
| void BLEManagerImpl::HandleConnectionEvent(bool connected, const char * remoteAddress) |
| { |
| if (connected) |
| { |
| ChipLogProgress(DeviceLayer, "Device Connected [%s]", remoteAddress); |
| AddConnectionData(remoteAddress); |
| } |
| else |
| { |
| ChipLogProgress(DeviceLayer, "Device DisConnected [%s]", remoteAddress); |
| } |
| } |
| |
| void BLEManagerImpl::HandleNewConnection(BLE_CONNECTION_OBJECT conId) |
| { |
| if (sInstance.mIsCentral) |
| { |
| ChipDeviceEvent event; |
| event.Type = DeviceEventType::kPlatformWebOSBLECentralConnected; |
| event.Platform.BLECentralConnected.mConnection = conId; |
| PlatformMgr().PostEventOrDie(&event); |
| } |
| } |
| |
| void BLEManagerImpl::HandleConnectFailed(CHIP_ERROR error) |
| { |
| if (sInstance.mIsCentral) |
| { |
| ChipDeviceEvent event; |
| event.Type = DeviceEventType::kPlatformWebOSBLECentralConnectFailed; |
| event.Platform.BLECentralConnectFailed.mError = error; |
| PlatformMgr().PostEventOrDie(&event); |
| } |
| } |
| |
| void BLEManagerImpl::HandleWriteComplete(BLE_CONNECTION_OBJECT conId) |
| { |
| ChipDeviceEvent event; |
| event.Type = DeviceEventType::kPlatformWebOSBLEWriteComplete; |
| event.Platform.BLEWriteComplete.mConnection = conId; |
| PlatformMgr().PostEventOrDie(&event); |
| } |
| |
| void BLEManagerImpl::HandleSubscribeOpComplete(BLE_CONNECTION_OBJECT conId, bool subscribed) |
| { |
| ChipDeviceEvent event; |
| event.Type = DeviceEventType::kPlatformWebOSBLESubscribeOpComplete; |
| event.Platform.BLESubscribeOpComplete.mConnection = conId; |
| event.Platform.BLESubscribeOpComplete.mIsSubscribed = subscribed; |
| PlatformMgr().PostEventOrDie(&event); |
| } |
| |
| void BLEManagerImpl::HandleTXCharChanged(BLE_CONNECTION_OBJECT conId, const uint8_t * value, size_t len) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| System::PacketBufferHandle buf = System::PacketBufferHandle::NewWithData(value, len); |
| |
| ChipLogDetail(DeviceLayer, "Indication received, conn = %p", conId); |
| |
| VerifyOrExit(!buf.IsNull(), err = CHIP_ERROR_NO_MEMORY); |
| |
| ChipDeviceEvent event; |
| event.Type = DeviceEventType::kPlatformWebOSBLEIndicationReceived; |
| event.Platform.BLEIndicationReceived.mConnection = conId; |
| event.Platform.BLEIndicationReceived.mData = std::move(buf).UnsafeRelease(); |
| PlatformMgr().PostEventOrDie(&event); |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| ChipLogError(DeviceLayer, "HandleTXCharChanged() failed: %s", ErrorStr(err)); |
| } |
| |
| void BLEManagerImpl::HandleRXCharWrite(BLE_CONNECTION_OBJECT conId, const uint8_t * value, size_t len) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| System::PacketBufferHandle buf; |
| |
| // Copy the data to a packet buffer. |
| buf = System::PacketBufferHandle::NewWithData(value, len); |
| VerifyOrExit(!buf.IsNull(), err = CHIP_ERROR_NO_MEMORY); |
| |
| // Post an event to the Chip queue to deliver the data into the Chip stack. |
| { |
| ChipDeviceEvent event; |
| event.Type = DeviceEventType::kCHIPoBLEWriteReceived; |
| ChipLogProgress(Ble, "Write request received debug %p", conId); |
| event.CHIPoBLEWriteReceived.ConId = conId; |
| event.CHIPoBLEWriteReceived.Data = std::move(buf).UnsafeRelease(); |
| PlatformMgr().PostEventOrDie(&event); |
| } |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(DeviceLayer, "HandleRXCharWrite() failed: %s", ErrorStr(err)); |
| } |
| } |
| |
| void BLEManagerImpl::HandleTXCharCCCDWrite(BLE_CONNECTION_OBJECT conId) {} |
| |
| void BLEManagerImpl::HandleTXComplete(BLE_CONNECTION_OBJECT conId) |
| { |
| // Post an event to the Chip queue to process the indicate confirmation. |
| ChipDeviceEvent event; |
| event.Type = DeviceEventType::kCHIPoBLEIndicateConfirm; |
| event.CHIPoBLEIndicateConfirm.ConId = conId; |
| PlatformMgr().PostEventOrDie(&event); |
| } |
| |
| void BLEManagerImpl::DriveBLEState() |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| // Perform any initialization actions that must occur after the Chip task is running. |
| if (!mFlags.Has(Flags::kAsyncInitCompleted)) |
| { |
| mFlags.Set(Flags::kAsyncInitCompleted); |
| ExitNow(); |
| } |
| |
| // If there's already a control operation in progress, wait until it completes. |
| VerifyOrExit(!mFlags.Has(Flags::kControlOpInProgress), /* */); |
| |
| // Initializes the Bluez BLE layer if needed. |
| if (mServiceMode == ConnectivityManager::kCHIPoBLEServiceMode_Enabled && !mFlags.Has(Flags::kWebOSBLELayerInitialized)) |
| { |
| mFlags.Set(Flags::kWebOSBLELayerInitialized); |
| } |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(DeviceLayer, "Disabling CHIPoBLE service due to error: %s", ErrorStr(err)); |
| mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Disabled; |
| } |
| } |
| |
| void BLEManagerImpl::DriveBLEState(intptr_t arg) |
| { |
| sInstance.DriveBLEState(); |
| } |
| |
| void BLEManagerImpl::NotifyChipConnectionClosed(BLE_CONNECTION_OBJECT conId) |
| { |
| ChipLogProgress(Ble, "Got notification regarding chip connection closure"); |
| } |
| |
| void BLEManagerImpl::InitiateScan(BleScanState scanType) |
| { |
| DriveBLEState(); |
| |
| if (scanType == BleScanState::kNotScanning) |
| { |
| BleConnectionDelegate::OnConnectionError(mBLEScanConfig.mAppState, CHIP_ERROR_INCORRECT_STATE); |
| ChipLogError(Ble, "Invalid scan type requested"); |
| return; |
| } |
| |
| mDeviceScanner = Internal::ChipDeviceScanner::Create(this); |
| mBLEScanConfig.mBleScanState = scanType; |
| |
| if (!mDeviceScanner) |
| { |
| mBLEScanConfig.mBleScanState = BleScanState::kNotScanning; |
| BleConnectionDelegate::OnConnectionError(mBLEScanConfig.mAppState, CHIP_ERROR_INTERNAL); |
| ChipLogError(Ble, "Failed to create a BLE device scanner"); |
| return; |
| } |
| |
| CHIP_ERROR err = mDeviceScanner->StartChipScan(kNewConnectionScanTimeoutMs); |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| mBLEScanConfig.mBleScanState = BleScanState::kNotScanning; |
| ChipLogError(Ble, "Failed to start a BLE can: %s", chip::ErrorStr(err)); |
| BleConnectionDelegate::OnConnectionError(mBLEScanConfig.mAppState, err); |
| return; |
| } |
| |
| ChipLogError(DeviceLayer, "BLE Scan Initiation Successful"); |
| } |
| |
| void BLEManagerImpl::CleanScanConfig() |
| { |
| mBLEScanConfig.mBleScanState = BleScanState::kNotScanning; |
| } |
| |
| void BLEManagerImpl::InitiateScan(intptr_t arg) |
| { |
| sInstance.InitiateScan(static_cast<BleScanState>(arg)); |
| } |
| |
| void BLEManagerImpl::NewConnection(BleLayer * bleLayer, void * appState, const SetupDiscriminator & connDiscriminator) |
| { |
| mBLEScanConfig.mDiscriminator = connDiscriminator; |
| mBLEScanConfig.mAppState = appState; |
| |
| // Scan initiation performed async, to ensure that the BLE subsystem is initialized. |
| PlatformMgr().ScheduleWork(InitiateScan, static_cast<intptr_t>(BleScanState::kScanForDiscriminator)); |
| } |
| |
| CHIP_ERROR BLEManagerImpl::CancelConnection() |
| { |
| return CHIP_ERROR_NOT_IMPLEMENTED; |
| } |
| |
| void BLEManagerImpl::NotifyBLEPeripheralRegisterAppComplete(bool aIsSuccess, void * apAppstate) |
| { |
| ChipDeviceEvent event; |
| event.Type = DeviceEventType::kPlatformWebOSBLEPeripheralRegisterAppComplete; |
| event.Platform.BLEPeripheralRegisterAppComplete.mIsSuccess = aIsSuccess; |
| event.Platform.BLEPeripheralRegisterAppComplete.mpAppstate = apAppstate; |
| PlatformMgr().PostEventOrDie(&event); |
| } |
| |
| void BLEManagerImpl::NotifyBLEPeripheralAdvConfiguredComplete(bool aIsSuccess, void * apAppstate) |
| { |
| ChipDeviceEvent event; |
| event.Type = DeviceEventType::kPlatformWebOSBLEPeripheralAdvConfiguredComplete; |
| event.Platform.BLEPeripheralAdvConfiguredComplete.mIsSuccess = aIsSuccess; |
| event.Platform.BLEPeripheralAdvConfiguredComplete.mpAppstate = apAppstate; |
| PlatformMgr().PostEventOrDie(&event); |
| } |
| |
| void BLEManagerImpl::NotifyBLEPeripheralAdvStartComplete(bool aIsSuccess, void * apAppstate) |
| { |
| ChipDeviceEvent event; |
| event.Type = DeviceEventType::kPlatformWebOSBLEPeripheralAdvStartComplete; |
| event.Platform.BLEPeripheralAdvStartComplete.mIsSuccess = aIsSuccess; |
| event.Platform.BLEPeripheralAdvStartComplete.mpAppstate = apAppstate; |
| PlatformMgr().PostEventOrDie(&event); |
| } |
| |
| void BLEManagerImpl::NotifyBLEPeripheralAdvStopComplete(bool aIsSuccess, void * apAppstate) |
| { |
| ChipDeviceEvent event; |
| event.Type = DeviceEventType::kPlatformWebOSBLEPeripheralAdvStopComplete; |
| event.Platform.BLEPeripheralAdvStopComplete.mIsSuccess = aIsSuccess; |
| event.Platform.BLEPeripheralAdvStopComplete.mpAppstate = apAppstate; |
| PlatformMgr().PostEventOrDie(&event); |
| } |
| |
| void BLEManagerImpl::OnScanComplete() |
| { |
| switch (mBLEScanConfig.mBleScanState) |
| { |
| case BleScanState::kNotScanning: |
| ChipLogProgress(Ble, "Scan complete notification without an active scan."); |
| break; |
| case BleScanState::kScanForAddress: |
| case BleScanState::kScanForDiscriminator: |
| mBLEScanConfig.mBleScanState = BleScanState::kNotScanning; |
| ChipLogProgress(Ble, "Scan complete. No matching device found."); |
| break; |
| case BleScanState::kConnecting: |
| break; |
| } |
| } |
| |
| void BLEManagerImpl::OnScanError(CHIP_ERROR err) |
| { |
| ChipLogDetail(Ble, "BLE scan error: %" CHIP_ERROR_FORMAT, err.Format()); |
| } |
| |
| bool BLEManagerImpl::gattGetServiceCb(LSHandle * sh, LSMessage * message, void * userData) |
| { |
| jvalue_ref parsedObj = { 0 }; |
| jschema_ref input_schema = jschema_parse(j_cstr_to_buffer("{}"), DOMOPT_NOOPT, NULL); |
| |
| if (!input_schema) |
| return false; |
| |
| JSchemaInfo schemaInfo; |
| jschema_info_init(&schemaInfo, input_schema, NULL, NULL); |
| parsedObj = jdom_parse(j_cstr_to_buffer(LSMessageGetPayload(message)), DOMOPT_NOOPT, &schemaInfo); |
| jschema_release(&input_schema); |
| |
| if (jis_null(parsedObj)) |
| return true; |
| |
| const char * payload = jvalue_tostring(parsedObj, input_schema); |
| |
| ChipLogProgress(DeviceLayer, "gattGetServiceCb payload is %s", payload); |
| |
| sInstance.HandleConnectionEvent(true, sInstance.mRemoteAddress); |
| |
| return true; |
| } |
| |
| bool BLEManagerImpl::gattConnectCb(LSHandle * sh, LSMessage * message, void * userData) |
| { |
| jvalue_ref parsedObj = { 0 }; |
| jschema_ref input_schema = jschema_parse(j_cstr_to_buffer("{}"), DOMOPT_NOOPT, NULL); |
| |
| if (!input_schema) |
| return false; |
| |
| JSchemaInfo schemaInfo; |
| jschema_info_init(&schemaInfo, input_schema, NULL, NULL); |
| parsedObj = jdom_parse(j_cstr_to_buffer(LSMessageGetPayload(message)), DOMOPT_NOOPT, &schemaInfo); |
| jschema_release(&input_schema); |
| |
| if (jis_null(parsedObj)) |
| return true; |
| |
| const char * payload = jvalue_tostring(parsedObj, input_schema); |
| |
| ChipLogProgress(DeviceLayer, "gattConnectCb payload is %s", payload); |
| |
| jvalue_ref clientIdObj = { 0 }; |
| if (jobject_get_exists(parsedObj, J_CSTR_TO_BUF("clientId"), &clientIdObj)) |
| { |
| raw_buffer clientId_buf = jstring_get(clientIdObj); |
| char * clientId = g_strdup(clientId_buf.m_str); |
| jstring_free_buffer(clientId_buf); |
| |
| printf("clientId : %s \n", clientId); |
| |
| if (sInstance.mClientId != nullptr) |
| { |
| g_free(sInstance.mClientId); |
| } |
| sInstance.mClientId = g_strdup(clientId); |
| |
| g_free(clientId); |
| } |
| |
| jvalue_ref addressObj = { 0 }; |
| if (jobject_get_exists(parsedObj, J_CSTR_TO_BUF("address"), &addressObj)) |
| { |
| raw_buffer address_buf = jstring_get(addressObj); |
| char * address = g_strdup(address_buf.m_str); |
| jstring_free_buffer(address_buf); |
| |
| printf("address : %s \n", address); |
| |
| if (sInstance.mRemoteAddress != nullptr) |
| { |
| g_free(sInstance.mRemoteAddress); |
| } |
| sInstance.mRemoteAddress = g_strdup(address); |
| |
| g_free(address); |
| } |
| |
| if (sInstance.mLSHandle == nullptr) |
| { |
| ChipLogError(DeviceLayer, "LS handle is null"); |
| return false; |
| } |
| sleep(1); |
| char ls2Param[100]; |
| snprintf(ls2Param, 100, "{\"address\":\"%s\"}", sInstance.mRemoteAddress); |
| |
| ChipLogProgress(DeviceLayer, "getService: Addr [%s]", sInstance.mRemoteAddress); |
| |
| int ret = 0; |
| |
| ret = LSCall(sInstance.mLSHandle, "luna://com.webos.service.bluetooth2/gatt/getServices", ls2Param, gattGetServiceCb, NULL, |
| NULL, NULL); |
| |
| VerifyOrExit(ret == 1, ChipLogError(DeviceLayer, "Failed to get GATT service . ret [%d]", ret)); |
| |
| exit: |
| if (ret != 1) |
| sInstance.HandleConnectFailed(CHIP_ERROR_INTERNAL); |
| |
| return true; |
| } |
| |
| gboolean BLEManagerImpl::ConnectChipThing(gpointer userData) |
| { |
| int ret = 0; |
| |
| if (sInstance.mLSHandle == nullptr) |
| { |
| ChipLogError(DeviceLayer, "LS handle is null"); |
| return false; |
| } |
| |
| char * address = (char *) userData; |
| ChipLogProgress(DeviceLayer, "ConnectRequest: Addr [%s]", address); |
| |
| char ls2Param[100]; |
| snprintf(ls2Param, 100, "{\"address\":\"%s\"}", address); |
| |
| ret = LSCall(sInstance.mLSHandle, "luna://com.webos.service.bluetooth2/gatt/connect", ls2Param, gattConnectCb, userData, NULL, |
| NULL); |
| |
| VerifyOrExit(ret == 1, ChipLogError(DeviceLayer, "Failed to create GATT client. ret [%d]", ret)); |
| |
| ChipLogProgress(DeviceLayer, "GATT Connect Issued"); |
| exit: |
| if (ret != 1) |
| sInstance.HandleConnectFailed(CHIP_ERROR_INTERNAL); |
| |
| g_free(address); |
| return G_SOURCE_REMOVE; |
| } |
| |
| void BLEManagerImpl::ConnectHandler(const char * address) |
| { |
| ChipLogProgress(DeviceLayer, "Try to connect New device scanned: %s", address); |
| |
| mLSHandle = MainLoop::Instance().mLSHandle; |
| |
| if (mLSHandle == nullptr) |
| { |
| ChipLogError(DeviceLayer, "LS handle is null"); |
| return; |
| } |
| |
| GSource * idleSource; |
| |
| idleSource = g_idle_source_new(); |
| g_source_set_callback(idleSource, ConnectChipThing, g_strdup(address), nullptr); |
| g_source_set_priority(idleSource, G_PRIORITY_HIGH_IDLE); |
| g_source_attach(idleSource, sInstance.mMainContext); |
| g_source_unref(idleSource); |
| } |
| |
| void BLEManagerImpl::OnChipDeviceScanned(char * address) |
| { |
| ChipLogProgress(DeviceLayer, "New device scanned: %s", address); |
| |
| if (mBLEScanConfig.mBleScanState == BleScanState::kScanForDiscriminator) |
| { |
| ChipLogProgress(DeviceLayer, "Device discriminator match. Attempting to connect."); |
| } |
| else if (mBLEScanConfig.mBleScanState == BleScanState::kScanForAddress) |
| { |
| if (strcmp(address, "e4:5f:01:20:49:c0") == 0) |
| { |
| ChipLogProgress(DeviceLayer, "SSH Ignore : %s", address); |
| return; |
| } |
| ChipLogProgress(DeviceLayer, "Device address match. Attempting to connect."); |
| } |
| else |
| { |
| ChipLogError(DeviceLayer, "Unknown discovery type. Ignoring scanned device."); |
| return; |
| } |
| |
| /* Set CHIP Connecting state */ |
| mBLEScanConfig.mBleScanState = BleScanState::kConnecting; |
| mDeviceScanner->StopChipScan(); |
| |
| /* Initiate Connect */ |
| ConnectHandler(address); |
| } |
| |
| } // namespace Internal |
| } // namespace DeviceLayer |
| } // namespace chip |
| |
| #endif // CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE |