blob: 5808b4bf482b61b740d08eacd50dfc0987676b5a [file] [log] [blame]
/*
*
* Copyright (c) 2022 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.
*/
#include "ChipDeviceScanner.h"
#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
#include <errno.h>
#include <lib/support/logging/CHIPLogging.h>
#include <pthread.h>
#include "MainLoop.h"
#include <pbnjson.h>
#include <string>
namespace chip {
namespace DeviceLayer {
namespace Internal {
namespace {
static unsigned int kScanTimeout = 10000;
// Default CHIP Scan Timeout in Millisecond
// static unsigned int kScanTimeout = 10000;
struct GObjectUnref
{
template <typename T>
void operator()(T * value)
{
g_object_unref(value);
}
};
using GCancellableUniquePtr = std::unique_ptr<GCancellable, GObjectUnref>;
using GDBusObjectManagerUniquePtr = std::unique_ptr<GDBusObjectManager, GObjectUnref>;
} // namespace
ChipDeviceScanner::ChipDeviceScanner(LSHandle * handle, ChipDeviceScannerDelegate * delegate) :
mLSHandle(handle), mDelegate(delegate)
{}
ChipDeviceScanner::ChipDeviceScanner(ChipDeviceScannerDelegate * delegate) : mDelegate(delegate) {}
ChipDeviceScanner::~ChipDeviceScanner()
{
StopScan();
// In case the timeout timer is still active
chip::DeviceLayer::SystemLayer().CancelTimer(TimerExpiredCallback, this);
mDelegate = nullptr;
}
std::unique_ptr<ChipDeviceScanner> ChipDeviceScanner::Create(LSHandle * handle, ChipDeviceScannerDelegate * delegate)
{
if (!LSCall(handle, "palm://com.webos.service.bluetooth2/adapter/getStatus", "{}", NULL, NULL, NULL, NULL))
{
g_print("Failed to call getStatus LSCall\n");
}
return std::make_unique<ChipDeviceScanner>(handle, delegate);
}
std::unique_ptr<ChipDeviceScanner> ChipDeviceScanner::Create(ChipDeviceScannerDelegate * delegate)
{
return std::make_unique<ChipDeviceScanner>(delegate);
}
gboolean ChipDeviceScanner::TimerExpiredCb(gpointer userData)
{
ChipDeviceScanner * self = (ChipDeviceScanner *) userData;
ChipLogProgress(DeviceLayer, "Scan Timer expired!!");
self->StopChipScan();
return G_SOURCE_REMOVE;
}
void ChipDeviceScanner::printFoundChipDevice(const jvalue_ref & scanRecord, const std::string & address)
{
int j = 0;
int scanRecordLength = jarray_size(scanRecord);
printf("printFoundChipDevice start : scanRecoredLength : %d \n", scanRecordLength);
while (j < scanRecordLength)
{
int32_t l = -1;
int32_t t = -1;
int32_t v0 = -1;
int32_t v1 = -1;
jvalue_ref lObj = jarray_get(scanRecord, j);
jvalue_ref tObj = jarray_get(scanRecord, j + 1);
jvalue_ref v0Obj = jarray_get(scanRecord, j + 2);
jvalue_ref v1Obj = jarray_get(scanRecord, j + 3);
jnumber_get_i32(lObj, &l);
jnumber_get_i32(tObj, &t);
jnumber_get_i32(v0Obj, &v0);
jnumber_get_i32(v1Obj, &v1);
if (t == 22 && v0 == 175 && v1 == 254)
{ // 22 = 0x16 175 = 0xAF, 254 = 0xFE
int32_t disc1 = -1;
int32_t disc2 = -1;
jvalue_ref disc1Obj = jarray_get(scanRecord, j + 5);
jvalue_ref disc2Obj = jarray_get(scanRecord, j + 6);
jnumber_get_i32(disc1Obj, &disc1);
jnumber_get_i32(disc2Obj, &disc2);
// uint16_t discriminator = (disc2 << 8) | disc1;
int32_t vid1 = -1;
int32_t vid2 = -1;
int32_t pid1 = -1;
int32_t pid2 = -1;
jvalue_ref vid1Obj = jarray_get(scanRecord, j + 7);
jvalue_ref vid2Obj = jarray_get(scanRecord, j + 8);
jvalue_ref pid1Obj = jarray_get(scanRecord, j + 9);
jvalue_ref pid2Obj = jarray_get(scanRecord, j + 10);
jnumber_get_i32(vid1Obj, &vid1);
jnumber_get_i32(vid2Obj, &vid2);
jnumber_get_i32(pid1Obj, &pid1);
jnumber_get_i32(pid2Obj, &pid2);
// int32_t vid = (vid2 << 8) | vid1;
// int32_t pid = (pid2 << 8) | pid1;
return;
}
j += l + 1;
}
}
bool ChipDeviceScanner::deviceGetstatusCb(LSHandle * sh, LSMessage * message, void * userData)
{
ChipDeviceScanner * self = (ChipDeviceScanner *) 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;
jvalue_ref devicesObj = { 0 };
if (jobject_get_exists(parsedObj, J_CSTR_TO_BUF("devices"), &devicesObj))
{
int devicesLength = jarray_size(devicesObj);
int i = 0;
while (i < devicesLength)
{
jvalue_ref devicesElementObj = jarray_get(devicesObj, i);
jvalue_ref manufacturerDataObj = { 0 };
if (jobject_get_exists(devicesElementObj, J_CSTR_TO_BUF("manufacturerData"), &manufacturerDataObj))
{
jvalue_ref scanRecordObj = { 0 };
if (jobject_get_exists(manufacturerDataObj, J_CSTR_TO_BUF("scanRecord"), &scanRecordObj))
{
int scanRecordLength = jarray_size(scanRecordObj);
int j = 0;
while (j < scanRecordLength)
{
jvalue_ref scanRecordElementObj = jarray_get(scanRecordObj, j);
int32_t scanRecordElement = -1;
jnumber_get_i32(scanRecordElementObj, &scanRecordElement);
if (scanRecordElement == 3)
{
int32_t firstByte = -1;
int32_t secondByte = -1;
int32_t thirdByte = -1;
jvalue_ref firstByteObj = jarray_get(scanRecordObj, j + 1);
jvalue_ref secondByteObj = jarray_get(scanRecordObj, j + 2);
jvalue_ref thirdByteObj = jarray_get(scanRecordObj, j + 3);
jnumber_get_i32(firstByteObj, &firstByte);
jnumber_get_i32(secondByteObj, &secondByte);
jnumber_get_i32(thirdByteObj, &thirdByte);
if (firstByte == 3 && secondByte == 246 && thirdByte == 255)
{
jvalue_ref addressObj = { 0 };
if (jobject_get_exists(devicesElementObj, 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);
printFoundChipDevice(scanRecordObj, address);
self->mDelegate->OnChipDeviceScanned(address);
}
jvalue_ref nameObj = { 0 };
if (jobject_get_exists(devicesElementObj, J_CSTR_TO_BUF("name"), &nameObj))
{
raw_buffer name_buf = jstring_get(nameObj);
char * name = g_strdup(name_buf.m_str);
jstring_free_buffer(name_buf);
printf("name : %s \n", name);
}
break;
}
else
{
break;
}
}
j = j + scanRecordElement + 1;
}
}
}
i = i + 1;
}
}
return true;
}
bool ChipDeviceScanner::startDiscoveryCb(LSHandle * sh, LSMessage * message, void * userData)
{
return true;
}
gboolean ChipDeviceScanner::TriggerScan(GMainLoop * mainLoop, gpointer userData)
{
ChipDeviceScanner * self = (ChipDeviceScanner *) userData;
int ret = 0;
GSource * idleSource;
self->mAsyncLoop = mainLoop;
ret = LSCall(self->mLSHandle, "luna://com.webos.service.bluetooth2/adapter/internal/startDiscovery",
"{\"typeOfDevice\":\"ble\"}", startDiscoveryCb, userData, NULL, NULL);
ret = LSCall(self->mLSHandle, "luna://com.webos.service.bluetooth2/device/getStatus", "{\"subscribe\":true}", deviceGetstatusCb,
userData, NULL, NULL);
VerifyOrExit(ret == 1, ChipLogError(DeviceLayer, "bt_adapter_le_start_scan() ret: %d", ret));
ChipLogProgress(DeviceLayer, "Scan started");
// Start Timer
idleSource = g_timeout_source_new(kScanTimeout);
g_source_set_callback(idleSource, TimerExpiredCb, userData, nullptr);
g_source_set_priority(idleSource, G_PRIORITY_HIGH_IDLE);
g_source_attach(idleSource, g_main_loop_get_context(self->mAsyncLoop));
g_source_unref(idleSource);
return true;
exit:
return false;
}
CHIP_ERROR ChipDeviceScanner::StartChipScan(unsigned timeoutMs)
{
CHIP_ERROR err = CHIP_NO_ERROR;
VerifyOrReturnError(!mIsScanning, CHIP_ERROR_INCORRECT_STATE);
kScanTimeout = timeoutMs;
mLSHandle = MainLoop::Instance().mLSHandle;
// All set to trigger LE Scan
ChipLogProgress(DeviceLayer, "Start CHIP Scan...");
if (MainLoop::Instance().AsyncRequest(TriggerScan, this) == false)
{
ChipLogError(DeviceLayer, "Failed to trigger Scan...");
err = CHIP_ERROR_INTERNAL;
goto exit;
}
mIsScanning = true; // optimistic, to allow all callbacks to check this
return CHIP_NO_ERROR;
exit:
ChipLogError(DeviceLayer, "Start CHIP Scan could not succeed fully! Stop Scan...");
StopChipScan();
// UnRegisterScanFilter();
return err;
}
bool ChipDeviceScanner::cancelDiscoveryCb(LSHandle * sh, LSMessage * message, void * userData)
{
return true;
}
CHIP_ERROR ChipDeviceScanner::StopChipScan()
{
int ret = 0;
VerifyOrReturnError(mIsScanning, CHIP_ERROR_INCORRECT_STATE);
ret = LSCall(mLSHandle, "luna://com.webos.service.bluetooth2/dadapter/cancelDiscovery", "{}", cancelDiscoveryCb, this, NULL,
NULL);
ChipLogError(DeviceLayer, "Stop CHIP scan ret: %d", ret);
g_main_loop_quit(mAsyncLoop);
ChipLogProgress(DeviceLayer, "CHIP Scanner Async Thread Quit Done..Wait for Thread Windup...!");
// Report to Impl class
mDelegate->OnScanComplete();
mIsScanning = false;
return CHIP_NO_ERROR;
}
CHIP_ERROR ChipDeviceScanner::StartScan(System::Clock::Timeout timeout)
{
return CHIP_NO_ERROR;
}
void ChipDeviceScanner::TimerExpiredCallback(chip::System::Layer * layer, void * appState)
{
static_cast<ChipDeviceScanner *>(appState)->StopScan();
}
CHIP_ERROR ChipDeviceScanner::StopScan()
{
return CHIP_NO_ERROR;
}
int ChipDeviceScanner::MainLoopStopScan(ChipDeviceScanner * self)
{
return 0;
}
int ChipDeviceScanner::MainLoopStartScan(ChipDeviceScanner * self)
{
return 0;
}
} // namespace Internal
} // namespace DeviceLayer
} // namespace chip
#endif // CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE