blob: 3ea67b6ad1c486c7c6c0f2f7b90900509253a533 [file] [log] [blame]
/*
*
* 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/CHIPBleServiceData.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 <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 HandleIncomingBleConnection(BLEEndPoint * bleEP)
{
ChipLogProgress(DeviceLayer, "con rcvd");
}
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));
OnChipBleConnectReceived = HandleIncomingBleConnection;
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