--- /dev/null
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved
+ *
+ * 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 ble_test.cpp
+ * @version 1.0
+ * @brief basic functional tests for tizen bluetooth API
+ */
+
+
+#include <string>
+#include <iostream>
+#include <gtest/gtest.h>
+#include <dlog.h>
+#include <unistd.h>
+#include <netinet/in.h>
+
+#include <bluetooth.h>
+#include <bluetooth_internal.h>
+#include <app_control.h>
+
+
+#define CODE_TO_STRING(name) case name: return #name
+
+#define BLUETOOTH_UUID_STRING_MAX 50
+
+const char * ble_error_to_string(int error) {
+ switch (error) {
+ CODE_TO_STRING(BT_ERROR_NONE);
+ CODE_TO_STRING(BT_ERROR_CANCELLED);
+ CODE_TO_STRING(BT_ERROR_INVALID_PARAMETER);
+ CODE_TO_STRING(BT_ERROR_OUT_OF_MEMORY);
+ CODE_TO_STRING(BT_ERROR_RESOURCE_BUSY);
+ CODE_TO_STRING(BT_ERROR_TIMED_OUT);
+ CODE_TO_STRING(BT_ERROR_NOW_IN_PROGRESS);
+ CODE_TO_STRING(BT_ERROR_PERMISSION_DENIED);
+ CODE_TO_STRING(BT_ERROR_QUOTA_EXCEEDED);
+ CODE_TO_STRING(BT_ERROR_NO_DATA);
+ CODE_TO_STRING(BT_ERROR_DEVICE_POLICY_RESTRICTION);
+ CODE_TO_STRING(BT_ERROR_NOT_INITIALIZED);
+ CODE_TO_STRING(BT_ERROR_NOT_ENABLED);
+ CODE_TO_STRING(BT_ERROR_ALREADY_DONE);
+ CODE_TO_STRING(BT_ERROR_OPERATION_FAILED);
+ CODE_TO_STRING(BT_ERROR_NOT_IN_PROGRESS);
+ CODE_TO_STRING(BT_ERROR_REMOTE_DEVICE_NOT_BONDED);
+ CODE_TO_STRING(BT_ERROR_AUTH_REJECTED);
+ CODE_TO_STRING(BT_ERROR_AUTH_FAILED);
+ CODE_TO_STRING(BT_ERROR_REMOTE_DEVICE_NOT_FOUND);
+ CODE_TO_STRING(BT_ERROR_REMOTE_DEVICE_NOT_CONNECTED);
+ CODE_TO_STRING(BT_ERROR_AGAIN);
+ CODE_TO_STRING(BT_ERROR_SERVICE_NOT_FOUND);
+ CODE_TO_STRING(BT_ERROR_AUTHORIZATION_REJECTED);
+ CODE_TO_STRING(BT_ERROR_MAX_CONNECTION);
+ default: return "Error not defined";
+ }
+}
+
+const char * ble_state_to_string(bt_adapter_state_e state) {
+ switch (state) {
+ CODE_TO_STRING(BT_ADAPTER_DISABLED);
+ CODE_TO_STRING(BT_ADAPTER_ENABLED);
+ default: return "State not defined";
+ }
+}
+
+const char * appctrl_error_to_string(int error) {
+ switch (error) {
+ CODE_TO_STRING(APP_CONTROL_ERROR_NONE);
+ CODE_TO_STRING(APP_CONTROL_ERROR_INVALID_PARAMETER);
+ CODE_TO_STRING(APP_CONTROL_ERROR_OUT_OF_MEMORY);
+ CODE_TO_STRING(APP_CONTROL_ERROR_APP_NOT_FOUND);
+ CODE_TO_STRING(APP_CONTROL_ERROR_KEY_NOT_FOUND);
+ CODE_TO_STRING(APP_CONTROL_ERROR_KEY_REJECTED);
+ CODE_TO_STRING(APP_CONTROL_ERROR_INVALID_DATA_TYPE);
+ CODE_TO_STRING(APP_CONTROL_ERROR_LAUNCH_REJECTED);
+ CODE_TO_STRING(APP_CONTROL_ERROR_PERMISSION_DENIED);
+ CODE_TO_STRING(APP_CONTROL_ERROR_LAUNCH_FAILED);
+ CODE_TO_STRING(APP_CONTROL_ERROR_TIMED_OUT);
+ CODE_TO_STRING(APP_CONTROL_ERROR_IO_ERROR);
+ CODE_TO_STRING(BT_ERROR_MAX_CONNECTION);
+ default: return "Error not defined";
+ }
+}
+
+void __uuid_hex_to_string(unsigned char *uuid, char *str) {
+ uint32_t uuid0, uuid4;
+ uint16_t uuid1, uuid2, uuid3, uuid5;
+
+ memcpy(&uuid0, &(uuid[0]), 4);
+ memcpy(&uuid1, &(uuid[4]), 2);
+ memcpy(&uuid2, &(uuid[6]), 2);
+ memcpy(&uuid3, &(uuid[8]), 2);
+ memcpy(&uuid4, &(uuid[10]), 4);
+ memcpy(&uuid5, &(uuid[14]), 2);
+
+ snprintf((char *)str, BLUETOOTH_UUID_STRING_MAX, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x",
+ ntohl(uuid0), ntohs(uuid1),
+ ntohs(uuid2), ntohs(uuid3),
+ ntohl(uuid4), ntohs(uuid5));
+ return;
+}
+
+void __bt_string_to_uuid_hex(const char *str, unsigned char *uuid) {
+ uint32_t uuid0, uuid4;
+ uint16_t uuid1, uuid2, uuid3, uuid5;
+
+ sscanf(str, "%08x-%04hx-%04hx-%04hx-%08x%04hx",
+ &uuid0, &uuid1, &uuid2, &uuid3, &uuid4, &uuid5);
+
+ uuid0 = htonl(uuid0);
+ uuid1 = htons(uuid1);
+ uuid2 = htons(uuid2);
+ uuid3 = htons(uuid3);
+ uuid4 = htonl(uuid4);
+ uuid5 = htons(uuid5);
+
+ memcpy(&(uuid[0]), &uuid0, 4);
+ memcpy(&(uuid[4]), &uuid1, 2);
+ memcpy(&(uuid[6]), &uuid2, 2);
+ memcpy(&(uuid[8]), &uuid3, 2);
+ memcpy(&(uuid[10]), &uuid4, 4);
+ memcpy(&(uuid[14]), &uuid5, 2);
+ return;
+}
+
+std::string __bin_to_hex(const char* source, int length) {
+ std::string dest;
+ for(int i = 0; i < length; i++) {
+ char digit[3];
+ sprintf(digit, "%02x", source[i]);
+ dest.append(digit);
+ }
+ return dest;
+}
+
+class BleTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ int ret = BT_ERROR_NONE;
+
+ ret = bt_initialize();
+ if (ret != BT_ERROR_NONE) {
+ std::cout << "[bt_initialize] failed." << std::endl;
+ return;
+ }
+ }
+
+ void TearDown() override {
+ int ret = BT_ERROR_NONE;
+
+ ret = bt_deinitialize();
+ if (ret != BT_ERROR_NONE) {
+ std::cout << "[bt_deinitialize] failed." << std::endl;
+ return;
+ }
+ }
+};
+
+void __state_changed_callback(int result, bt_adapter_state_e adapter_state, void *user_data);
+void __scan_result_cb(int result, bt_adapter_le_device_scan_result_info_s *info, void *user_data);
+
+class CallbackResultHolder {
+private:
+ GMainLoop *mainloop; // The GMainLoop is required for state_changed_callback() to be called.
+ int result;
+ bt_adapter_state_e adapter_state;
+ bt_adapter_le_service_data_s *service_data;
+
+public:
+ CallbackResultHolder() : result(BT_ERROR_NONE),
+ adapter_state(BT_ADAPTER_DISABLED),
+ service_data(NULL) {
+ mainloop = g_main_loop_new(NULL, FALSE);
+ }
+ ~CallbackResultHolder() {
+ g_main_loop_unref (mainloop);
+ if (service_data != NULL)
+ bt_adapter_le_free_service_data_list(service_data, 1);
+ }
+
+ void wait_async_result() {
+ g_main_loop_run(mainloop);
+ return;
+ }
+
+ void set_state_changed_cb() {
+ bt_adapter_set_state_changed_cb(__state_changed_callback, this);
+ }
+
+ void set_state_callback_result(int result, bt_adapter_state_e adapter_state) {
+ this->result = result;
+ this->adapter_state = adapter_state;
+ g_main_loop_quit(mainloop);
+ }
+
+ int get_changed_state(bt_adapter_state_e *adapter_state) {
+ *adapter_state = this->adapter_state;
+ return result;
+ }
+
+ void set_scan_callback_result(int result, bt_adapter_le_service_data_s *service_data) {
+ this->result = result;
+ this->service_data = service_data;
+ g_main_loop_quit(mainloop);
+ }
+
+ int get_scan_result(bt_adapter_le_service_data_s **service_data) {
+ (void) service_data; // to avoid -Werror=unused-but-set-parameter.
+ *service_data = this->service_data;
+ return result;
+ }
+};
+
+void __state_changed_callback(int result, bt_adapter_state_e adapter_state, void *user_data) {
+ CallbackResultHolder* holder = static_cast<CallbackResultHolder *>(user_data);
+ holder->set_state_callback_result(result, adapter_state);
+}
+
+void __scan_result_cb(int result, bt_adapter_le_device_scan_result_info_s *info, void *user_data) {
+ // Conversion between 16 bit UUID and 128 bit UUID
+ // - https://stackoverflow.com/questions/36212020/how-can-i-convert-a-bluetooth-16-bit-service-uuid-into-a-128-bit-uuid
+ // The UUID, FFF9, is the same as "0000fff9-0000-1000-8000-00805f9b34fb".
+ const std::string FIDO_CABLE_UUID16 = "FFF9"; // Used for Android devices
+ const std::string GOOGLE_CABLE_UUID16 = "FDE2"; // Used for Apple devices
+
+ if(result != BT_ERROR_NONE)
+ return;
+
+ bt_adapter_le_packet_type_e pkt_type = BT_ADAPTER_LE_PACKET_ADVERTISING;
+ bt_adapter_le_service_data_s *data_list;
+ int count;
+ int ret;
+ ret = bt_adapter_le_get_scan_result_service_data_list(info, pkt_type, &data_list, &count);
+ if (ret == BT_ERROR_NONE) { // To avoid BT_ERROR_NO_DATA
+ if (FIDO_CABLE_UUID16.compare(data_list[0].service_uuid) == 0
+ || GOOGLE_CABLE_UUID16.compare(data_list[0].service_uuid) == 0) {
+ std::cout << "Scanned Service Data "
+ << ": UUID=" << data_list[0].service_uuid
+ << ", Data=" << __bin_to_hex(data_list[0].service_data, data_list[0].service_data_len)
+ << std::endl;
+ CallbackResultHolder* holder = static_cast<CallbackResultHolder *>(user_data);
+ holder->set_scan_callback_result(result, data_list);
+ }
+ }
+}
+
+
+TEST_F(BleTest, testCheckStatus)
+{
+ int ret = BT_ERROR_NONE;
+ bt_adapter_state_e adapter_state;
+
+ ret = bt_adapter_get_state(&adapter_state);
+ EXPECT_EQ(ret, BT_ERROR_NONE)
+ << "[bt_adapter_get_state] failed. " << "ret=" << ble_error_to_string(ret) << std::endl;
+
+ std::cout << "BLE state=" << ble_state_to_string(adapter_state) << std::endl;
+}
+
+TEST_F(BleTest, testEnableBle)
+{
+ int ret = BT_ERROR_NONE;
+ bt_adapter_state_e adapter_state;
+ CallbackResultHolder result_holder;
+
+ ret = bt_adapter_get_state(&adapter_state);
+ EXPECT_EQ(ret, BT_ERROR_NONE);
+ std::cout << "Before enabling. state=" << ble_state_to_string(adapter_state) << std::endl;
+
+ if(adapter_state == BT_ADAPTER_ENABLED) {
+ result_holder.set_state_changed_cb();
+ ret = bt_adapter_disable();
+ EXPECT_EQ(ret, BT_ERROR_NONE);
+ // wait until bt_adapter_disable() is completed.
+ result_holder.wait_async_result();
+ ret = result_holder.get_changed_state(&adapter_state);
+ EXPECT_EQ(ret, BT_ERROR_NONE);
+ EXPECT_EQ(adapter_state, BT_ADAPTER_DISABLED);
+ std::cout << "After disabling. state=" << ble_state_to_string(adapter_state) << std::endl;
+ }
+
+ // enable bluetooth adapter.
+ result_holder.set_state_changed_cb();
+ ret = bt_adapter_enable();
+ EXPECT_EQ(ret, BT_ERROR_NONE)
+ << "[bt_adapter_enable] failed. " << "ret=" << ble_error_to_string(ret) << std::endl;
+
+ // wait until bt_adapter_enable() is completed.
+ result_holder.wait_async_result();
+ ret = result_holder.get_changed_state(&adapter_state);
+ EXPECT_EQ(ret, BT_ERROR_NONE);
+ EXPECT_EQ(adapter_state, BT_ADAPTER_ENABLED);
+ std::cout << "After enabling. state=" << ble_state_to_string(adapter_state) << std::endl;
+}
+
+TEST_F(BleTest, testEnableBleManually)
+{
+ int ret = BT_ERROR_NONE;
+ bt_adapter_state_e adapter_state;
+ CallbackResultHolder result_holder;
+
+ ret = bt_adapter_get_state(&adapter_state);
+ EXPECT_EQ(ret, BT_ERROR_NONE);
+ std::cout << "Before enabling. state=" << ble_state_to_string(adapter_state) << std::endl;
+
+ if(adapter_state == BT_ADAPTER_ENABLED) {
+ result_holder.set_state_changed_cb();
+ ret = bt_adapter_disable();
+ EXPECT_EQ(ret, BT_ERROR_NONE);
+ // wait until bt_adapter_disable() is completed.
+ result_holder.wait_async_result();
+ ret = result_holder.get_changed_state(&adapter_state);
+ EXPECT_EQ(ret, BT_ERROR_NONE);
+ EXPECT_EQ(adapter_state, BT_ADAPTER_DISABLED);
+ std::cout << "After disabling. state=" << ble_state_to_string(adapter_state) << std::endl;
+ }
+
+ // enable bluetooth adapter manually.
+ result_holder.set_state_changed_cb();
+ app_control_h service = NULL;
+ app_control_create(&service);
+ EXPECT_TRUE(service != NULL) << "service_create failed!";
+
+ app_control_set_operation(service, APP_CONTROL_OPERATION_SETTING_BT_ENABLE);
+ ret = app_control_send_launch_request(service, NULL, NULL);
+ EXPECT_EQ(ret, APP_CONTROL_ERROR_NONE)
+ << "[app_control_send_launch_request] failed. " << "ret=" << appctrl_error_to_string(ret);
+
+ std::cout << "Waiting for BLE to be enabled..." << std::endl;
+ std::cout << "... Enable BLE manually." << std::endl;
+
+ // wait until bt_adapter_enable() is completed.
+ result_holder.wait_async_result();
+ ret = result_holder.get_changed_state(&adapter_state);
+ EXPECT_EQ(ret, BT_ERROR_NONE);
+ EXPECT_EQ(adapter_state, BT_ADAPTER_ENABLED);
+ std::cout << "After enabling. state=" << ble_state_to_string(adapter_state) << std::endl;
+
+ // distory app control
+ app_control_destroy(service);
+}
+
+TEST_F(BleTest, testReadBleAdvert)
+{
+ int ret = BT_ERROR_NONE;
+ CallbackResultHolder result_holder;
+ bt_adapter_le_service_data_s *service_data;
+
+ // Starting Scanning
+ std::cout << "Starting BLE Scan..." << std::endl;
+ ret = bt_adapter_le_start_scan(__scan_result_cb, &result_holder);
+ if(ret == BT_ERROR_OPERATION_FAILED) {
+ // Wait a second for time passing after eanbling BLE state.
+ sleep(1);
+ ret = bt_adapter_le_start_scan(__scan_result_cb, &result_holder);
+ }
+ EXPECT_TRUE(ret == BT_ERROR_NONE || ret == BT_ERROR_NOW_IN_PROGRESS)
+ << "[bt_adapter_le_start_scan] failed. ret=" << ble_error_to_string(ret) << std::endl;
+
+ // Wait BLE Advert
+ std::cout << "Waiting BLE Advert..." << std::endl;
+ std::cout << "... Start a Passkey transaction with a roaming authenticator." << std::endl;
+ result_holder.wait_async_result();
+ ret = result_holder.get_scan_result(&service_data);
+ EXPECT_EQ(ret, BT_ERROR_NONE);
+
+ ret = bt_adapter_le_stop_scan();
+ EXPECT_EQ(ret, BT_ERROR_NONE)
+ << "[bt_adapter_le_stop_scan] failed. ret=" << ble_error_to_string(ret) << std::endl;
+}