1 // Copyright (c) 2014 Intel Corporation. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "bluetooth/bluetooth_instance_capi.h"
9 #include "common/picojson.h"
13 inline const char* BoolToString(bool b) {
14 return b ? "true" : "false";
17 static void* event_loop(void* arg) {
18 GMainLoop* event_loop;
19 event_loop = g_main_loop_new(NULL, FALSE);
20 g_main_loop_run(event_loop);
24 } // anonymous namespace
26 BluetoothInstance::BluetoothInstance()
27 : is_js_context_initialized_(false),
28 adapter_enabled_(false),
29 js_reply_needed_(false),
30 stop_discovery_from_js_(false) {
32 // we need a thread for running the main loop
33 // and catching bluetooth glib signals
34 pthread_t event_thread;
35 pthread_attr_t thread_attr;
36 pthread_attr_init(&thread_attr);
37 pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
38 event_thread = pthread_create(&event_thread, &thread_attr,
41 CAPI(bt_initialize());
45 BluetoothInstance::~BluetoothInstance() {
46 UninitializeAdapter();
47 CAPI(bt_deinitialize());
50 void BluetoothInstance::HandleMessage(const char* message) {
54 picojson::parse(v, message, message + strlen(message), &err);
56 LOG_ERR("Ignoring message");
60 std::string cmd = v.get("cmd").to_str();
61 if (cmd == "DiscoverDevices")
62 HandleDiscoverDevices(v);
63 else if (cmd == "StopDiscovery")
64 HandleStopDiscovery(v);
65 else if (cmd == "SetAdapterProperty")
66 HandleSetAdapterProperty(v);
67 else if (cmd == "CreateBonding")
68 HandleCreateBonding(v);
69 else if (cmd == "DestroyBonding")
70 HandleDestroyBonding(v);
71 else if (cmd == "RFCOMMListen")
72 HandleRFCOMMListen(v);
73 else if (cmd == "CloseSocket")
75 else if (cmd == "UnregisterServer")
76 HandleUnregisterServer(v);
79 void BluetoothInstance::HandleSyncMessage(const char* message) {
83 picojson::parse(v, message, message + strlen(message), &err);
85 LOG_ERR("Ignoring Sync message.");
89 std::string cmd = v.get("cmd").to_str();
90 if (cmd == "GetDefaultAdapter")
91 HandleGetDefaultAdapter(v);
92 else if (cmd == "SocketWriteData")
93 HandleSocketWriteData(v);
96 void BluetoothInstance::OnStateChanged(int result,
97 bt_adapter_state_e adapter_state, void* user_data) {
98 BluetoothInstance* obj = static_cast<BluetoothInstance*>(user_data);
100 LOG_ERR("user_data is NULL");
104 obj->adapter_enabled_ = (adapter_state == BT_ADAPTER_ENABLED) ? true : false;
106 if (obj->js_reply_needed_) {
107 // FIXME(clecou) directly call 'GetDefaultAdapter' once NTB is integrated.
108 // After testing, 100 ms is necessary to really get a powered adapter.
109 g_timeout_add(100, obj->GetDefaultAdapter, obj);
113 picojson::value::object o;
115 o["cmd"] = picojson::value("");
116 o["reply_id"] = picojson::value(obj->callbacks_id_map_["Powered"]);
118 o["error"] = picojson::value(static_cast<double>(1));
120 o["error"] = picojson::value(static_cast<double>(0));
122 obj->InternalPostMessage(picojson::value(o));
123 obj->callbacks_id_map_.erase("Powered");
126 void BluetoothInstance::OnNameChanged(char* name, void* user_data) {
127 BluetoothInstance* obj = static_cast<BluetoothInstance*>(user_data);
129 LOG_ERR("user_data is NULL");
133 picojson::value::object o;
135 o["error"] = picojson::value(static_cast<double>(0));
136 o["cmd"] = picojson::value("");
137 o["reply_id"] = picojson::value(obj->callbacks_id_map_["Name"]);
138 obj->InternalPostMessage(picojson::value(o));
139 obj->callbacks_id_map_.erase("Name");
142 void BluetoothInstance::OnVisibilityChanged(int result,
143 bt_adapter_visibility_mode_e visibility_mode, void* user_data) {
144 BluetoothInstance* obj = static_cast<BluetoothInstance*>(user_data);
146 LOG_ERR("user_data is NULL");
150 picojson::value::object o;
152 o["cmd"] = picojson::value("");
153 o["reply_id"] = picojson::value(obj->callbacks_id_map_["Discoverable"]);
155 o["error"] = picojson::value(static_cast<double>(1));
157 o["error"] = picojson::value(static_cast<double>(0));
159 obj->InternalPostMessage(picojson::value(o));
160 obj->callbacks_id_map_.erase("Discoverable");
163 void BluetoothInstance::OnDiscoveryStateChanged(int result,
164 bt_adapter_device_discovery_state_e discovery_state,
165 bt_adapter_device_discovery_info_s* discovery_info, void* user_data) {
166 BluetoothInstance* obj = static_cast<BluetoothInstance*>(user_data);
168 LOG_ERR("user_data is NULL");
172 picojson::value::object o;
174 switch (discovery_state) {
175 case BT_ADAPTER_DEVICE_DISCOVERY_STARTED: {
176 o["cmd"] = picojson::value("");
177 o["reply_id"] = picojson::value(obj->callbacks_id_map_["StartDiscovery"]);
180 o["error"] = picojson::value(static_cast<double>(1));
182 o["error"] = picojson::value(static_cast<double>(0));
184 obj->InternalPostMessage(picojson::value(o));
185 obj->callbacks_id_map_.erase("StartDiscovery");
188 case BT_ADAPTER_DEVICE_DISCOVERY_FINISHED: {
189 if (obj->stop_discovery_from_js_) {
190 o["cmd"] = picojson::value("");
192 picojson::value(obj->callbacks_id_map_["StopDiscovery"]);
195 o["error"] = picojson::value(static_cast<double>(1));
197 o["error"] = picojson::value(static_cast<double>(0));
200 // discovery stop was not initiated by JS. It was done by a timeout...
201 o["cmd"] = picojson::value("DiscoveryFinished");
203 obj->InternalPostMessage(picojson::value(o));
204 obj->callbacks_id_map_.erase("StopDiscovery");
205 obj->stop_discovery_from_js_ = false;
208 case BT_ADAPTER_DEVICE_DISCOVERY_FOUND: {
209 o["Alias"] = picojson::value(discovery_info->remote_name);
210 o["Address"] = picojson::value(discovery_info->remote_address);
212 int major = discovery_info->bt_class.major_device_class;
213 int minor = discovery_info->bt_class.minor_device_class;
214 int service_class = discovery_info->bt_class.major_service_class_mask;
215 o["ClassMajor"] = picojson::value(static_cast<double>(major));
216 o["ClassMinor"] = picojson::value(static_cast<double>(minor));
217 o["ClassService"] = picojson::value(static_cast<double>(service_class));
219 picojson::array uuids;
220 for (int i = 0; i < discovery_info->service_count; i++)
221 uuids.push_back(picojson::value(discovery_info->service_uuid[i]));
223 o["UUIDs"] = picojson::value(uuids);
226 bool trusted = false;
227 bool connected = false;
229 if (discovery_info->is_bonded) {
230 bt_device_info_s* device_info = NULL;
231 CAPI(bt_adapter_get_bonded_device_info(discovery_info->remote_address,
234 LOG_ERR("device_info is NULL");
236 if (!device_info->is_bonded)
237 LOG_ERR("remote device should be bonded!");
240 trusted = device_info->is_authorized;
241 connected = device_info->is_connected;
242 CAPI(bt_adapter_free_device_info(device_info));
245 o["Paired"] = picojson::value(BoolToString(paired));
246 o["Trusted"] = picojson::value(BoolToString(trusted));
247 o["Connected"] = picojson::value(BoolToString(connected));
249 o["cmd"] = picojson::value("DeviceFound");
250 o["found_on_discovery"] = picojson::value(true);
252 obj->InternalPostMessage(picojson::value(o));
256 LOG_ERR("Unknown discovery state callback!");
261 bool BluetoothInstance::OnKnownBondedDevice(bt_device_info_s* device_info,
263 BluetoothInstance* obj = static_cast<BluetoothInstance*>(user_data);
265 LOG_ERR("user_data is NULL!");
269 LOG_ERR("device_info is NULL!");
273 picojson::value::object o;
274 char* alias = device_info->remote_name;
275 o["Alias"] = picojson::value(alias);
277 char* address = device_info->remote_address;
278 o["Address"] = picojson::value(address);
280 int major = device_info->bt_class.major_device_class;
281 int minor = device_info->bt_class.minor_device_class;
282 int service_class = device_info->bt_class.major_service_class_mask;
283 o["ClassMajor"] = picojson::value(static_cast<double>(major));
284 o["ClassMinor"] = picojson::value(static_cast<double>(minor));
285 o["ClassService"] = picojson::value(static_cast<double>(service_class));
287 // parse UUIDs supported by remote device
288 picojson::array uuids;
289 for (int i = 0; i < device_info->service_count; i++)
290 uuids.push_back(picojson::value(device_info->service_uuid[i]));
292 o["UUIDs"] = picojson::value(uuids);
293 o["Paired"] = picojson::value(BoolToString(device_info->is_bonded));
294 o["Trusted"] = picojson::value(BoolToString(device_info->is_authorized));
295 o["Connected"] = picojson::value(BoolToString(device_info->is_connected));
296 o["reply_id"] = picojson::value("");
297 o["cmd"] = picojson::value("BondedDevice");
298 obj->InternalPostMessage(picojson::value(o));
302 void BluetoothInstance::OnBondCreated(int result, bt_device_info_s* device_info,
304 BluetoothInstance* obj = static_cast<BluetoothInstance*>(user_data);
306 LOG_ERR("user_data is NULL!");
310 LOG_ERR("device_info is NULL!");
314 picojson::value::object o;
315 o["cmd"] = picojson::value("");
316 o["reply_id"] = picojson::value(obj->callbacks_id_map_["CreateBonding"]);
317 o["capi"] = picojson::value(static_cast<double>(1));
319 LOG_ERR("onBondCreated() failed");
320 o["error"] = picojson::value(static_cast<double>(1));
322 o["error"] = picojson::value(static_cast<double>(0));
324 obj->InternalPostMessage(picojson::value(o));
325 obj->callbacks_id_map_.erase("CreateBonding");
328 void BluetoothInstance::OnBondDestroyed(int result, char* remote_address,
330 BluetoothInstance* obj = static_cast<BluetoothInstance*>(user_data);
332 LOG_ERR("user_data is NULL!");
336 if (!remote_address) {
337 LOG_ERR("remote_address is NULL!");
340 picojson::value::object o;
341 o["cmd"] = picojson::value("");
342 o["reply_id"] = picojson::value(obj->callbacks_id_map_["DestroyBonding"]);
343 o["capi"] = picojson::value(static_cast<double>(1));
345 LOG_ERR("onBondDestroyed() failed");
346 o["error"] = picojson::value(static_cast<double>(1));
348 o["error"] = picojson::value(static_cast<double>(0));
350 obj->InternalPostMessage(picojson::value(o));
351 obj->callbacks_id_map_.erase("DestroyBonding");
354 void BluetoothInstance::OnSocketConnected(int result,
355 bt_socket_connection_state_e connection_state,
356 bt_socket_connection_s* connection,
358 BluetoothInstance* obj = static_cast<BluetoothInstance*>(user_data);
360 LOG_ERR("user_data is NULL!");
364 LOG_ERR("connection is NULL!");
368 picojson::value::object o;
371 LOG_ERR("onSocketConnected() is failed");
372 o["error"] = picojson::value(static_cast<double>(1));
375 if (connection_state == BT_SOCKET_CONNECTED &&
376 connection->local_role == BT_SOCKET_SERVER) {
377 o["cmd"] = picojson::value("RFCOMMSocketAccept");
378 o["uuid"] = picojson::value(connection->service_uuid);
380 picojson::value(static_cast<double>(connection->socket_fd));
381 o["peer"] = picojson::value(connection->remote_address);
383 CAPI(bt_socket_set_data_received_cb(OnSocketHasData, NULL));
384 } else if (connection_state == BT_SOCKET_CONNECTED &&
385 connection->local_role == BT_SOCKET_CLIENT) {
386 o["cmd"] = picojson::value("");
388 picojson::value(obj->callbacks_id_map_["ConnectToService"]);
389 obj->callbacks_id_map_.erase("ConnectToService");
391 o["uuid"] = picojson::value(connection->service_uuid);
393 picojson::value(static_cast<double>(connection->socket_fd));
395 CAPI(bt_socket_set_data_received_cb(OnSocketHasData, NULL));
396 } else if (connection_state == BT_SOCKET_DISCONNECTED) {
397 o["cmd"] = picojson::value("");
399 picojson::value(obj->callbacks_id_map_["RFCOMMsocketDestroy"]);
400 obj->callbacks_id_map_.erase("RFCOMMsocketDestroy");
402 picojson::value(static_cast<double>(connection->socket_fd));
404 LOG_ERR("Unknown role!");
407 obj->InternalPostMessage(picojson::value(o));
410 void BluetoothInstance::OnSocketHasData(bt_socket_received_data_s* data,
412 BluetoothInstance* obj = static_cast<BluetoothInstance*>(user_data);
414 LOG_ERR("user_data is NULL");
418 LOG_ERR("data is NULL");
421 picojson::value::object o;
422 o["cmd"] = picojson::value("SocketHasData");
423 o["socket_fd"] = picojson::value(static_cast<double>(data->socket_fd));
424 o["data"] = picojson::value(static_cast<std::string>(data->data));
425 obj->InternalPostMessage(picojson::value(o));
428 gboolean BluetoothInstance::GetDefaultAdapter(gpointer user_data) {
429 BluetoothInstance* obj = static_cast<BluetoothInstance*>(user_data);
431 LOG_ERR("user_data is NULL");
435 picojson::value::object o;
438 CAPI(bt_adapter_get_name(&name));
441 o["name"] = picojson::value(name);
443 char* address = NULL;
444 CAPI(bt_adapter_get_address(&address));
447 o["address"] = picojson::value(address);
449 bool powered, visible = false;
451 if (obj->adapter_enabled_) {
454 bt_adapter_visibility_mode_e mode =
455 BT_ADAPTER_VISIBILITY_MODE_NON_DISCOVERABLE;
457 CAPI(bt_adapter_get_visibility(&mode, NULL));
458 visible = (mode > 0) ? true : false;
460 o["powered"] = picojson::value(powered);
461 o["visible"] = picojson::value(visible);
463 // This is the JS API entry point, so we should clean our message queue
464 // on the next PostMessage call.
465 if (!obj->is_js_context_initialized_)
466 obj->is_js_context_initialized_ = true;
468 obj->InternalSetSyncReply(picojson::value(o));
470 // Retrieve already bonded devices linked to the adapter in order to
471 // fill known_devices array on javascript side.
472 CAPI(bt_adapter_foreach_bonded_device(OnKnownBondedDevice, obj));
474 obj->js_reply_needed_ = false;
479 void BluetoothInstance::InitializeAdapter() {
480 // register C API bluetooth callbacks
481 CAPI(bt_adapter_set_state_changed_cb(OnStateChanged, this));
482 CAPI(bt_adapter_set_name_changed_cb(OnNameChanged, this));
483 CAPI(bt_adapter_set_visibility_mode_changed_cb(OnVisibilityChanged, this));
484 CAPI(bt_adapter_set_device_discovery_state_changed_cb(OnDiscoveryStateChanged,
486 CAPI(bt_device_set_bond_created_cb(OnBondCreated, this));
487 CAPI(bt_device_set_bond_destroyed_cb(OnBondDestroyed, this));
489 bt_adapter_state_e state = BT_ADAPTER_DISABLED;
490 CAPI(bt_adapter_get_state(&state));
492 // Most of the C API functions require as precondition to previously had
493 // called bt_adapter_enable(). So if adapter is turned OFF, we enable it.
494 if (state == BT_ADAPTER_DISABLED) {
495 CAPI(bt_adapter_enable());
497 adapter_enabled_ = true;
501 void BluetoothInstance::UninitializeAdapter() {
502 // unregister C API bluetooth callbacks
503 CAPI(bt_adapter_unset_state_changed_cb());
504 CAPI(bt_adapter_unset_name_changed_cb());
505 CAPI(bt_adapter_unset_visibility_mode_changed_cb());
506 CAPI(bt_adapter_unset_device_discovery_state_changed_cb());
507 CAPI(bt_device_unset_bond_created_cb());
508 CAPI(bt_device_unset_bond_destroyed_cb());
509 CAPI(bt_socket_unset_connection_state_changed_cb());
510 CAPI(bt_socket_unset_data_received_cb());
513 void BluetoothInstance::HandleGetDefaultAdapter(const picojson::value& msg) {
514 if (!adapter_enabled_) {
515 js_reply_needed_ = true;
519 GetDefaultAdapter(this);
522 void BluetoothInstance::HandleSetAdapterProperty(const picojson::value& msg) {
523 picojson::value::object o;
525 std::string property = msg.get("property").to_str();
526 callbacks_id_map_[property] = msg.get("reply_id").to_str();
528 if (property == "Powered") {
529 bool power = msg.get("value").get<bool>();
531 CAPI(bt_adapter_enable());
533 CAPI(bt_adapter_disable());
534 } else if (property == "Name") {
535 std::string name = msg.get("value").to_str();
536 CAPI(bt_adapter_set_name(name.c_str()));
537 } else if (property == "Discoverable") {
538 bool visible = msg.get("value").get<bool>();
539 int timeout = static_cast<int>(msg.get("timeout").get<double>());
541 bt_adapter_visibility_mode_e discoverable_mode =
542 BT_ADAPTER_VISIBILITY_MODE_NON_DISCOVERABLE;
545 discoverable_mode = BT_ADAPTER_VISIBILITY_MODE_GENERAL_DISCOVERABLE;
547 discoverable_mode = BT_ADAPTER_VISIBILITY_MODE_LIMITED_DISCOVERABLE;
549 CAPI(bt_adapter_set_visibility(discoverable_mode, timeout));
551 LOG_ERR("bad property received!");
555 void BluetoothInstance::HandleDiscoverDevices(const picojson::value& msg) {
556 callbacks_id_map_["StartDiscovery"] = msg.get("reply_id").to_str();
557 CAPI(bt_adapter_start_device_discovery());
560 void BluetoothInstance::HandleStopDiscovery(const picojson::value& msg) {
561 callbacks_id_map_["StopDiscovery"] = msg.get("reply_id").to_str();
563 bool is_discovering = false;
564 CAPI(bt_adapter_is_discovering(&is_discovering));
568 stop_discovery_from_js_ = true;
569 CAPI(bt_adapter_stop_device_discovery());
572 void BluetoothInstance::HandleCreateBonding(const picojson::value& msg) {
573 callbacks_id_map_["CreateBonding"] = msg.get("reply_id").to_str();
574 std::string address = msg.get("address").to_str();
575 CAPI(bt_device_create_bond(address.c_str()));
578 void BluetoothInstance::HandleDestroyBonding(const picojson::value& msg) {
579 callbacks_id_map_["DestroyBonding"] = msg.get("reply_id").to_str();
580 std::string address = msg.get("address").to_str();
581 CAPI(bt_device_destroy_bond(address.c_str()));
584 void BluetoothInstance::HandleRFCOMMListen(const picojson::value& msg) {
585 picojson::value::object o;
591 bt_socket_create_rfcomm(msg.get("uuid").to_str().c_str(), &socket_fd),
594 o["error"] = picojson::value(static_cast<double>(1));
595 InternalPostMessage(picojson::value(o));
599 CAPI_ERR(bt_socket_listen_and_accept_rfcomm(socket_fd, 0), error);
601 o["error"] = picojson::value(static_cast<double>(1));
602 InternalPostMessage(picojson::value(o));
606 CAPI_ERR(bt_socket_set_connection_state_changed_cb(OnSocketConnected, this),
609 o["error"] = picojson::value(static_cast<double>(1));
610 InternalPostMessage(picojson::value(o));
614 o["error"] = picojson::value(static_cast<double>(0));
615 // give the listened socket to JS and store it in service_handler
616 o["socket_fd"] = picojson::value(static_cast<double>(socket_fd));
617 InternalPostMessage(picojson::value(o));
620 void BluetoothInstance::HandleConnectToService(const picojson::value& msg) {
621 callbacks_id_map_["ConnectToService"] = msg.get("reply_id").to_str();
625 bt_socket_connect_rfcomm(msg.get("address").to_str().c_str(),
626 msg.get("uuid").to_str().c_str()),
629 CAPI(bt_socket_set_connection_state_changed_cb(OnSocketConnected, this));
632 void BluetoothInstance::HandleSocketWriteData(const picojson::value& msg) {
633 picojson::value::object o;
634 std::string data = msg.get("data").to_str();
635 int socket = static_cast<int>(msg.get("socket_fd").get<double>());
637 CAPI(bt_socket_send_data(socket, data.c_str(),
638 static_cast<int>(data.size())));
639 o["size"] = picojson::value(static_cast<double>(data.size()));
641 InternalSetSyncReply(picojson::value(o));
644 void BluetoothInstance::HandleCloseSocket(const picojson::value& msg) {
645 picojson::value::object o;
647 int socket = static_cast<int>(msg.get("socket_fd").get<double>());
649 CAPI_ERR(bt_socket_disconnect_rfcomm(socket), error);
651 o["error"] = picojson::value(static_cast<double>(0));
653 o["error"] = picojson::value(static_cast<double>(1));
656 o["cmd"] = picojson::value("");
657 o["reply_id"] = msg.get("reply_id");
658 o["capi"] = picojson::value(static_cast<double>(1));
659 InternalPostMessage(picojson::value(o));
662 void BluetoothInstance::HandleUnregisterServer(const picojson::value& msg) {
663 callbacks_id_map_["RFCOMMsocketDestroy"] = msg.get("reply_id").to_str();
664 int socket = static_cast<int>(msg.get("server_fd").get<double>());
666 CAPI(bt_socket_destroy_rfcomm(socket));
669 void BluetoothInstance::FlushPendingMessages() {
670 // Flush previous pending messages.
674 MessageQueue::iterator it;
675 for (it = queue_.begin(); it != queue_.end(); ++it)
676 PostMessage((*it).serialize().c_str());
679 void BluetoothInstance::InternalPostMessage(picojson::value v) {
680 // If the JavaScript 'context' hasn't been initialized yet (i.e. the C++
681 // backend was loaded and it is already executing but
682 // tizen.bluetooth.getDefaultAdapter() hasn't been called so far), we need to
683 // queue the PostMessage calls and defer them until the default adapter is set
684 // on the JS side. That will guarantee the correct callbacks will be called,
685 // and on the right order, only after tizen.bluetooth.getDefaultAdapter() is
688 if (!is_js_context_initialized_) {
693 FlushPendingMessages();
694 PostMessage(v.serialize().c_str());
697 void BluetoothInstance::InternalSetSyncReply(picojson::value v) {
698 SendSyncReply(v.serialize().c_str());
700 FlushPendingMessages();