'gio-2.0',
'bluez',
],
- 'bluetooth%': 'bluez4',
+ 'bluetooth%': 'tizen_capi',
},
'includes': [
'../common/pkg-config.gypi',
'bluetooth_instance.h',
],
'conditions': [
+ [ 'bluetooth == "bluez5" or bluetooth == "bluez4"', {
+ 'variables': {
+ 'packages': [
+ 'gio-2.0',
+ 'bluez',
+ ],
+ },
+ }],
[ 'bluetooth == "bluez5"', {
- 'sources': ['bluetooth_instance_bluez5.cc'],
+ 'sources': [
+ 'bluetooth_instance_bluez5.cc',
+ ],
'defines': ['BLUEZ_5'],
}
],
[ 'bluetooth == "bluez4"', {
- 'sources': ['bluetooth_instance_bluez4.cc'],
+ 'sources': [
+ 'bluetooth_instance_bluez4.cc',
+ ],
'defines': ['BLUEZ_4'],
}
],
- [ 'tizen == 1', {
- 'variables': { 'packages': ['capi-network-bluetooth'] },
- }],
+ [ 'bluetooth == "tizen_capi"', {
+ 'sources!': [
+ 'bluetooth_instance.cc',
+ 'bluetooth_instance.h',
+ ],
+ 'sources': [
+ 'bluetooth_instance_capi.cc',
+ 'bluetooth_instance_capi.h',
+ ],
+ 'variables': {
+ 'packages': [
+ 'capi-network-bluetooth',
+ 'glib-2.0',
+ ]
+ },
+ 'defines': ['TIZEN_CAPI_BT'],
+ }
+ ],
],
},
],
extension.setMessageListener(function(json) {
var msg = JSON.parse(json);
-
- if (msg.cmd == 'DeviceFound')
+ if (msg.cmd == 'BondedDevice')
+ handleBondedDevice(msg);
+ else if (msg.cmd == 'DeviceFound')
handleDeviceFound(msg);
else if (msg.cmd == 'DiscoveryFinished')
handleDiscoveryFinished();
delete _callbacks[reply_id];
callback(msg);
} else {
- console.log('Invalid reply_id from Tizen Bluetooth: ' + reply_id);
+ // do not print error log when the postmessage was not initiated by JS
+ if (reply_id != '')
+ console.log('Invalid reply_id from Tizen Bluetooth: ' + reply_id);
}
}
});
this.change_listener = null;
}
-
-var signature_to_type = { 'n': 'number',
- 'f': 'function',
- 'b': 'boolean',
- 's': 'string',
- 'o': 'object'
- };
-
-// Returns if the passed arguments match the signature.
-function validateArguments(signature, args) {
- var full_args = Array.prototype.slice.call(args);
-
- // After '?' everything is optional.
- var mandatory_len = signature.indexOf('?') === -1 ? signature.length : signature.indexOf('?');
-
- if (full_args.length < mandatory_len)
- return false;
-
- // Mandatory arguments.
- for (var i = 0; i < mandatory_len; i++) {
- if (typeof full_args[i] !== signature_to_type[signature[i]] || full_args[i] === null)
- return false;
- }
-
- // Optional args may be null.
- for (var i = mandatory_len; i < full_args.length && i < signature.length - 1; i++) {
- if (full_args[i] !== null && typeof full_args[i] !== signature_to_type[signature[i + 1]])
- return false;
- }
-
- return true;
-}
-
-function validateObject(object, signature, attributes) {
- for (var i = 0; i < signature.length; i++) {
- if (object.hasOwnProperty(attributes[i]) &&
- typeof object[attributes[i]] !== signature_to_type[signature[i]]) {
- return false;
- }
- }
-
- return true;
-}
-
function validateAddress(address) {
if (typeof address !== 'string')
return false;
return copiedDevices;
};
+var handleBondedDevice = function(msg) {
+ var device = new BluetoothDevice(msg);
+ adapter.addDevice(device, false);
+};
+
var handleDeviceFound = function(msg) {
var device = new BluetoothDevice(msg);
var is_new = adapter.addDevice(device, msg.found_on_discovery);
var handleRFCOMMSocketAccept = function(msg) {
for (var i in adapter.service_handlers) {
var server = adapter.service_handlers[i];
- if (server.channel === msg.channel) {
+ // FIXME(clecou) BlueZ4 backend compares rfcomm channel number but this parameter
+ // is not available in Tizen C API so we check socket fd.
+ // A better approach would be to adapt backends instances to have a single JSON protocol.
+ if (server.channel === msg.channel || server.server_fd === msg.socket_fd) {
var j = adapter.indexOfDevice(adapter.known_devices, msg.peer);
var peer = adapter.known_devices[j];
}
BluetoothAdapter.prototype.setName = function(name, successCallback, errorCallback) {
- if (!validateArguments('s?ff', arguments)) {
+ if (!xwalk.utils.validateArguments('s?ff', arguments)) {
throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
}
};
BluetoothAdapter.prototype.setPowered = function(state, successCallback, errorCallback) {
- if (!validateArguments('b?ff', arguments)) {
+ if (!xwalk.utils.validateArguments('b?ff', arguments)) {
throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
}
};
BluetoothAdapter.prototype.setVisible = function(mode, successCallback, errorCallback, timeout) {
- if (!validateArguments('b?ffn', arguments)) {
+ if (!xwalk.utils.validateArguments('b?ffn', arguments)) {
throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
}
};
BluetoothAdapter.prototype.discoverDevices = function(discoverySuccessCallback, errorCallback) {
- if (!validateArguments('o?f', arguments)) {
+ if (!xwalk.utils.validateArguments('o?f', arguments)) {
throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
}
- if (!validateObject(discoverySuccessCallback, 'ffff',
- ['onstarted', 'ondevicefound', 'ondevicedisappeared', 'onfinished'])) {
+ if (!xwalk.utils.validateObject(discoverySuccessCallback, 'ffff',
+ ['onstarted', 'ondevicefound', 'ondevicedisappeared', 'onfinished'])) {
throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
}
};
BluetoothAdapter.prototype.stopDiscovery = function(successCallback, errorCallback) {
- if (!validateArguments('?ff', arguments)) {
+ if (!xwalk.utils.validateArguments('?ff', arguments)) {
throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
}
};
BluetoothAdapter.prototype.getKnownDevices = function(deviceArraySuccessCallback, errorCallback) {
- if (!validateArguments('f?f', arguments)) {
+ if (!xwalk.utils.validateArguments('f?f', arguments)) {
throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
}
};
BluetoothAdapter.prototype.getDevice = function(address, deviceSuccessCallback, errorCallback) {
- if (!validateArguments('sf?f', arguments)) {
+ if (!xwalk.utils.validateArguments('sf?f', arguments)) {
throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
}
};
BluetoothAdapter.prototype.createBonding = function(address, successCallback, errorCallback) {
- if (!validateArguments('sf?f', arguments)) {
+ if (!xwalk.utils.validateArguments('sf?f', arguments)) {
throw new tizen.WebAPIError(tizen.WebAPIException.TYPE_MISMATCH_ERR);
}
}
}
+ // FIXME(clecou) Update known device state here when using C API Tizen backend
+ // BlueZ backends update the device state automatically when catching dbus signals.
+ // A better approach would be to adapt backends instances to have a single JSON protocol.
+ if (result.capi)
+ _addConstProperty(adapter.known_devices[i], 'isBonded', true);
+
successCallback(cb_device);
}
});
};
BluetoothAdapter.prototype.destroyBonding = function(address, successCallback, errorCallback) {
- if (!validateArguments('s?ff', arguments)) {
+ if (!xwalk.utils.validateArguments('s?ff', arguments)) {
throw new tizen.WebAPIError(tizen.WebAPIException.TYPE_MISMATCH_ERR);
}
}
}
+ // FIXME(clecou) Update known device state here when using C API Tizen backend
+ // BlueZ backends update the device state automatically when catching dbus signals
+ // A better approach would be to adapt backends instances to have a single JSON protocol.
+ if (result.capi)
+ _addConstProperty(adapter.known_devices[i], 'isBonded', false);
+
successCallback(cb_device);
}
});
BluetoothAdapter.prototype.registerRFCOMMServiceByUUID =
function(uuid, name, serviceSuccessCallback, errorCallback) {
- if (!validateArguments('ssf?f', arguments)) {
+ if (!xwalk.utils.validateArguments('ssf?f', arguments)) {
throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
}
};
BluetoothAdapter.prototype.setChangeListener = function(listener) {
- if (!validateArguments('o', arguments)) {
+ if (!xwalk.utils.validateArguments('o', arguments)) {
throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
}
- if (!validateObject(listener, 'fff',
- ['onstatechanged', 'onnamechanged', 'onvisibilitychanged'])) {
+ if (!xwalk.utils.validateObject(listener, 'fff',
+ ['onstatechanged', 'onnamechanged', 'onvisibilitychanged'])) {
throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
}
_addConstProperty(this, 'address', msg.Address);
_addConstProperty(this, 'deviceClass', new BluetoothClass());
- _addConstProperty(this.deviceClass, 'minor', (msg.Class >> 2) & _deviceClassMask.MINOR);
- _addConstProperty(this.deviceClass, 'major', (msg.Class >> 8) & _deviceClassMask.MAJOR);
+ _addConstProperty(this.deviceClass, 'minor', (msg.ClassMinor >> 2) & _deviceClassMask.MINOR);
+ _addConstProperty(this.deviceClass, 'major', msg.ClassMajor & _deviceClassMask.MAJOR);
_addConstProperty(this, 'isBonded', (msg.Paired == 'true') ? true : false);
_addConstProperty(this, 'isTrusted', (msg.Trusted == 'true') ? true : false);
_addConstProperty(this, 'isConnected', (msg.Connected == 'true') ? true : false);
- // Parse UUIDs
- var uuids_array = [];
+
if (msg.UUIDs) {
- uuids_array = msg.UUIDs.substring(msg.UUIDs.indexOf('[') + 1,
- msg.UUIDs.indexOf(']')).split(',');
- for (var i = 0; i < uuids_array.length; i++) {
- uuids_array[i] = uuids_array[i].substring(2, uuids_array[i].length - 1);
+ if (typeof msg.UUIDs === 'string') {
+ // FIXME(clecou) BlueZ backend sends a string to convert it into an array
+ // A better approach would be to adapt backends instances to have a single JSON protocol.
+ var uuids_array = [];
+ uuids_array = msg.UUIDs.substring(msg.UUIDs.indexOf('[') + 1,
+ msg.UUIDs.indexOf(']')).split(',');
+ for (var i = 0; i < uuids_array.length; i++) {
+ uuids_array[i] = uuids_array[i].substring(2, uuids_array[i].length - 1);
+ }
+ _addConstProperty(this, 'uuids', uuids_array);
+ } else {
+ // Tizen C API backend directly sends an array
+ _addConstProperty(this, 'uuids', msg.UUIDs);
}
}
- _addConstProperty(this, 'uuids', uuids_array);
- var services = (msg.Class >> 13) & _deviceClassMask.SERVICE;
+ var services = (msg.ClassService >> 13) & _deviceClassMask.SERVICE;
var services_array = [];
// 11 is the number of bits in _deviceClassMask.SERVICE
}
BluetoothDevice.prototype.connectToServiceByUUID =
- function(uuid, socketSuccessCallback, errorCallback) {};
+ function(uuid, socketSuccessCallback, errorCallback) {
+
+ if (!xwalk.utils.validateArguments('sf?f', arguments)) {
+ throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
+ }
+
+ if (adapter.checkServiceAvailability(errorCallback))
+ return;
+
+ var uuid_found = false;
+ for (var i = 0; i < this.uuids.length; i++) {
+ if (this.uuids[i] == uuid) {
+ uuid_found = true;
+ break;
+ }
+ }
+ if (uuid_found == false) {
+ var error = new tizen.WebAPIError(tizen.WebAPIException.NOT_FOUND_ERR);
+ errorCallback(error);
+ }
+
+ var msg = {
+ 'cmd': 'ConnectToService',
+ 'uuid': uuid,
+ 'address' : this.address
+ };
+
+ postMessage(msg, function(result) {
+ if (result.error != 0) {
+ if (errorCallback) {
+ var error = new tizen.WebAPIError(tizen.WebAPIException.UNKNOWN_ERR);
+ errorCallback(error);
+ }
+
+ throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
+ return;
+ }
+
+ if (socketSuccessCallback) {
+ var socket_cb = new BluetoothSocket(result.uuid, this, result);
+ socketSuccessCallback(socket_cb);
+ }
+ });
+};
BluetoothDevice.prototype._clone = function() {
var clone = new BluetoothDevice();
BluetoothSocket.prototype.writeData = function(data) {
+ // make sure that socket is connected and opened.
+ if (this.state == BluetoothSocketState.CLOSE) {
+ throw new tizen.WebAPIException(tizen.WebAPIException.UNKNOWN_ERR);
+ return;
+ }
+
var msg = {
'cmd': 'SocketWriteData',
'data': data,
};
postMessage(msg, function(result) {
- if (result.error)
+ if (result.error) {
console.log('Can\'t close socket (' + this.socket_fd + ').');
+ throw new tizen.WebAPIException(tizen.WebAPIException.UNKNOWN_ERR);
+ return;
+ }
+
+ // FIXME(clecou) Update socket object state only when using Tizen C API backend.
+ // BlueZ4 backend independently updates socket state based on a dbus callback mechanism.
+ // A better approach would be to adapt backends instances to have a single JSON protocol.
+ if (result.capi) {
+ for (var i in adapter.sockets) {
+ var socket = adapter.sockets[i];
+ if (socket.socket_fd === msg.socket_fd) {
+ if (socket.onclose && typeof socket.onmessage === 'function') {
+ _addConstProperty(adapter.sockets[i], 'state', BluetoothSocketState.CLOSE);
+ socket.onclose();
+ }
+ }
+ }
+ }
});
};
}
BluetoothServiceHandler.prototype.unregister = function(successCallback, errorCallback) {
- if (!validateArguments('?ff', arguments)) {
+ if (!xwalk.utils.validateArguments('?ff', arguments)) {
throw new tizen.WebAPIError(tizen.WebAPIException.TYPE_MISMATCH_ERR);
}
return;
}
- if (successCallback) {
+ _addConstProperty(this, 'isConnected', false);
+ if (successCallback)
successCallback();
- }
});
};
#include "bluetooth/bluetooth_extension.h"
-#if defined(TIZEN)
+#if defined(TIZEN_CAPI_BT)
#include <bluetooth.h>
-#endif
-
+#include "bluetooth/bluetooth_instance_capi.h"
+#else
#include "bluetooth/bluetooth_instance.h"
+#endif
common::Extension* CreateExtension() {
-#if defined(TIZEN)
- int init = bt_initialize();
- if (init != BT_ERROR_NONE)
- g_printerr("\n\nCouldn't initialize Bluetooth module.");
+#if defined(TIZEN_CAPI_BT)
+ CAPI(bt_initialize());
#endif
return new BluetoothExtension;
#include "bluetooth/bluetooth_instance.h"
-#if defined(TIZEN)
-#include <bluetooth.h>
-#endif
-
#include <sys/types.h>
#include <sys/socket.h>
picojson::value::object& o) {
if (!strcmp(key, "Class")) {
guint32 class_id = g_variant_get_uint32(value);
- o[key] = picojson::value(static_cast<double>(class_id));
+ o["ClassMajor"] = picojson::value \
+ (static_cast<double>((class_id & 0x00001F00) >> 8));
+ o["ClassMinor"] = picojson::value \
+ (static_cast<double>(class_id & 0x000000FC));
+ o["ClassService"] = picojson::value\
+ (static_cast<double>(class_id & 0x00FF0000));
} else if (!strcmp(key, "RSSI")) {
gint16 class_id = g_variant_get_int16(value);
o[key] = picojson::value(static_cast<double>(class_id));
bool powered = msg.get("value").get<bool>();
int error = 0;
-#if defined(TIZEN)
- if (powered)
- error = bt_adapter_enable();
- else
- error = bt_adapter_disable();
-#else
-
OnAdapterPropertySetData* property_set_callback_data_ =
new OnAdapterPropertySetData;
property_set_callback_data_->property = std::string("Powered");
G_DBUS_CALL_FLAGS_NONE, 5000, all_pending_,
OnAdapterPropertySetThunk,
property_set_callback_data_);
-#endif
// Reply right away in case of error, or powered off.
if (error || powered == false) {
g_object_unref(it->second);
g_bus_unwatch_name(name_watch_id_);
-
-#if defined(TIZEN)
- bt_deinitialize();
-#endif
}
void BluetoothInstance::PlatformInitialize() {
picojson::value::object& o) {
if (!strcmp(key, "Class")) {
guint32 class_id = g_variant_get_uint32(value);
- o[key] = picojson::value(static_cast<double>(class_id));
+ o["ClassMajor"] = picojson::value \
+ (static_cast<double>((class_id & 0x00001F00) >> 8));
+ o["ClassMinor"] = picojson::value \
+ (static_cast<double>(class_id & 0x000000FC));
+ o["ClassService"] = picojson::value\
+ (static_cast<double>(class_id & 0x00FF0000));
} else if (!strcmp(key, "RSSI")) {
gint16 class_id = g_variant_get_int16(value);
o[key] = picojson::value(static_cast<double>(class_id));
this);
}
-picojson::value BluetoothInstance::HandleGetDefaultAdapter(
+void BluetoothInstance::HandleGetDefaultAdapter(
const picojson::value& msg) {
if (adapter_info_.empty())
- return picojson::value();
+ return;
picojson::value::object o;
o["cmd"] = picojson::value("");
if (!is_js_context_initialized_)
is_js_context_initialized_ = true;
- picojson::value v(o);
- return v;
+ InternalSetSyncReply(picojson::value(o));
}
GDBusProxy* BluetoothInstance::CreateDeviceProxy(GAsyncResult* res) {
--- /dev/null
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "bluetooth/bluetooth_instance_capi.h"
+
+#include <pthread.h>
+
+#include "common/picojson.h"
+
+namespace {
+
+inline const char* BoolToString(bool b) {
+ return b ? "true" : "false";
+}
+
+static void* event_loop(void* arg) {
+ GMainLoop* event_loop;
+ event_loop = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(event_loop);
+ return NULL;
+}
+
+} // anonymous namespace
+
+BluetoothInstance::BluetoothInstance()
+ : is_js_context_initialized_(false),
+ adapter_enabled_(false),
+ js_reply_needed_(false),
+ stop_discovery_from_js_(false) {
+
+ // we need a thread for running the main loop
+ // and catching bluetooth glib signals
+ pthread_t event_thread;
+ pthread_attr_t thread_attr;
+ pthread_attr_init(&thread_attr);
+ pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
+ event_thread = pthread_create(&event_thread, &thread_attr,
+ event_loop, NULL);
+
+ InitializeAdapter();
+}
+
+BluetoothInstance::~BluetoothInstance() {
+ UninitializeAdapter();
+ CAPI(bt_deinitialize());
+}
+
+void BluetoothInstance::HandleMessage(const char* message) {
+ picojson::value v;
+
+ std::string err;
+ picojson::parse(v, message, message + strlen(message), &err);
+ if (!err.empty()) {
+ LOG_ERR("Ignoring message");
+ return;
+ }
+
+ std::string cmd = v.get("cmd").to_str();
+ if (cmd == "DiscoverDevices")
+ HandleDiscoverDevices(v);
+ else if (cmd == "StopDiscovery")
+ HandleStopDiscovery(v);
+ else if (cmd == "SetAdapterProperty")
+ HandleSetAdapterProperty(v);
+ else if (cmd == "CreateBonding")
+ HandleCreateBonding(v);
+ else if (cmd == "DestroyBonding")
+ HandleDestroyBonding(v);
+ else if (cmd == "RFCOMMListen")
+ HandleRFCOMMListen(v);
+ else if (cmd == "CloseSocket")
+ HandleCloseSocket(v);
+ else if (cmd == "UnregisterServer")
+ HandleUnregisterServer(v);
+}
+
+void BluetoothInstance::HandleSyncMessage(const char* message) {
+ picojson::value v;
+
+ std::string err;
+ picojson::parse(v, message, message + strlen(message), &err);
+ if (!err.empty()) {
+ LOG_ERR("Ignoring Sync message.");
+ return;
+ }
+
+ std::string cmd = v.get("cmd").to_str();
+ if (cmd == "GetDefaultAdapter")
+ HandleGetDefaultAdapter(v);
+ else if (cmd == "SocketWriteData")
+ HandleSocketWriteData(v);
+}
+
+void BluetoothInstance::OnStateChanged(int result,
+ bt_adapter_state_e adapter_state, void* user_data) {
+ BluetoothInstance* obj = static_cast<BluetoothInstance*>(user_data);
+ if (!obj) {
+ LOG_ERR("user_data is NULL");
+ return;
+ }
+
+ obj->adapter_enabled_ = (adapter_state == BT_ADAPTER_ENABLED) ? true : false;
+
+ if (obj->js_reply_needed_) {
+ // FIXME(clecou) directly call 'GetDefaultAdapter' once NTB is integrated.
+ // After testing, 100 ms is necessary to really get a powered adapter.
+ g_timeout_add(100, obj->GetDefaultAdapter, obj);
+ return;
+ }
+
+ picojson::value::object o;
+
+ o["cmd"] = picojson::value("");
+ o["reply_id"] = picojson::value(obj->callbacks_id_map_["Powered"]);
+ if (result)
+ o["error"] = picojson::value(static_cast<double>(1));
+ else
+ o["error"] = picojson::value(static_cast<double>(0));
+
+ obj->InternalPostMessage(picojson::value(o));
+ obj->callbacks_id_map_.erase("Powered");
+}
+
+void BluetoothInstance::OnNameChanged(char* name, void* user_data) {
+ BluetoothInstance* obj = static_cast<BluetoothInstance*>(user_data);
+ if (!obj) {
+ LOG_ERR("user_data is NULL");
+ return;
+ }
+
+ picojson::value::object o;
+
+ o["error"] = picojson::value(static_cast<double>(0));
+ o["cmd"] = picojson::value("");
+ o["reply_id"] = picojson::value(obj->callbacks_id_map_["Name"]);
+ obj->InternalPostMessage(picojson::value(o));
+ obj->callbacks_id_map_.erase("Name");
+}
+
+void BluetoothInstance::OnVisibilityChanged(int result,
+ bt_adapter_visibility_mode_e visibility_mode, void* user_data) {
+ BluetoothInstance* obj = static_cast<BluetoothInstance*>(user_data);
+ if (!obj) {
+ LOG_ERR("user_data is NULL");
+ return;
+ }
+
+ picojson::value::object o;
+
+ o["cmd"] = picojson::value("");
+ o["reply_id"] = picojson::value(obj->callbacks_id_map_["Discoverable"]);
+ if (result)
+ o["error"] = picojson::value(static_cast<double>(1));
+ else
+ o["error"] = picojson::value(static_cast<double>(0));
+
+ obj->InternalPostMessage(picojson::value(o));
+ obj->callbacks_id_map_.erase("Discoverable");
+}
+
+void BluetoothInstance::OnDiscoveryStateChanged(int result,
+ bt_adapter_device_discovery_state_e discovery_state,
+ bt_adapter_device_discovery_info_s* discovery_info, void* user_data) {
+ BluetoothInstance* obj = static_cast<BluetoothInstance*>(user_data);
+ if (!obj) {
+ LOG_ERR("user_data is NULL");
+ return;
+ }
+
+ picojson::value::object o;
+
+ switch (discovery_state) {
+ case BT_ADAPTER_DEVICE_DISCOVERY_STARTED: {
+ o["cmd"] = picojson::value("");
+ o["reply_id"] = picojson::value(obj->callbacks_id_map_["StartDiscovery"]);
+ if (result) {
+ LOG_ERR(result);
+ o["error"] = picojson::value(static_cast<double>(1));
+ } else {
+ o["error"] = picojson::value(static_cast<double>(0));
+ }
+ obj->InternalPostMessage(picojson::value(o));
+ obj->callbacks_id_map_.erase("StartDiscovery");
+ break;
+ }
+ case BT_ADAPTER_DEVICE_DISCOVERY_FINISHED: {
+ if (obj->stop_discovery_from_js_) {
+ o["cmd"] = picojson::value("");
+ o["reply_id"] =
+ picojson::value(obj->callbacks_id_map_["StopDiscovery"]);
+ if (result) {
+ LOG_ERR(result);
+ o["error"] = picojson::value(static_cast<double>(1));
+ } else {
+ o["error"] = picojson::value(static_cast<double>(0));
+ }
+ } else {
+ // discovery stop was not initiated by JS. It was done by a timeout...
+ o["cmd"] = picojson::value("DiscoveryFinished");
+ }
+ obj->InternalPostMessage(picojson::value(o));
+ obj->callbacks_id_map_.erase("StopDiscovery");
+ obj->stop_discovery_from_js_ = false;
+ break;
+ }
+ case BT_ADAPTER_DEVICE_DISCOVERY_FOUND: {
+ o["Alias"] = picojson::value(discovery_info->remote_name);
+ o["Address"] = picojson::value(discovery_info->remote_address);
+
+ int major = discovery_info->bt_class.major_device_class;
+ int minor = discovery_info->bt_class.minor_device_class;
+ int service_class = discovery_info->bt_class.major_service_class_mask;
+ o["ClassMajor"] = picojson::value(static_cast<double>(major));
+ o["ClassMinor"] = picojson::value(static_cast<double>(minor));
+ o["ClassService"] = picojson::value(static_cast<double>(service_class));
+
+ picojson::array uuids;
+ for (int i = 0; i < discovery_info->service_count; i++)
+ uuids.push_back(picojson::value(discovery_info->service_uuid[i]));
+
+ o["UUIDs"] = picojson::value(uuids);
+
+ bool paired = false;
+ bool trusted = false;
+ bool connected = false;
+
+ if (discovery_info->is_bonded) {
+ bt_device_info_s* device_info = NULL;
+ CAPI(bt_adapter_get_bonded_device_info(discovery_info->remote_address,
+ &device_info));
+ if (!device_info)
+ LOG_ERR("device_info is NULL");
+
+ if (!device_info->is_bonded)
+ LOG_ERR("remote device should be bonded!");
+
+ paired = true;
+ trusted = device_info->is_authorized;
+ connected = device_info->is_connected;
+ CAPI(bt_adapter_free_device_info(device_info));
+ }
+
+ o["Paired"] = picojson::value(BoolToString(paired));
+ o["Trusted"] = picojson::value(BoolToString(trusted));
+ o["Connected"] = picojson::value(BoolToString(connected));
+
+ o["cmd"] = picojson::value("DeviceFound");
+ o["found_on_discovery"] = picojson::value(true);
+
+ obj->InternalPostMessage(picojson::value(o));
+ break;
+ }
+ default:
+ LOG_ERR("Unknown discovery state callback!");
+ break;
+ }
+}
+
+bool BluetoothInstance::OnKnownBondedDevice(bt_device_info_s* device_info,
+ void* user_data) {
+ BluetoothInstance* obj = static_cast<BluetoothInstance*>(user_data);
+ if (!obj) {
+ LOG_ERR("user_data is NULL!");
+ return false;
+ }
+ if (!device_info) {
+ LOG_ERR("device_info is NULL!");
+ return false;
+ }
+
+ picojson::value::object o;
+ char* alias = device_info->remote_name;
+ o["Alias"] = picojson::value(alias);
+
+ char* address = device_info->remote_address;
+ o["Address"] = picojson::value(address);
+
+ int major = device_info->bt_class.major_device_class;
+ int minor = device_info->bt_class.minor_device_class;
+ int service_class = device_info->bt_class.major_service_class_mask;
+ o["ClassMajor"] = picojson::value(static_cast<double>(major));
+ o["ClassMinor"] = picojson::value(static_cast<double>(minor));
+ o["ClassService"] = picojson::value(static_cast<double>(service_class));
+
+ // parse UUIDs supported by remote device
+ picojson::array uuids;
+ for (int i = 0; i < device_info->service_count; i++)
+ uuids.push_back(picojson::value(device_info->service_uuid[i]));
+
+ o["UUIDs"] = picojson::value(uuids);
+ o["Paired"] = picojson::value(BoolToString(device_info->is_bonded));
+ o["Trusted"] = picojson::value(BoolToString(device_info->is_authorized));
+ o["Connected"] = picojson::value(BoolToString(device_info->is_connected));
+ o["reply_id"] = picojson::value("");
+ o["cmd"] = picojson::value("BondedDevice");
+ obj->InternalPostMessage(picojson::value(o));
+ return true;
+}
+
+void BluetoothInstance::OnBondCreated(int result, bt_device_info_s* device_info,
+ void* user_data) {
+ BluetoothInstance* obj = static_cast<BluetoothInstance*>(user_data);
+ if (!obj) {
+ LOG_ERR("user_data is NULL!");
+ return;
+ }
+ if (!device_info) {
+ LOG_ERR("device_info is NULL!");
+ return;
+ }
+
+ picojson::value::object o;
+ o["cmd"] = picojson::value("");
+ o["reply_id"] = picojson::value(obj->callbacks_id_map_["CreateBonding"]);
+ o["capi"] = picojson::value(static_cast<double>(1));
+ if (result) {
+ LOG_ERR("onBondCreated() failed");
+ o["error"] = picojson::value(static_cast<double>(1));
+ } else {
+ o["error"] = picojson::value(static_cast<double>(0));
+ }
+ obj->InternalPostMessage(picojson::value(o));
+ obj->callbacks_id_map_.erase("CreateBonding");
+}
+
+void BluetoothInstance::OnBondDestroyed(int result, char* remote_address,
+ void* user_data) {
+ BluetoothInstance* obj = static_cast<BluetoothInstance*>(user_data);
+ if (!obj) {
+ LOG_ERR("user_data is NULL!");
+ return;
+ }
+
+ if (!remote_address) {
+ LOG_ERR("remote_address is NULL!");
+ return;
+ }
+ picojson::value::object o;
+ o["cmd"] = picojson::value("");
+ o["reply_id"] = picojson::value(obj->callbacks_id_map_["DestroyBonding"]);
+ o["capi"] = picojson::value(static_cast<double>(1));
+ if (result) {
+ LOG_ERR("onBondDestroyed() failed");
+ o["error"] = picojson::value(static_cast<double>(1));
+ } else {
+ o["error"] = picojson::value(static_cast<double>(0));
+ }
+ obj->InternalPostMessage(picojson::value(o));
+ obj->callbacks_id_map_.erase("DestroyBonding");
+}
+
+void BluetoothInstance::OnSocketConnected(int result,
+ bt_socket_connection_state_e connection_state,
+ bt_socket_connection_s* connection,
+ void* user_data) {
+ BluetoothInstance* obj = static_cast<BluetoothInstance*>(user_data);
+ if (!obj) {
+ LOG_ERR("user_data is NULL!");
+ return;
+ }
+ if (!connection) {
+ LOG_ERR("connection is NULL!");
+ return;
+ }
+
+ picojson::value::object o;
+
+ if (result) {
+ LOG_ERR("onSocketConnected() is failed");
+ o["error"] = picojson::value(static_cast<double>(1));
+ }
+
+ if (connection_state == BT_SOCKET_CONNECTED &&
+ connection->local_role == BT_SOCKET_SERVER) {
+ o["cmd"] = picojson::value("RFCOMMSocketAccept");
+ o["uuid"] = picojson::value(connection->service_uuid);
+ o["socket_fd"] =
+ picojson::value(static_cast<double>(connection->socket_fd));
+ o["peer"] = picojson::value(connection->remote_address);
+
+ CAPI(bt_socket_set_data_received_cb(OnSocketHasData, NULL));
+ } else if (connection_state == BT_SOCKET_CONNECTED &&
+ connection->local_role == BT_SOCKET_CLIENT) {
+ o["cmd"] = picojson::value("");
+ o["reply_id"] =
+ picojson::value(obj->callbacks_id_map_["ConnectToService"]);
+ obj->callbacks_id_map_.erase("ConnectToService");
+
+ o["uuid"] = picojson::value(connection->service_uuid);
+ o["socket_fd"] =
+ picojson::value(static_cast<double>(connection->socket_fd));
+
+ CAPI(bt_socket_set_data_received_cb(OnSocketHasData, NULL));
+ } else if (connection_state == BT_SOCKET_DISCONNECTED) {
+ o["cmd"] = picojson::value("");
+ o["reply_id"] =
+ picojson::value(obj->callbacks_id_map_["RFCOMMsocketDestroy"]);
+ obj->callbacks_id_map_.erase("RFCOMMsocketDestroy");
+ o["socket_fd"] =
+ picojson::value(static_cast<double>(connection->socket_fd));
+ } else {
+ LOG_ERR("Unknown role!");
+ return;
+ }
+ obj->InternalPostMessage(picojson::value(o));
+}
+
+void BluetoothInstance::OnSocketHasData(bt_socket_received_data_s* data,
+ void* user_data) {
+ BluetoothInstance* obj = static_cast<BluetoothInstance*>(user_data);
+ if (!obj) {
+ LOG_ERR("user_data is NULL");
+ return;
+ }
+ if (!data) {
+ LOG_ERR("data is NULL");
+ return;
+ }
+ picojson::value::object o;
+ o["cmd"] = picojson::value("SocketHasData");
+ o["socket_fd"] = picojson::value(static_cast<double>(data->socket_fd));
+ o["data"] = picojson::value(static_cast<std::string>(data->data));
+ obj->InternalPostMessage(picojson::value(o));
+}
+
+gboolean BluetoothInstance::GetDefaultAdapter(gpointer user_data) {
+ BluetoothInstance* obj = static_cast<BluetoothInstance*>(user_data);
+ if (!obj) {
+ LOG_ERR("user_data is NULL");
+ return TRUE;
+ }
+
+ picojson::value::object o;
+
+ char* name = NULL;
+ CAPI(bt_adapter_get_name(&name));
+ if (!name)
+ return TRUE;
+ o["name"] = picojson::value(name);
+
+ char* address = NULL;
+ CAPI(bt_adapter_get_address(&address));
+ if (!address)
+ return TRUE;
+ o["address"] = picojson::value(address);
+
+ bool powered, visible = false;
+
+ if (obj->adapter_enabled_) {
+ powered = true;
+
+ bt_adapter_visibility_mode_e mode =
+ BT_ADAPTER_VISIBILITY_MODE_NON_DISCOVERABLE;
+
+ CAPI(bt_adapter_get_visibility(&mode, NULL));
+ visible = (mode > 0) ? true : false;
+ }
+ o["powered"] = picojson::value(powered);
+ o["visible"] = picojson::value(visible);
+
+ // This is the JS API entry point, so we should clean our message queue
+ // on the next PostMessage call.
+ if (!obj->is_js_context_initialized_)
+ obj->is_js_context_initialized_ = true;
+
+ obj->InternalSetSyncReply(picojson::value(o));
+
+ // Retrieve already bonded devices linked to the adapter in order to
+ // fill known_devices array on javascript side.
+ CAPI(bt_adapter_foreach_bonded_device(OnKnownBondedDevice, obj));
+
+ obj->js_reply_needed_ = false;
+
+ return FALSE;
+}
+
+void BluetoothInstance::InitializeAdapter() {
+ // register C API bluetooth callbacks
+ CAPI(bt_adapter_set_state_changed_cb(OnStateChanged, this));
+ CAPI(bt_adapter_set_name_changed_cb(OnNameChanged, this));
+ CAPI(bt_adapter_set_visibility_mode_changed_cb(OnVisibilityChanged, this));
+ CAPI(bt_adapter_set_device_discovery_state_changed_cb(OnDiscoveryStateChanged,
+ this));
+ CAPI(bt_device_set_bond_created_cb(OnBondCreated, this));
+ CAPI(bt_device_set_bond_destroyed_cb(OnBondDestroyed, this));
+
+ bt_adapter_state_e state = BT_ADAPTER_DISABLED;
+ CAPI(bt_adapter_get_state(&state));
+
+ // Most of the C API functions require as precondition to previously had
+ // called bt_adapter_enable(). So if adapter is turned OFF, we enable it.
+ if (state == BT_ADAPTER_DISABLED) {
+ CAPI(bt_adapter_enable());
+ } else {
+ adapter_enabled_ = true;
+ }
+}
+
+void BluetoothInstance::UninitializeAdapter() {
+ // unregister C API bluetooth callbacks
+ CAPI(bt_adapter_unset_state_changed_cb());
+ CAPI(bt_adapter_unset_name_changed_cb());
+ CAPI(bt_adapter_unset_visibility_mode_changed_cb());
+ CAPI(bt_adapter_unset_device_discovery_state_changed_cb());
+ CAPI(bt_device_unset_bond_created_cb());
+ CAPI(bt_device_unset_bond_destroyed_cb());
+ CAPI(bt_socket_unset_connection_state_changed_cb());
+ CAPI(bt_socket_unset_data_received_cb());
+}
+
+void BluetoothInstance::HandleGetDefaultAdapter(const picojson::value& msg) {
+ if (!adapter_enabled_) {
+ js_reply_needed_ = true;
+ return;
+ }
+
+ GetDefaultAdapter(this);
+}
+
+void BluetoothInstance::HandleSetAdapterProperty(const picojson::value& msg) {
+ picojson::value::object o;
+
+ std::string property = msg.get("property").to_str();
+ callbacks_id_map_[property] = msg.get("reply_id").to_str();
+
+ if (property == "Powered") {
+ bool power = msg.get("value").get<bool>();
+ if (power)
+ CAPI(bt_adapter_enable());
+ else
+ CAPI(bt_adapter_disable());
+ } else if (property == "Name") {
+ std::string name = msg.get("value").to_str();
+ CAPI(bt_adapter_set_name(name.c_str()));
+ } else if (property == "Discoverable") {
+ bool visible = msg.get("value").get<bool>();
+ int timeout = static_cast<int>(msg.get("timeout").get<double>());
+
+ bt_adapter_visibility_mode_e discoverable_mode =
+ BT_ADAPTER_VISIBILITY_MODE_NON_DISCOVERABLE;
+ if (visible) {
+ if (timeout == 0)
+ discoverable_mode = BT_ADAPTER_VISIBILITY_MODE_GENERAL_DISCOVERABLE;
+ else
+ discoverable_mode = BT_ADAPTER_VISIBILITY_MODE_LIMITED_DISCOVERABLE;
+ }
+ CAPI(bt_adapter_set_visibility(discoverable_mode, timeout));
+ } else {
+ LOG_ERR("bad property received!");
+ }
+}
+
+void BluetoothInstance::HandleDiscoverDevices(const picojson::value& msg) {
+ callbacks_id_map_["StartDiscovery"] = msg.get("reply_id").to_str();
+ CAPI(bt_adapter_start_device_discovery());
+}
+
+void BluetoothInstance::HandleStopDiscovery(const picojson::value& msg) {
+ callbacks_id_map_["StopDiscovery"] = msg.get("reply_id").to_str();
+
+ bool is_discovering = false;
+ CAPI(bt_adapter_is_discovering(&is_discovering));
+ if (!is_discovering)
+ return;
+
+ stop_discovery_from_js_ = true;
+ CAPI(bt_adapter_stop_device_discovery());
+}
+
+void BluetoothInstance::HandleCreateBonding(const picojson::value& msg) {
+ callbacks_id_map_["CreateBonding"] = msg.get("reply_id").to_str();
+ std::string address = msg.get("address").to_str();
+ CAPI(bt_device_create_bond(address.c_str()));
+}
+
+void BluetoothInstance::HandleDestroyBonding(const picojson::value& msg) {
+ callbacks_id_map_["DestroyBonding"] = msg.get("reply_id").to_str();
+ std::string address = msg.get("address").to_str();
+ CAPI(bt_device_destroy_bond(address.c_str()));
+}
+
+void BluetoothInstance::HandleRFCOMMListen(const picojson::value& msg) {
+ picojson::value::object o;
+
+ int socket_fd = 0;
+ int error = 0;
+
+ CAPI_ERR(
+ bt_socket_create_rfcomm(msg.get("uuid").to_str().c_str(), &socket_fd),
+ error);
+ if (error) {
+ o["error"] = picojson::value(static_cast<double>(1));
+ InternalPostMessage(picojson::value(o));
+ return;
+ }
+
+ CAPI_ERR(bt_socket_listen_and_accept_rfcomm(socket_fd, 0), error);
+ if (error) {
+ o["error"] = picojson::value(static_cast<double>(1));
+ InternalPostMessage(picojson::value(o));
+ return;
+ }
+
+ CAPI_ERR(bt_socket_set_connection_state_changed_cb(OnSocketConnected, this),
+ error);
+ if (error) {
+ o["error"] = picojson::value(static_cast<double>(1));
+ InternalPostMessage(picojson::value(o));
+ return;
+ }
+
+ o["error"] = picojson::value(static_cast<double>(0));
+ // give the listened socket to JS and store it in service_handler
+ o["socket_fd"] = picojson::value(static_cast<double>(socket_fd));
+ InternalPostMessage(picojson::value(o));
+}
+
+void BluetoothInstance::HandleConnectToService(const picojson::value& msg) {
+ callbacks_id_map_["ConnectToService"] = msg.get("reply_id").to_str();
+ int error = 0;
+
+ CAPI_ERR(
+ bt_socket_connect_rfcomm(msg.get("address").to_str().c_str(),
+ msg.get("uuid").to_str().c_str()),
+ error);
+ if (!error)
+ CAPI(bt_socket_set_connection_state_changed_cb(OnSocketConnected, this));
+}
+
+void BluetoothInstance::HandleSocketWriteData(const picojson::value& msg) {
+ picojson::value::object o;
+ std::string data = msg.get("data").to_str();
+ int socket = static_cast<int>(msg.get("socket_fd").get<double>());
+
+ CAPI(bt_socket_send_data(socket, data.c_str(),
+ static_cast<int>(data.size())));
+ o["size"] = picojson::value(static_cast<double>(data.size()));
+
+ InternalSetSyncReply(picojson::value(o));
+}
+
+void BluetoothInstance::HandleCloseSocket(const picojson::value& msg) {
+ picojson::value::object o;
+ int error = 0;
+ int socket = static_cast<int>(msg.get("socket_fd").get<double>());
+
+ CAPI_ERR(bt_socket_disconnect_rfcomm(socket), error);
+ if (!error)
+ o["error"] = picojson::value(static_cast<double>(0));
+ else
+ o["error"] = picojson::value(static_cast<double>(1));
+
+
+ o["cmd"] = picojson::value("");
+ o["reply_id"] = msg.get("reply_id");
+ o["capi"] = picojson::value(static_cast<double>(1));
+ InternalPostMessage(picojson::value(o));
+}
+
+void BluetoothInstance::HandleUnregisterServer(const picojson::value& msg) {
+ callbacks_id_map_["RFCOMMsocketDestroy"] = msg.get("reply_id").to_str();
+ int socket = static_cast<int>(msg.get("server_fd").get<double>());
+
+ CAPI(bt_socket_destroy_rfcomm(socket));
+}
+
+void BluetoothInstance::FlushPendingMessages() {
+ // Flush previous pending messages.
+ if (queue_.empty())
+ return;
+
+ MessageQueue::iterator it;
+ for (it = queue_.begin(); it != queue_.end(); ++it)
+ PostMessage((*it).serialize().c_str());
+}
+
+void BluetoothInstance::InternalPostMessage(picojson::value v) {
+ // If the JavaScript 'context' hasn't been initialized yet (i.e. the C++
+ // backend was loaded and it is already executing but
+ // tizen.bluetooth.getDefaultAdapter() hasn't been called so far), we need to
+ // queue the PostMessage calls and defer them until the default adapter is set
+ // on the JS side. That will guarantee the correct callbacks will be called,
+ // and on the right order, only after tizen.bluetooth.getDefaultAdapter() is
+ // called.
+
+ if (!is_js_context_initialized_) {
+ queue_.push_back(v);
+ return;
+ }
+
+ FlushPendingMessages();
+ PostMessage(v.serialize().c_str());
+}
+
+void BluetoothInstance::InternalSetSyncReply(picojson::value v) {
+ SendSyncReply(v.serialize().c_str());
+
+ FlushPendingMessages();
+}
--- /dev/null
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BLUETOOTH_BLUETOOTH_INSTANCE_CAPI_H_
+#define BLUETOOTH_BLUETOOTH_INSTANCE_CAPI_H_
+
+#include <bluetooth.h>
+#include <glib.h>
+
+#include <iostream>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "common/extension.h"
+#include "common/picojson.h"
+
+#define LOG_ERR(msg) std::cerr << "[Error] " << msg << std::endl
+
+// Macros interfacing with C code from Bluetooth API.
+#define CAPI(fnc) \
+ do { \
+ int _er = (fnc); \
+ if (_er != BT_ERROR_NONE) { \
+ LOG_ERR(#fnc " failed"); \
+ } \
+ } while (0)
+
+// same CAPI macro providing error code
+#define CAPI_ERR(fnc, _er) \
+ do { \
+ _er = (fnc); \
+ if (_er != BT_ERROR_NONE) { \
+ LOG_ERR(#fnc " failed"); \
+ } \
+ } while (0)
+
+namespace picojson {
+
+class value;
+
+} // namespace picojson
+
+class BluetoothInstance : public common::Instance {
+ public:
+ BluetoothInstance();
+ ~BluetoothInstance();
+
+ private:
+ virtual void HandleMessage(const char* msg);
+ virtual void HandleSyncMessage(const char* msg);
+
+ void InitializeAdapter();
+ void UninitializeAdapter();
+
+ void HandleDiscoverDevices(const picojson::value& msg);
+ void HandleStopDiscovery(const picojson::value& msg);
+ void HandleGetDefaultAdapter(const picojson::value& msg);
+ void HandleSetAdapterProperty(const picojson::value& msg);
+ void HandleCreateBonding(const picojson::value& msg);
+ void HandleDestroyBonding(const picojson::value& msg);
+ void HandleRFCOMMListen(const picojson::value& msg);
+ void HandleConnectToService(const picojson::value& msg);
+ void HandleSocketWriteData(const picojson::value& msg);
+ void HandleCloseSocket(const picojson::value& msg);
+ void HandleUnregisterServer(const picojson::value& msg);
+
+ void InternalPostMessage(picojson::value v);
+ void InternalSetSyncReply(picojson::value v);
+ void FlushPendingMessages();
+
+ static gboolean GetDefaultAdapter(gpointer user_data);
+
+ static void OnStateChanged(int result, bt_adapter_state_e adapter_state,
+ void* user_data);
+
+ static void OnNameChanged(char* name, void* user_data);
+
+ static void OnVisibilityChanged(int result,
+ bt_adapter_visibility_mode_e visibility_mode, void* user_data);
+
+ static void OnDiscoveryStateChanged(int result,
+ bt_adapter_device_discovery_state_e discovery_state,
+ bt_adapter_device_discovery_info_s* discovery_info, void* user_data);
+
+ static void OnBondCreated(int result, bt_device_info_s* device_info,
+ void* user_data);
+
+ static void OnBondDestroyed(int result, char* remote_address,
+ void* user_data);
+
+ static bool OnKnownBondedDevice(bt_device_info_s* device_info,
+ void* user_data);
+
+ static void OnSocketConnected(int result,
+ bt_socket_connection_state_e connection_state,
+ bt_socket_connection_s* connection, void* user_data);
+
+ static void OnSocketHasData(bt_socket_received_data_s* data, void* user_data);
+
+ // Map JS reply_id to a C API callback
+ std::map<std::string, std::string> callbacks_id_map_;
+
+ typedef std::vector<picojson::value> MessageQueue;
+ MessageQueue queue_;
+
+ bool is_js_context_initialized_;
+ bool adapter_enabled_;
+ bool js_reply_needed_;
+ bool stop_discovery_from_js_;
+};
+
+#endif // BLUETOOTH_BLUETOOTH_INSTANCE_CAPI_H_
<body>
-<input type="text" size=50 id="text">
+<input type="text" size=90 id="text">
<br>
-<textarea cols=50 rows=20 id="output"></textarea>
+<textarea cols=80 rows=25 id="output"></textarea>
<br>
<button id="button1">Click 1</button>
<button id="button2">Click 2</button>
<button id="button3">Click 3</button>
<button id="button4">Click 4</button>
+<button id="button5">Click 5</button>
+<button id="button6">Click 6</button>
+<button id="button7">Click 7</button>
+<button id="button8">Click 8</button>
+<button id="button9">Click 9</button>
+<button id="button10">Click 10</button>
+<button id="button11">Click 11</button>
+<button id="button12">Click 12</button>
+<button id="button13">Click 13</button>
+<button id="button14">Click 14</button>
<pre id="console"></pre>
<script src="js/js-test-pre.js"></script>
var output = document.getElementById("output");
var adapter = tizen.bluetooth.getDefaultAdapter();
+output.value += '\n## Default adapter ##';
+output.value += '\nadapter name: ' + adapter.name;
+output.value += ' / address ' + adapter.address;
var devices = [];
var device_index = 0;
function handle(button, text, callback) {
- var b = document.getElementById(button);
- b.innerText = text;
- b.addEventListener("click", callback);
+ var b = document.getElementById(button);
+ b.innerText = text;
+ b.addEventListener("click", callback);
}
var oldName = "";
var oldVisible = false;
+
+var CHAT_SERVICE_UUID = "5BCE9431-6C75-32AB-AFE0-2EC108A30860";
+var chatServiceHandler = null;
+var clientSocket = null;
+
+function validateAddress(address) {
+ if (typeof address !== 'string')
+ return false;
+
+ var regExp = /([\dA-F][\dA-F]:){5}[\dA-F][\dA-F]/i;
+ if (!address.match(regExp))
+ return false;
+
+ return true;
+}
+
+function getAddressFromInputText() {
+ var address = document.getElementById("text");
+
+ if (address.value == "") {
+ output.value += '\nPlease enter a MAC address in input area first !!!';
+ return;
+ }
+
+ if (!validateAddress(address.value))
+ output.value += '\nMAC address is bad !!!';
+
+ return address.value;
+}
+
+function runPowerTest() {
+ oldState = adapter.powered;
+ output.value += '\nAdapter Power : ' + adapter.powered;
+ if(oldState == false)
+ adapter.setPowered(true, onChangedAdapterState, onErrorCallback);
+ else
+ adapter.setPowered(false, onChangedAdapterState, onErrorCallback);
+};
+
+var onChangedAdapterState = function() {
+ output.value += '\nAdapter New State: ' + adapter.powered;
+}
+
+function runNameTest() {
+ output.value += '\nCurrent adapter Name: ' + adapter.name;
+ adapter.setName("FooBar", onChangedAdapterName, onErrorCallback);
+};
+
+var onChangedAdapterName = function() {
+ output.value += '\nNew adapter Name: ' + adapter.name;
+}
+
+var onChangedAdapterVisible = function() {
+ shouldBe("adapter.visible === !oldVisible", "true");
+ oldVisible = adapter.visible;
+}
+
function runChangePropertiesTest() {
- oldName = adapter.name;
- oldVisible = adapter.visible;
+ oldName = adapter.name;
+ oldVisible = adapter.visible;
- debug("Adapter Name (oldName): " + adapter.name);
- debug("Adapter Visible (oldVisible): " + adapter.visible);
+ output.value += '\nAdapter Name (oldName): ' + adapter.name;
+ output.value += '\nAdapter Visible (oldVisible): ' + adapter.visible;
- shouldBe("adapter.name === oldName", "true");
- shouldBe("adapter.powered", "true");
- shouldBe("adapter.visible === oldVisible", "true");
+ shouldBe("adapter.name === oldName", "true");
+ shouldBe("adapter.powered", "true");
+ shouldBe("adapter.visible === oldVisible", "true");
- adapter.setName("FooBigBarrrrr", onChangedAdapterName, onErrorCallback);
- adapter.setVisible(!oldVisible, onChangedAdapterVisible, onErrorCallback);
+ adapter.setName("FooBigBarrrrr", onChangedAdapterName, onErrorCallback);
+ adapter.setVisible(!oldVisible, onChangedAdapterVisible, onErrorCallback);
- setTimeout(function() {
- shouldBe("adapter.name === oldName", "false");
- }, 1000);
+ setTimeout(function() {
+ shouldBe("adapter.name === oldName", "false");
+ }, 1000);
- setTimeout(function() {
- adapter.setName(oldName, onChangedAdapterName, onErrorCallback);
- adapter.setVisible(!oldVisible, onChangedAdapterVisible, onErrorCallback);
- }, 1000);
+ setTimeout(function() {
+ adapter.setName(oldName, onChangedAdapterName, onErrorCallback);
+ adapter.setVisible(!oldVisible, onChangedAdapterVisible, onErrorCallback);
+ }, 1000);
};
var discoverDevicesSuccessCallback = {
- onstarted: function() {
- output.value += '\n\n## Discovery Started ##';
- },
- ondevicefound: function(device) {
- output.value += '\n' + device_index + '- Device Found: ' + device.name;
- devices[device_index] = device;
- device_index += 1;
- },
- ondevicedisappeared: function(address) {
- output.value += '\nDevice Disappeared: ' + address;
- },
- onfinished: function(devices) {
- output.value += '\n\n## Discovery Finished. Devices Found: ##';
- for (var i = 0; i < devices.length; i++) {
- output.value += '\nDevice Name: ' + devices[i].name;
- }
+ onstarted: function() {
+ output.value += '\n\n## Discovery Started ##';
+ },
+ ondevicefound: function(device) {
+ output.value += '\n' + device_index + '- Device Found: ' + device.name;
+ devices[device_index] = device;
+ device_index += 1;
+ },
+ ondevicedisappeared: function(address) {
+ output.value += '\nDevice Disappeared: ' + address;
+ },
+ onfinished: function(devices) {
+ output.value += '\n\n## Discovery Finished. Devices Found: ##';
+ for (var i = 0; i < devices.length; i++) {
+ output.value += '\nDevice Name: ' + devices[i].name;
}
+ }
};
-var onErrorCallback = function(e) {
- console.log("\nFailed: " + e.message);
+var onGotKnownDevices = function(devices) {
+ output.value += '\n\n## Known Devices: ##';
+ for (var i = 0; i < devices.length; i++) {
+ output.value += '\nDevice Name: ' + devices[i].name;
+ output.value += ' / address: ' + devices[i].address;
+ }
+}
+
+var onGotDeviceInfo = function(device) {
+ output.value += '\n\n## Get Device Info: ##';
+ output.value += '\nName: ' + device.name;
+ output.value += ' / Address: ' + device.address;
+ output.value += '\nClass major: ' + device.deviceClass.major;
+ output.value += ' / minor: ' + device.deviceClass.minor;
+ output.value += ' / service class: ' + device.deviceClass.services;
+ output.value += '\nIs Bonded: ' + ((device.isBonded) ? "Yes" : "No");
+ output.value += ' / Is Trusted: ' + ((device.isTrusted) ? "Yes" : "No");
+ output.value += ' / Is Connected: ' + ((device.isConnected) ? "Yes" : "No");
+ output.value += '\nUUIDs: ' + device.uuids.join("\n");
+ }
+
+var onHasServicesTests = function(device) {
+ output.value += '\n\n## hasServices tests ##';
+ var test = false;
+ test = device.deviceClass.hasService(tizen.bluetooth.deviceService.POSITIONING);
+ output.value += '\nPOSITIONING: ' + (test ? "Yes" : "No");
+ test = device.deviceClass.hasService(tizen.bluetooth.deviceService.NETWORKING);
+ output.value += ' / NETWORKING: ' + (test ? "Yes" : "No");
+ test = device.deviceClass.hasService(tizen.bluetooth.deviceService.TELEPHONY);
+ output.value += ' / TELEPHONY: ' + (test ? "Yes" : "No");
+}
+
+function runCreateBondingTest() {
+ output.value += '\n\n## Create bonding with MAC: ' + getAddressFromInputText() + ' ##';
+ adapter.createBonding(getAddressFromInputText(), onSuccessCreateBonding, onErrorCallback);
};
-var onGotKnownDevices = function(devices) {
- output.value += '\n\n## Known Devices: ##';
- for (var i = 0; i < devices.length; i++) {
- output.value += '\nDevice Name: ' + devices[i].name;
- }
+var onSuccessCreateBonding = function(device) {
+ output.value += '\nDevice Name:' + device.name + ' bond successfully created !';
+ output.value += '\nDevice Address:' + device.address;
}
-var onChangedAdapterName = function() {
- debug("Adapter New Name: " + adapter.name);
+function runDestroyBondingTest() {
+ output.value += '\n\n## Destroy bonding with MAC: ' + getAddressFromInputText() + ' ##';
+ adapter.destroyBonding(getAddressFromInputText(), onSuccessDestroyBonding, onErrorCallback);
+
+};
+
+var onSuccessDestroyBonding = function(device) {
+ output.value += '\nDevice Name:' + device.name + ' bond successfully destroyed !';
+ output.value += '\nDevice Address:' + device.address;
}
-var onChangedAdapterVisible = function() {
- shouldBe("adapter.visible === !oldVisible", "true");
- oldVisible = adapter.visible;
+var onChatServiceSuccessCb = function(handler) {
+ output.value += '\nChat service registration was successful!';
+
+ chatServiceHandler = handler;
+
+ handler.onconnect = function(socket) {
+ output.value += '\nClient is connected: ' + socket.peer.name + ',' + socket.peer.address;
+ socket.onmessage = function() {
+ var data = socket.readData();
+ output.value += '\nThe socket received data: ' + socket.data;
+ };
+
+ socket.onclose = function() {
+ output.value += '\nThe socket is closed.';
+ };
+ };
}
-handle("button1", "Scan", function() {
- adapter.discoverDevices(discoverDevicesSuccessCallback, function(e){
- console.log ("Failed to search devices: " + e.message + "(" + e.name + ")");
+function runUnregisterChatServiceTest() {
+ if (chatServiceHandler != null) {
+ chatServiceHandler.unregister(function() {
+ output.value += '\nChat service is unregistered';
+ chatServiceHandler = null;
+ },
+ function(e) {
+ output.value += '\nFailed to unregister service: ' + e.message;
});
+ }
+}
+
+var onSocketConnected = function(socket) {
+ clientSocket = socket;
+ output.value += '\nOpening a socket successfully!!!';
+ socket.onmessage = function () {
+ var data = socket.readData();
+ var recvmsg = "";
+ for (var i = 0; i < data.length; i++) {
+ recvmsg += String.fromCharCode(data[i]);
+ }
+ output.value += '\nserver msg >> ' + recvmsg;
+ };
+
+ socket.onclose = function() {
+ output.value += '\nsocket disconnected.';
+ };
+}
+
+var onDeviceReady = function(device) {
+ if (device.uuids.indexOf(CHAT_SERVICE_UUID) != -1) {
+ device.connectToServiceByUUID(CHAT_SERVICE_UUID, onSocketConnected, function(e) {
+ output.value += '\nError connecting to service. Reason: ' + e.message;
+ });
+ } else {
+ output.value += '\nChat service is not supported by this device';
+ }
+}
+
+function sendMessage(msg) {
+ if (clientSocket != null && clientSocket.state == "OPEN") {
+ clientSocket.writeData(msg);
+ }
+}
+
+var onErrorCallback = function(e) {
+ output.value += '\n[ERROR] Failed: ' + e.message;
+};
+
+handle("button1", "Power On/OFF", function() {
+ runPowerTest();
});
-handle("button2", "Stop Scan", function() {
- adapter.stopDiscovery(function() {
- output.value += "\n## Discovery Stopped ##";
- }, onErrorCallback);
+handle("button2", "Test Name", function() {
+ runNameTest();
});
-handle("button3", "Get Known Devices", function() {
- adapter.getKnownDevices(onGotKnownDevices, onErrorCallback);
+handle("button3", "Run Adapter properties test", function() {
+ runChangePropertiesTest();
});
-handle("button4", "Run Adapter properties test", function() {
- runChangePropertiesTest();
+
+handle("button4", "Start Scan", function() {
+ adapter.discoverDevices(discoverDevicesSuccessCallback, function(e){
+ output.value += '\n[ERROR] Failed to search devices: ' + e.message + '(' + e.name + ')';
+ });
+});
+
+handle("button5", "Stop Scan", function() {
+ adapter.stopDiscovery(function() {
+ output.value += "\n## Discovery Stopped ##";
+ }, onErrorCallback);
+});
+
+handle("button6", "Get Known Devices", function() {
+ adapter.getKnownDevices(onGotKnownDevices, onErrorCallback);
+});
+
+handle("button7", "Get Device Info", function() {
+ adapter.getDevice(getAddressFromInputText(), onGotDeviceInfo, onErrorCallback);
+});
+
+handle("button8", "Test hasService", function() {
+ adapter.getDevice(getAddressFromInputText(), onHasServicesTests, onErrorCallback);
+});
+
+handle("button9", "Create Bonding", function() {
+ runCreateBondingTest();
+});
+
+handle("button10", "Destroy Bonding", function() {
+ runDestroyBondingTest();
+});
+
+handle("button11", "Register RFCOMM chat service", function() {
+ output.value += '\n\n## Register RFCOMM Chat service ##';
+ adapter.registerRFCOMMServiceByUUID(CHAT_SERVICE_UUID, "Chat service", onChatServiceSuccessCb, onErrorCallback);
+});
+
+handle("button12", "Unregister chat service", function() {
+ runUnregisterChatServiceTest();
+});
+
+handle("button13", "Connect to chat service", function() {
+ output.value += '\n\n## Connect to Chat service ##';
+ adapter.getDevice(getAddressFromInputText(), onDeviceReady, function(e) { output.value += '\n"[ERROR] ' + e.message; });
+});
+
+handle("button14", "Send message", function() {
+ var msg = document.getElementById("text");
+ var message = msg.value;
+ output.value += '\n\n## Send message ##';
+
+ if (message == "") {
+ output.value += '\nPlease write a message in text area first !';
+ return;
+ }
+
+ if (typeof message !== 'string') {
+ output.value += '\ntext message is invalid !';
+ return;
+ }
+
+ sendMessage(message);
});
</script>