/* * * 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 #include #include #include "MainLoop.h" #include #include 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 void operator()(T * value) { g_object_unref(value); } }; using GCancellableUniquePtr = std::unique_ptr; using GDBusObjectManagerUniquePtr = std::unique_ptr; } // 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::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(handle, delegate); } std::unique_ptr ChipDeviceScanner::Create(ChipDeviceScannerDelegate * delegate) { return std::make_unique(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(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