build_common/arduino/extlibs/arduino/arduino-1.5.8
extlibs/tinydtls/dtls-client
extlibs/tinydtls/dtls-server
+extlibs/bluez/bluez
# Ignore editor (e.g. Emacs) backup and autosave files
*~
--- /dev/null
+From 31fafe0dd2265cd181734ee909554f24fd3c3c20 Mon Sep 17 00:00:00 2001
+From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Date: Fri, 26 Jun 2015 14:52:22 +0300
+Subject: [PATCH] core/advertising: Fix using wrong instance id
+
+The instance id shall be limited to the number of instance the kernel
+support, and since this will probably be quite small the ids should be
+reused once the client unregistered.
+---
+ src/advertising.c | 12 +++++++-----
+ 1 file changed, 7 insertions(+), 5 deletions(-)
+
+diff --git a/src/advertising.c b/src/advertising.c
+index d6ab084..3763d85 100644
+--- a/src/advertising.c
++++ b/src/advertising.c
+@@ -48,7 +48,7 @@ struct btd_advertising {
+ uint16_t mgmt_index;
+ uint8_t max_adv_len;
+ uint8_t max_ads;
+- unsigned int next_instance_id;
++ unsigned int instance_bitmap;
+ };
+
+ #define AD_TYPE_BROADCAST 0
+@@ -155,6 +155,8 @@ static void advertisement_remove(void *data)
+
+ queue_remove(ad->manager->ads, ad);
+
++ util_clear_uid(&ad->manager->instance_bitmap, ad->instance);
++
+ g_idle_add(advertisement_free_idle_cb, ad);
+ }
+
+@@ -633,6 +635,7 @@ static DBusMessage *register_advertisement(DBusConnection *conn,
+ DBusMessageIter args;
+ struct advertisement *ad;
+ struct dbus_obj_match match;
++ uint8_t instance;
+
+ DBG("RegisterAdvertisement");
+
+@@ -649,7 +652,8 @@ static DBusMessage *register_advertisement(DBusConnection *conn,
+ if (queue_find(manager->ads, match_advertisement, &match))
+ return btd_error_already_exists(msg);
+
+- if (queue_length(manager->ads) >= manager->max_ads)
++ instance = util_get_uid(&manager->instance_bitmap, manager->max_ads);
++ if (!instance)
+ return btd_error_failed(msg, "Maximum advertisements reached");
+
+ dbus_message_iter_next(&args);
+@@ -664,7 +668,7 @@ static DBusMessage *register_advertisement(DBusConnection *conn,
+
+ DBG("Registered advertisement at path %s", match.path);
+
+- ad->instance = manager->next_instance_id++;
++ ad->instance = instance;
+ ad->manager = manager;
+
+ queue_push_tail(manager->ads, ad);
+@@ -787,8 +791,6 @@ advertising_manager_create(struct btd_adapter *adapter)
+
+ manager->ads = queue_new();
+
+- manager->next_instance_id = 1;
+-
+ return manager;
+ }
+
+--
+2.1.4
+
--- /dev/null
+# ------------------------------------------------------------------------
+# Copyright 2015 Intel Corporation
+#
+# 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.
+# ------------------------------------------------------------------------
+
+######################################################################
+# BlueZ download script
+#
+# BlueZ is currently only used by the IoTivity Linux BLE transport.
+# The IoTivity Linux BLE transport depends on two headers found in the
+# BlueZ bluetooth library: <bluetooth/bluetooth.h> and
+# <bluetooth/hci.h>. Those headers are used solely when pulling in
+# the types and constants necessary to a create kernel Bluetooth
+# management socket. There is no need to link BlueZ's libbluetooth
+# library.
+######################################################################
+import os
+import subprocess
+
+Import('env')
+
+# First check if the BlueZ bluetooth library development files are
+# installed. Add the location of the bluetooth library headers to the
+# include path if they are installed. Otherwise, download the BlueZ
+# source code and adjust the include path, accordingly.
+
+# The subprocess.run() function became available in Python 3.5. Use
+# the older high-level API instead, since some installations may still
+# be using older versions of Python.
+bluez_installed = (subprocess.call([ 'pkg-config',
+ 'bluez',
+ '--exists' ]) == 0)
+
+if bluez_installed:
+ env.ParseConfig("pkg-config bluez --cflags-only-I")
+ Return()
+
+if not os.path.exists('bluez'):
+ bluez = env.Action([
+ 'git clone git://git.kernel.org/pub/scm/bluetooth/bluez.git',
+ 'cd bluez && ln -s lib bluetooth && cd -'])
+
+ print 'Downloading BlueZ ...'
+ if env.Execute(bluez):
+ print '''
+*********************************** Error *********************************
+* Please download Bluez 5.32 or better, build it and install it. *
+* Alternatively, but less preferrably, create a symbolic as follows *
+* $ cd extlibs/bluez *
+* $ git clone git://git.kernel.org/pub/scm/bluetooth/bluez.git *
+* $ cd bluez *
+* $ ln -s lib bluetooth *
+* $ cd ../../.. *
+***************************************************************************
+'''
+ Exit(1)
+ else:
+ print 'BlueZ download complete.'
+
+env.AppendUnique(CPPPATH = ['#extlibs/bluez/bluez'])
+
+
+# Local Variables:
+# mode:python
+# indent-tabs-mode: nil
+# End:
--- /dev/null
+diff --git a/src/bluetooth.service.in b/src/bluetooth.service.in
+index 35e9457..8204a79 100644
+--- a/src/bluetooth.service.in
++++ b/src/bluetooth.service.in
+@@ -5,7 +5,7 @@ Documentation=man:bluetoothd(8)
+ [Service]
+ Type=dbus
+ BusName=org.bluez
+-ExecStart=@libexecdir@/bluetoothd
++ExecStart=@libexecdir@/bluetoothd --experimental
+ NotifyAccess=main
+ #WatchdogSec=10
+ #Restart=on-failure
liboctbstack_env.AppendUnique(LIBPATH = [env.get('BUILD_DIR')])
-if target_os in ['android', 'linux','tizen']:
- liboctbstack_env.AppendUnique(LIBS = ['connectivity_abstraction'])
+if target_os in ['android', 'linux', 'tizen']:
+ liboctbstack_env.PrependUnique(LIBS = ['connectivity_abstraction'])
if with_ra:
liboctbstack_env.AppendUnique(LIBS = ['ra_xmpp'])
+
liboctbstack_env.AppendUnique(LIBS = ['coap', 'm'])
if target_os == 'tizen':
if with_ra == True:
env.AppendUnique(CPPDEFINES = ['RA_ADAPTER'])
if target_os == 'linux':
- env.AppendUnique(CPPDEFINES = ['IP_ADAPTER','NO_EDR_ADAPTER','NO_LE_ADAPTER'])
+ env.AppendUnique(CPPDEFINES = ['IP_ADAPTER','NO_EDR_ADAPTER','LE_ADAPTER'])
elif target_os == 'tizen':
env.AppendUnique(CPPDEFINES = ['IP_ADAPTER','EDR_ADAPTER','LE_ADAPTER'])
elif target_os in['darwin','ios']:
env.AppendUnique(CPPDEFINES = ['NO_EDR_ADAPTER'])
if 'BLE' in transport:
- if target_os == 'linux':
- print "CA Transport BLE is not supported in Linux"
- Exit(1)
- else:
- env.AppendUnique(CPPDEFINES = ['LE_ADAPTER'])
- print "CA Transport is BLE"
+ env.AppendUnique(CPPDEFINES = ['LE_ADAPTER'])
+ print "CA Transport is BLE"
else:
env.AppendUnique(CPPDEFINES = ['NO_LE_ADAPTER'])
--- /dev/null
+/* ****************************************************************
+ *
+ * Copyright 2015 Intel Corporation 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.
+ *
+ ******************************************************************/
+
+#ifndef CA_GATT_SERVICE_H
+#define CA_GATT_SERVICE_H
+
+
+/**
+ * @name OIC GATT Transport Constants
+ *
+ * Group of constants, such as UUIDs, specific to the OIC GATT
+ * Transport Profile.
+ */
+//@{
+/// OIC Transport Profile GATT service UUID.
+#define CA_GATT_SERVICE_UUID "ADE3D529-C784-4F63-A987-EB69F70EE816"
+
+/// OIC Transport Profile GATT request characteristic UUID.
+#define CA_GATT_REQUEST_CHRC_UUID "AD7B334F-4637-4B86-90B6-9D787F03D218"
+
+/**
+ * Standard Bluetooth GATT characteristic user description descriptor
+ * UUID.
+ *
+ * @note Used by both the OIC GATT request and response
+ * characteristics.
+ */
+#define CA_GATT_CHRC_USER_DESCRIPTION_DESC_UUID "2901"
+
+/**
+ * OIC Transport Profile GATT request characteristic user description
+ * descriptor value.
+ */
+#define CA_GATT_REQUEST_USER_DESCRIPTION "OIC Node Request"
+
+/// OIC Transport Profile GATT response characteristic UUID.
+#define CA_GATT_RESPONSE_CHRC_UUID "E9241982-4580-42C4-8831-95048216B256"
+
+/**
+ * OIC Transport Profile GATT response characteristic user description
+ * descriptor value.
+ */
+#define CA_GATT_RESPONSE_USER_DESCRIPTION "OIC Node Response"
+
+/**
+ * Standard Bluetooth GATT client characteristic configuration
+ * descriptor UUID.
+ *
+ * @note Only used by the OIC GATT response characteristic.
+ */
+#define CA_GATT_CONFIGURATION_DESC_UUID "2902"
+
+/**
+ * OIC Transport Profile GATT response client characteristic
+ * configuration descriptor value.
+ */
+#define CA_GATT_RESPONSE_CONFIG_DESC "0001"
+//@}
+
+
+#endif // CA_GATT_SERVICE_H
*
******************************************************************/
-/**
- * @file
- *
- * This file contains the APIs for LE adapters to be implemented.
- */
#ifndef CA_LEADAPTER_H_
#define CA_LEADAPTER_H_
#include "cacommon.h"
#include "caadapterinterface.h"
-#include "cathreadpool.h" /* for thread pool */
+#include "cathreadpool.h"
-// BLE Interface APIs.
#ifdef __cplusplus
extern "C"
{
#endif
-/**
- * Stores the information of the Data to be sent from the queues.
- * This structure will be pushed to the sender/receiver queue for processing.
- */
-typedef struct
-{
- CAEndpoint_t *remoteEndpoint; /**< Remote endpoint contains the
- information of remote device. */
- void *data; /**< Data to be transmitted over LE transport. */
- uint32_t dataLen; /**< Length of the data being transmitted. */
-} CALEData_t;
-
-/**
- * Stores information of all the senders.
- * This structure will be used to track and de-fragmentation all incoming data packets.
- */
-typedef struct
-{
- uint32_t recvDataLen;
- uint32_t totalDataLen;
- char *defragData;
- CAEndpoint_t *remoteEndpoint;
-}CABLESenderInfo_t;
/**
* Initialize LE connectivity interface.
+ *
* @param[in] registerCallback Callback to register LE interfaces to
- * Connectivity Abstraction Layer.
+ * Connectivity Abstraction Layer.
* @param[in] reqRespCallback Callback to notify request and response
- * messages from server(s) started at
- * Connectivity Abstraction Layer.
+ * messages from server(s) started at
+ * Connectivity Abstraction Layer.
* @param[in] netCallback Callback to notify the network additions
- * to Connectivity Abstraction Layer.
+ * to Connectivity Abstraction Layer.
* @param[in] errorCallback errorCallback to notify error to
- * connectivity common logic layer from adapter.
+ * connectivity common logic layer from adapter.
* @param[in] handle Threadpool Handle.
+ *
* @return ::CA_STATUS_OK or Appropriate error code.
*/
CAResult_t CAInitializeLE(CARegisterConnectivityCallback registerCallback,
CANetworkPacketReceivedCallback reqRespCallback,
CANetworkChangeCallback netCallback,
- CAErrorHandleCallback errorCallback, ca_thread_pool_t handle);
-
-/**
- * Starting LE connectivity adapters.
- * As its peer to peer it doesnot require to start any servers.
- * @return ::CA_STATUS_OK or Appropriate error code.
- */
-CAResult_t CAStartLE();
-
-/**
- * Starting listening server for receiving multicast search requests.
- * Transport Specific Behavior:
- * LE Starts GATT Server with prefixed UUID and Characteristics as per
- * OIC Specification.
- * @return ::CA_STATUS_OK or Appropriate error code.
- */
-CAResult_t CAStartLEListeningServer();
-
-/**
- * for starting discovery servers for receiving multicast advertisements.
- * Transport Specific Behavior:
- * LE Starts GATT Server with prefixed UUID and Characteristics as per
- * OIC Specification.
- * @return ::CA_STATUS_OK or Appropriate error code
- */
-CAResult_t CAStartLEDiscoveryServer();
-
-/**
- * Sends data to the endpoint using the adapter connectivity.
- * @param[in] endpoint Remote Endpoint information (like ipaddress ,
- * port, reference uri and connectivity type) to
- * which the unicast data has to be sent.
- * @param[in] data Data which required to be sent.
- * @param[in] dataLen Size of data to be sent.
- * @note dataLen must be > 0.
- * @return The number of bytes sent on the network, or -1 on error.
- */
-int32_t CASendLEUnicastData(const CAEndpoint_t *endpoint, const void *data,
- uint32_t dataLen);
-
-/**
- * Sends Multicast data to the endpoint using the LE connectivity.
- * @param[in] endpoint Remote Endpoint information to which the
- * unicast data has to be sent.
- * @param[in] data Data which required to be sent.
- * @param[in] dataLen Size of data to be sent.
- * @note dataLen must be > 0.
- * @return The number of bytes sent on the network, or -1 on error.
- */
-int32_t CASendLEMulticastData(const CAEndpoint_t *endpoint, const void *data, uint32_t dataLen);
-
-/**
- * Starts notification server on EDR adapters.
- * @return ::CA_STATUS_OK or Appropriate error code.
- */
-CAResult_t CAStartLENotifyServer();
-
-/**
- * Send notification information.
- * @param[in] endpoint Remote Endpoint information (like ipaddress ,
- * port, reference uri and connectivity type)
- * to which the unicast data has to be sent.
- * @param[in] data Data which required to be sent.
- * @param[in] dataLen Size of data to be sent.
- * @note dataLen must be > 0.
- * @return The number of bytes sent on the network, or 0 on error.
- */
-uint32_t CASendLENotification(const CAEndpoint_t *endpoint, const void *data,
- uint32_t dataLen);
-
-/**
- * Get LE Connectivity network information.
- * @param[out] info Local connectivity information structures.
- * @param[out] size Number of local connectivity structures.
- * @return ::CA_STATUS_OK or Appropriate error code.
- */
-CAResult_t CAGetLEInterfaceInformation(CAEndpoint_t **info, uint32_t *size);
-
-/**
- * Read Synchronous API callback.
- * @return ::CA_STATUS_OK or Appropriate error code.
- */
-CAResult_t CAReadLEData();
-
-/**
- * Stopping the adapters and close socket connections.
- * LE Stops all GATT servers and GATT Clients.
- * @return ::CA_STATUS_OK or Appropriate error code.
- */
-CAResult_t CAStopLE();
-
-/**
- * Terminate the LE connectivity adapter.
- * Configuration information will be deleted from further use.
- */
-void CATerminateLE();
-
-/**
- * This function will receive the data from the GattServer and add the data to
- * the Server receiver queue.
- * @param[in] remoteAddress Remote address of the device from where data
- * is received.
- * @param[in] serviceUUID Uuid of the OIC service running on the remote
- * device.
- * @param[in] data Actual data Received from the remote device.
- * @param[in] dataLength Length of the data received from the remote device.
- * @param[in] sentLength Length of the data sent from the remote device.
- * @return ::CA_STATUS_OK or Appropriate error code.
- * @retval ::CA_STATUS_OK Successful.
- * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
- * @retval ::CA_STATUS_FAILED Operation failed.
- *
- */
-CAResult_t CALEAdapterServerReceivedData(const char *remoteAddress, const char *serviceUUID,
- const void *data, uint32_t dataLength,
- uint32_t *sentLength);
+ CAErrorHandleCallback errorCallback,
+ ca_thread_pool_t handle);
-/**
- * This function will receive the data from the GattClient and add the
- * data into the Client receiver queue.
- * @param[in] remoteAddress Remote address of the device from where data
- * is received.
- * @param[in] serviceUUID Uuid of the OIC service running on the remote
- * device.
- * @param[in] data Actual data received from the remote device.
- * @param[in] dataLength Length of the data received from the remote device.
- * @param[in] sentLength Length of the data sent from the remote device.
- * @return ::CA_STATUS_OK or Appropriate error code.
- * @retval ::CA_STATUS_OK Successful.
- * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
- * @retval ::CA_STATUS_FAILED Operation failed.
- */
-CAResult_t CALEAdapterClientReceivedData(const char *remoteAddress, const char *serviceUUID,
- const void *data, uint32_t dataLength,
- uint32_t *sentLength);
-
-/**
- * This function is used to set the NetworkPacket received callback to CA
- * layer from adapter layer.
- * @param[in] callback callback handle sent from the upper layer.
- */
-void CASetLEReqRespAdapterCallback(CANetworkPacketReceivedCallback callback);
-
-/**
- * This function will push the data from CA layer to the Sender processor queue.
- *
- * @param[in] remoteEndpoint Remote endpoint information of the server.
- * @param[in] data Data to be transmitted from LE.
- * @param[in] dataLen length of the Data being transmitted.
- *
- * @return ::CA_STATUS_OK or Appropriate error code.
- * @retval ::CA_STATUS_OK Successful.
- * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
- * @retval ::CA_STATUS_FAILED Operation failed.
- */
-CAResult_t CALEAdapterServerSendData(const CAEndpoint_t *remoteEndpoint,
- const void *data, uint32_t dataLen);
-
-/**
- * This function will push the data from CA layer to the Sender processor queue.
- *
- * @param[in] remoteEndpoint Remote endpoint information of the server.
- * @param[in] data Data to be transmitted from LE.
- * @param[in] dataLen length of the Data being transmitted.
- *
- * @return ::CA_STATUS_OK or Appropriate error code.
- * @retval ::CA_STATUS_OK Successful.
- * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
- * @retval ::CA_STATUS_FAILED Operation failed.
- */
-CAResult_t CALEAdapterClientSendData(const CAEndpoint_t *remoteEndpoint,
- const void *data, uint32_t dataLen);
-
-/**
- * This function will be associated with the sender queue for GattServer.
- * This function will fragment the data to the MTU of the transport and
- * send the data in fragments to the adapters. The function will be
- * blocked untill all data is sent out from the adapter.
- *
- * @param[in] threadData Data pushed to the queue which contains the info
- * about RemoteEndpoint and Data.
- */
-void CALEServerSendDataThread(void *threadData);
-
-/**
- * This function will be associated with the sender queue for GattClient.
- * This function will fragment the data to the MTU of the transport and
- * send the data in fragments to the adapters. The function will be
- * blocked until all data is sent out from the adapter.
- *
- * @param[in] threadData Data pushed to the queue which contains the info
- * about RemoteEndpoint and Data.
- */
-void CALEClientSendDataThread(void *threadData);
-
-/**
- * This function will be associated with the receiver queue. This function will reassemble
- * the received data from each sender respectively and will send it up to CA layer.
- * Respective sender's header will provide the length of the data sent.
- *
- * @param [IN] threadData Data pushed to the queue which contains the info about RemoteEndpoint
- * and Data.
- */
-void CALEDataReceiverHandler(void *threadData);
-
-/**
- * This function is used to initialize both GattServer and GattClient
- * queues. All four queues will be initialized with this function invocations.
- */
-void CAInitLEQueues();
-
-/**
- * This function will stop all queues created for GattServer and GattClient. All
- * four queues will be be stopped with this function invocations.
- */
-void CAStopLEQueues();
-
-/**
- * This function will terminate all queues created for GattServer and
- * GattClient. All four queues will be be terminated with this function
- * invocations.
- */
-void CATerminateLEQueues();
-
-/**
- * This function will initialize the Receiver and Sender queues for
- * GattServer. This function will inturn call the functions
- * CAInitBleServerReceiverQueue() and CAInitBleServerSenderQueue() to
- * initialize the queues.
- * @return ::CA_STATUS_OK or Appropriate error code.
- * @retval ::CA_STATUS_OK Successful.
- * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
- * @retval ::CA_STATUS_FAILED Operation failed.
- */
-CAResult_t CAInitLEServerQueues();
-
-/**
- * This function will initialize the Receiver and Sender queues for
- * GattClient. This function will inturn call the functions
- * CAInitBleClientReceiverQueue() and CAInitBleClientSenderQueue() to
- * initialize the queues.
- *
- * @return ::CA_STATUS_OK or Appropriate error code.
- * @retval ::CA_STATUS_OK Successful.
- * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
- * @retval ::CA_STATUS_FAILED Operation failed.
- *
- */
-CAResult_t CAInitLEClientQueues();
-
-/**
- * This function will initialize the Receiver queue for GattServer. This
- * will initialize the queue to process the function
- * CABLEServerSendDataThread() when ever the task is added to this queue.
- *
- * @return ::CA_STATUS_OK or Appropriate error code.
- * @retval ::CA_STATUS_OK Successful.
- * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
- * @retval ::CA_STATUS_FAILED Operation failed.
- */
-CAResult_t CAInitLEServerSenderQueue();
-
-/**
- * This function will initialize the Receiver queue for GattClient. This
- * will initialize the queue to process the function
- * CABLEClientSendDataThread() when ever the task is added to this queue.
- *
- * @return ::CA_STATUS_OK or Appropriate error code.
- * @retval ::CA_STATUS_OK Successful.
- * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
- * @retval ::CA_STATUS_FAILED Operation failed.
- */
-CAResult_t CAInitLEClientSenderQueue();
-
-/**
- * This function will initialize the Receiver queue for LEAdapter. This will initialize
- * the queue to process the function CABLEDataReceiverHandler() when ever the task
- * is added to this queue.
- *
- * @return ::CA_STATUS_OK or Appropriate error code
- * @retval ::CA_STATUS_OK Successful
- * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments
- * @retval ::CA_STATUS_FAILED Operation failed
- *
- */
-CAResult_t CAInitLEReceiverQueue();
-
-/**
- * This function will create the Data required to send it in the queue.
- *
- * @param[in] remoteEndpoint Remote endpoint information of the server.
- * @param[in] data Data to be transmitted from LE.
- * @param[in] dataLength length of the Data being transmitted.
- *
- * @return ::CA_STATUS_OK or Appropriate error code.
- * @retval ::CA_STATUS_OK Successful.
- * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
- * @retval ::CA_STATUS_FAILED Operation failed.
- */
-CALEData_t *CACreateLEData(const CAEndpoint_t *remoteEndpoint, const void *data,
- uint32_t dataLength);
-
-/**
- * Used to free the BLE information stored in the sender/receiver queues.
- * @param[in] bleData Structure contains the information of a particular
- * data segment.
- */
-void CAFreeLEData(CALEData_t *bleData);
-
-/**
- * This will be used to notify device status changes to the LE adapter layer.
- * @param[in] adapter_state State of the adapter.
- */
-typedef void (*CALEDeviceStateChangedCallback)(CAAdapterState_t adapter_state);
-
-/**
- * This will be used to notify that network packet received from
- * GATTClient to adapter layer.
- * @param[in] remoteAddress Remote endpoint Address.
- * @param[in] serviceUUID Service UUID.
- * @param[in] data Data received.
- * @param[in] dataLength Length of the data received.
- * @param[in] sentLength Length of the data sent.
- * @return ::CA_STATUS_OK or Appropriate error code.
- * @retval ::CA_STATUS_OK Successful.
- * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
- * @retval ::CA_STATUS_FAILED Operation failed.
- */
-typedef CAResult_t (*CABLEClientDataReceivedCallback)(const char *remoteAddress,
- const char *serviceUUID, const void *data,
- uint32_t dataLength, uint32_t *sentLength);
-
-/**
- * This will be used to notify that network packet received from
- * GATTServer to adapter layer.
- * @param[in] remoteAddress Remote endpoint Address.
- * @param[in] serviceUUID Service UUID.
- * @param[in] data Data received.
- * @param[in] dataLength Length of the data received.
- * @param[in] sentLength Length of the data sent.
- * @return ::CA_STATUS_OK or Appropriate error code.
- * @retval ::CA_STATUS_OK Successful.
- * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
- * @retval ::CA_STATUS_FAILED Operation failed.
- */
-typedef CAResult_t (*CABLEServerDataReceivedCallback)(const char *remoteAddress,
- const char *serviceUUID, const void *data,
- uint32_t dataLength, uint32_t *sentLength);
#ifdef __cplusplus
} /* extern "C" */
******************************************************************/
/**
- * @file
- *
* This file provides APIs for BLE modules.
*/
#include <stdbool.h>
#include "cacommon.h"
-#include "caleadapter.h"
+#include "cathreadpool.h"
#ifdef __cplusplus
extern "C"
/**
* Provide info about different mode of data transfer.
- * This enum is used to differentiate between unicast and multicast data transfer.
+ *
+ * This enum is used to differentiate between unicast and multicast
+ * data transfer.
*/
typedef enum
{
LE_UNICAST /**< When this enum is selected, data will be updated to desired OIC Server. */
} CALETransferType_t;
+/**
+ * This will be used to notify device status changes to the LE adapter layer.
+ * @param[in] adapter_state State of the adapter.
+ */
+typedef void (*CALEDeviceStateChangedCallback)(CAAdapterState_t adapter_state);
/**
- * Initialize the LE adapter layer. This will be invoked from the CA layer.
+ * Notify the adapter layer that a packet was received from the GATT
+ * peer.
+ *
+ * @param[in] remoteAddress Remote endpoint Address.
+ * @param[in] data Data received.
+ * @param[in] dataLength Length of the data received.
+ * @param[in] sentLength Length of the data sent.
*
* @return ::CA_STATUS_OK or Appropriate error code.
* @retval ::CA_STATUS_OK Successful.
* @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
* @retval ::CA_STATUS_FAILED Operation failed.
*/
+typedef CAResult_t (*CABLEDataReceivedCallback)(const char *remoteAddress,
+ const void *data,
+ uint32_t dataLength,
+ uint32_t *sentLength);
+
+/**
+ * Initialize the LE adapter layer. This will be invoked from the CA
+ * layer.
+ *
+ * @return ::CA_STATUS_OK or Appropriate error code
+ * @retval ::CA_STATUS_OK Successful
+ * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments
+ * @retval ::CA_STATUS_FAILED Operation failed
+ */
CAResult_t CAInitializeLEAdapter();
/**
+ * Start the LE adapter layer.
+ *
+ * This function will be invoked from the CA layer when the LE
+ * "network" is selected via @c CASelectNetwork(). It gives an
+ * opportunity for LE adapter implementations to perform operations
+ * before starting a GATT client or server. Most LE adapter
+ * implementations will simply implement this function as no-op.
+ *
+ * @return ::CA_STATUS_OK or Appropriate error code
+ */
+CAResult_t CAStartLEAdapter();
+
+/**
* Used to get the current state of the LE adapter.
*
- * @return ::CA_STATUS_OK or Appropriate error code.
- * @retval ::CA_STATUS_OK Successful.
- * @retval ::CA_ADAPTER_NOT_ENABLED adapter not enabled.
- * @retval ::CA_STATUS_FAILED Operation failed.
+ * @return ::CA_STATUS_OK or Appropriate error code
+ * @retval ::CA_STATUS_OK Successful
+ * @retval ::CA_ADAPTER_NOT_ENABLED adapter not enabled
+ * @retval ::CA_STATUS_FAILED Operation failed
*/
CAResult_t CAGetLEAdapterState();
/**
- * Used to initialize the network monitor layer of the LE adapter. Mutex variables required
- * to operate in this layer and other paramters can be initialized in this function.
+ * Initialize the network monitor layer of the LE adapter. Mutex
+ * variables required to operate in this layer and other parameters
+ * can be initialized in this function.
*
- * @return ::CA_STATUS_OK or Appropriate error code.
- * @retval ::CA_STATUS_OK Successful.
- * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
- * @retval ::CA_STATUS_FAILED Operation failed.
+ * @return ::CA_STATUS_OK or Appropriate error code
+ * @retval ::CA_STATUS_OK Successful
+ * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments
+ * @retval ::CA_STATUS_FAILED Operation failed
*/
CAResult_t CAInitializeLENetworkMonitor();
/**
- * Used to terminate the network monitor layer of the LE adapter. The variables intialized
- * in CAInitializeLEAdapterController() must be cleared in this function.
+ * Terminate the network monitor layer of the LE adapter. The
+ * variables initialized in @c CAInitializeLENetworkMonitor() must be
+ * cleared in this function.
*/
void CATerminateLENetworkMonitor();
/**
- * This function is used to set the callback for the Device state changes in the adapter.
+ * Set the callback for the device state changes in the adapter.
*
- * @param[in] callback Callback to notify the Device state change to the CA Layer.
+ * @param[in] callback Callback to notify the Device state change to
+ * the CA Layer
*
- * @return ::CA_STATUS_OK or Appropriate error code.
- * @retval ::CA_STATUS_OK Successful.
- * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
- * @retval ::CA_STATUS_FAILED Operation failed.
+ * @return ::CA_STATUS_OK or Appropriate error code
+ * @retval ::CA_STATUS_OK Successful
+ * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments
+ * @retval ::CA_STATUS_FAILED Operation failed
*/
CAResult_t CASetLEAdapterStateChangedCb(CALEDeviceStateChangedCallback callback);
/**
- * Used to initilaze all the mutex variables required.
- * to operate the LE network monitor layer.
- * @return ::CA_STATUS_OK or Appropriate error code.
- * @retval ::CA_STATUS_OK Successful.
- * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
- * @retval ::CA_STATUS_FAILED Operation failed.
+ * Initialize all the mutex variables required to operate the LE
+ * network monitor layer.
+ *
+ * @return ::CA_STATUS_OK or Appropriate error code
+ * @retval ::CA_STATUS_OK Successful
+ * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments
+ * @retval ::CA_STATUS_FAILED Operation failed
*/
CAResult_t CAInitLENetworkMonitorMutexVariables();
/**
- * Used to terminate all the mutex variables required
- * to operate the LE network monitor layer.
+ * Used to terminate all the mutex variables required to operate the LE
+ * network monitor layer.
*/
void CATerminateLENetworkMonitorMutexVariables();
/**
- * Provides the BD address of the local adapter.
- * @param[out] local_address pointer to the location where bd address needs to be stored.
+ * Provides the MAC address of the local Bluetooth adapter.
*
- * @return ::CA_STATUS_OK or Appropriate error code.
- * @retval ::CA_STATUS_OK Successful.
- * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
- * @retval ::CA_STATUS_FAILED Operation failed.
+ * @param[out] local_address Pointer to the location where bd address
+ * needs to be stored.
+ *
+ * @return ::CA_STATUS_OK or Appropriate error code
+ * @retval ::CA_STATUS_OK Successful
+ * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments
+ * @retval ::CA_STATUS_FAILED Operation failed
*/
CAResult_t CAGetLEAddress(char **local_address);
/**
- * Used to start Gatt Server thread for service creation and advertise ble service.
+ * Start Gatt Server thread for service creation and advertise BLE
+ * service.
*
- * @return ::CA_STATUS_OK or Appropriate error code.
- * @retval ::CA_STATUS_OK Successful.
- * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
- * @retval ::CA_STATUS_FAILED Operation failed.
+ * @return ::CA_STATUS_OK or Appropriate error code
+ * @retval ::CA_STATUS_OK Successful
+ * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments
+ * @retval ::CA_STATUS_FAILED Operation failed
*/
CAResult_t CAStartLEGattServer();
/**
- * Used to stop BLE Gatt Service.
+ * Stop BLE Gatt Service.
*
- * @return ::CA_STATUS_OK or Appropriate error code.
- * @retval ::CA_STATUS_OK Successful.
- * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
- * @retval ::CA_STATUS_FAILED Operation failed.
+ * @return ::CA_STATUS_OK or Appropriate error code
+ * @retval ::CA_STATUS_OK Successful
+ * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments
+ * @retval ::CA_STATUS_FAILED Operation failed
*/
CAResult_t CAStopLEGattServer();
/**
- * Used to stop Gatt Server thread and remove service registration, stop advertising.
+ * Stop Gatt Server thread and remove service registration, stop
+ * advertising.
*/
void CATerminateLEGattServer();
/**
- * Used to store upper layer callback locally
- * which will be used to send the data to application.
- * @param[in] callback Callback function to pass the data to CA layer.
+ * Used to store upper layer callback locally which will be used to
+ * send the data to application.
+ *
+ * @param[in] callback Callback function to pass the data to CA layer.
*/
-void CASetLEReqRespServerCallback(CABLEServerDataReceivedCallback callback);
+void CASetLEReqRespServerCallback(CABLEDataReceivedCallback callback);
/**
- * Used to update characteristics(Read/Write) value
- * that we want to send to particular client.
+ * Update characteristics(Read/Write) value that we want to send to
+ * particular client.
*
- * @param[in] address BD address of Gatt client.
- * @param[in] charValue Data that we want to send to client(unicast).
- * @param[in] charValueLen Length of the data.
+ * @param[in] address BD address of Gatt client
+ * @param[in] charValue Data that we want to send to client(unicast)
+ * @param[in] charValueLen Length of the data.
*
- * @return ::CA_STATUS_OK or Appropriate error code.
- * @retval ::CA_STATUS_OK Successful.
- * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
- * @retval ::CA_STATUS_FAILED Operation failed.
+ * @return ::CA_STATUS_OK or Appropriate error code
+ * @retval ::CA_STATUS_OK Successful
+ * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments
+ * @retval ::CA_STATUS_FAILED Operation failed
*/
-CAResult_t CAUpdateCharacteristicsToGattClient(const char* address, const char *charValue,
- const uint32_t charValueLen);
+CAResult_t CAUpdateCharacteristicsToGattClient(const char *address,
+ const char *charValue,
+ uint32_t charValueLen);
/**
- * Used to update characteristics(Read/Write) value that we want to multicast to all clients.
+ * Update characteristics(Read/Write) value that we want to multicast
+ * to all clients.
*
- * @param[in] charValue Data that we want to send to clients(multicast).
- * @param[in] charValueLen Length of the data.
+ * @param[in] charValue Data that we want to send to clients(multicast)
+ * @param[in] charValueLen Length of the data.
*
- * @return ::CA_STATUS_OK or Appropriate error code.
- * @retval ::CA_STATUS_OK Successful.
- * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
- * @retval ::CA_STATUS_FAILED Operation failed.
+ * @return ::CA_STATUS_OK or Appropriate error code
+ * @retval ::CA_STATUS_OK Successful
+ * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments
+ * @retval ::CA_STATUS_FAILED Operation failed
*/
CAResult_t CAUpdateCharacteristicsToAllGattClients(const char *charValue,
uint32_t charValueLen);
/**
- * Used to start CAStartBleGattClientThread for initializing Gatt Client.
+ * Start @c CAStartBleGattClientThread for initializing Gatt Client.
*
- * @return ::CA_STATUS_OK or Appropriate error code.
- * @retval ::CA_STATUS_OK Successful.
- * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
- * @retval ::CA_STATUS_FAILED Operation failed.
+ * @return ::CA_STATUS_OK or Appropriate error code
+ * @retval ::CA_STATUS_OK Successful
+ * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments
+ * @retval ::CA_STATUS_FAILED Operation failed
*/
CAResult_t CAStartLEGattClient();
/**
- * Used to stop Gatt Client gracefully in turn it will
- * call CATerminateBLEGattClient function.
- * @return ::CA_STATUS_OK or Appropriate error code.
- * @retval ::CA_STATUS_OK Successful.
- * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
- * @retval ::CA_STATUS_FAILED Operation failed.
+ * Stop Gatt client gracefully. In turn it will call the
+ * @c CATerminateBLEGattClient function.
+ *
+ * @return ::CA_STATUS_OK or Appropriate error code
+ * @retval ::CA_STATUS_OK Successful
+ * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments
+ * @retval ::CA_STATUS_FAILED Operation failed
*/
void CAStopLEGattClient();
/**
- * Used to unset all the callbacks and stop service discovery.
+ * Unset all the callbacks and stop service discovery
*/
void CATerminateLEGattClient();
/**
- * API to read the data from characteristics and invoke notifyCallback.
+ * Read the data from characteristics and invoke notify callback.
*/
void CACheckLEData();
/**
- * Sets the value of characteristic and update the value to GATTServer(unicast).
+ * Set the value of characteristic and update the value to
+ * GATTServer (unicast).
*
- * @param[in] remoteAddress The address of the remote device.
- * @param[in] data The value of characteristic (byte array).
- * @param[in] dataLen The length of value.
- * @param[in] type Type of the transfer(::CALETransferType_t).
- * @param[in] position The unique index of each ble server. Used for multicast feature.
+ * @param[in] remoteAddress The address of the remote device
+ * @param[in] data The value of characteristic (byte array)
+ * @param[in] dataLen The length of value
+ * @param[in] type Type of the transfer(::CALETransferType_t)
+ * @param[in] position The unique index of each ble server. Used
+ * for multicast feature.
*
- * @return ::CA_STATUS_OK or Appropriate error code.
- * @retval ::CA_STATUS_OK Successful.
- * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
- * @retval ::CA_STATUS_FAILED Operation failed.
+ * @return ::CA_STATUS_OK or Appropriate error code
+ * @retval ::CA_STATUS_OK Successful
+ * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments
+ * @retval ::CA_STATUS_FAILED Operation failed
*/
-CAResult_t CAUpdateCharacteristicsToGattServer(const char *remoteAddress, const char *data,
- const uint32_t dataLen, CALETransferType_t type,
- const int32_t position);
+CAResult_t CAUpdateCharacteristicsToGattServer(const char *remoteAddress,
+ const char *data,
+ uint32_t dataLen,
+ CALETransferType_t type,
+ int32_t position);
/**
- * Sets the value of characteristic and update the value to all registered.
- * GATTServer -> Multicast.
- * @param[in] data The value of characteristic (byte array).
- * @param[in] dataLen The length of value.
+ * Set the value of characteristic and update the value to all
+ * registered GATTServer (multicast).
*
- * @return ::CA_STATUS_OK or Appropriate error code.
- * @retval ::CA_STATUS_OK Successful.
- * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
- * @retval ::CA_STATUS_FAILED Operation failed.
+ * @param[in] data The value of characteristic (byte array)
+ * @param[in] dataLen The length of value
+ *
+ * @return ::CA_STATUS_OK or Appropriate error code
+ * @retval ::CA_STATUS_OK Successful
+ * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments
+ * @retval ::CA_STATUS_FAILED Operation failed
*/
-CAResult_t CAUpdateCharacteristicsToAllGattServers(const char *data, uint32_t dataLen);
+CAResult_t CAUpdateCharacteristicsToAllGattServers(const char *data,
+ uint32_t dataLen);
/**
- * Used to store upper layer callback locally which will be used
- * to send the data to application.
- * @param[in] callback Callback function to pass the data to CA layer.
+ * Store upper layer callback locally which will be used to send the
+ * data to application.
+ *
+ * @param[in] callback Callback function to pass the data to CA layer.
*/
-void CASetLEReqRespClientCallback(CABLEClientDataReceivedCallback callback);
+void CASetLEReqRespClientCallback(CABLEDataReceivedCallback callback);
/**
- * Used to Set the gThreadPool handle which is required for spawning new thread.
+ * Set the server thread pool handle which is required for spawning
+ * new thread.
*
- * @param[in] handle Thread pool handle which is given by above layer
- * for using thread creation task.
- * @return ::CA_STATUS_OK or Appropriate error code.
- * @retval ::CA_STATUS_OK Successful.
- * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
- * @retval ::CA_STATUS_FAILED Operation failed.
+ * @param[in] handle Thread pool handle which is given by above layer
+ * for using thread creation task.
+ *
+ * @return ::CA_STATUS_OK or Appropriate error code
+ * @retval ::CA_STATUS_OK Successful
+ * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments
+ * @retval ::CA_STATUS_FAILED Operation failed
*/
void CASetLEServerThreadPoolHandle(ca_thread_pool_t handle);
/**
-* Used to Set the gThreadPool handle which is required for spawning new thread.
-* @param[in] handle Thread pool handle which is given by above layer
-* for using thread creation task.
+* Set the client thread pool handle which is required for spawning new
+* thread.
+*
+* @param[in] handle Thread pool handle which is given by above layer
+* for using thread creation task.
*/
void CASetLEClientThreadPoolHandle(ca_thread_pool_t handle);
/**
- * Used to unset the callback of adapter connection state change.
+ * Unset the callback of adapter connection state change.
*
* @return ::CA_STATUS_OK or Appropriate error code.
* @retval ::CA_STATUS_OK Successful.
CAResult_t CAUnSetLEAdapterStateChangedCb();
/**
- * This will be used to notify errors in BLE adapter
- * @param[in] remoteAddress Remote endpoint Address.
- * @param[in] serviceUUID Service UUID.
- * @param[in] data Data received.
- * @param[in] dataLength Length of the data received.
- * @param[in] result error code as per CAResult_t.
+ * This will be used to notify errors in BLE adapter.
+ *
+ * @param[in] remoteAddress Remote endpoint Address
+ * @param[in] data Data received
+ * @param[in] dataLength Length of the data received
+ * @param[in] result error code as per CAResult_t
*/
-typedef void (*CABLEErrorHandleCallback)(const char *remoteAddress, const void *data,
- uint32_t dataLength, CAResult_t result);
+typedef void (*CABLEErrorHandleCallback)(const char *remoteAddress,
+ const void *data,
+ uint32_t dataLength,
+ CAResult_t result);
/**
- * sets the error handle callback.
- * @param[in] callback Callback function to update error to the adapter.
+ * Set the client error handler callback.
+ *
+ * @param[in] callback Callback function to update error to the
+ * adapter.
*/
void CASetBLEClientErrorHandleCallback(CABLEErrorHandleCallback callback);
/**
- * sets the error handle callback.
- * @param[in] callback Callback function to update error to the adapter.
+ * Set the server error handler callback.
+ *
+ * @param[in] callback Callback function to update error to the
+ * adapter.
*/
void CASetBLEServerErrorHandleCallback(CABLEErrorHandleCallback callback);
#endif
#endif /* CA_LE_INTERFACE_H_ */
-
src_dir = os.path.join(os.curdir, 'bt_le_adapter')
-# Source files to build common for all platforms.
-common_files = None
-common_files = [ os.path.join(src_dir,
- 'caleadapter.c') ]
-
+# Source files common to all platforms.
+common_files = [ os.path.join(src_dir, 'caleadapter.c') ]
# Get list of target-specific source file base names, i.e. no parent
# directories prepended to the path.
# The list of BLE adapter source files is a combination of both the
# common and target-specific source file lists.
env.AppendUnique(CA_SRC = common_files + target_files)
-
static ca_mutex g_gattObjectMutex = NULL;
static ca_mutex g_deviceStateListMutex = NULL;
-static CABLEClientDataReceivedCallback g_CABLEClientDataReceivedCallback = NULL;
+static CABLEDataReceivedCallback g_CABLEClientDataReceivedCallback = NULL;
//getting jvm
void CALEClientJniInit()
}
CAResult_t CAUpdateCharacteristicsToGattServer(const char *remoteAddress, const char *data,
- const uint32_t dataLen, CALETransferType_t type,
- const int32_t position)
+ uint32_t dataLen, CALETransferType_t type,
+ int32_t position)
{
OIC_LOG(DEBUG, TAG, "call CALEClientSendUnicastMessage");
VERIFY_NON_NULL(data, TAG, "data is null");
return CALEClientSendMulticastMessage(data, dataLen);
}
-void CASetLEReqRespClientCallback(CABLEClientDataReceivedCallback callback)
+void CASetLEReqRespClientCallback(CABLEDataReceivedCallback callback)
{
OIC_LOG(DEBUG, TAG, "IN");
ca_mutex_lock(g_bleServerBDAddressMutex);
uint32_t sentLength = 0;
- g_CABLEClientDataReceivedCallback(address, OIC_GATT_SERVICE_UUID, receivedData, length,
+ g_CABLEClientDataReceivedCallback(address, receivedData, length,
&sentLength);
ca_mutex_unlock(g_bleServerBDAddressMutex);
return CA_STATUS_OK;
}
+CAResult_t CAStartLEAdapter()
+{
+ // Nothing to do.
+
+ return CA_STATUS_OK;
+}
+
CAResult_t CAInitLENwkMonitorMutexVaraibles()
{
OIC_LOG(DEBUG, TAG, "IN");
static bool g_isStartServer = false;
static bool g_isInitializedServer = false;
-static CABLEServerDataReceivedCallback g_CABLEServerDataReceivedCallback = NULL;
+static CABLEDataReceivedCallback g_CABLEServerDataReceivedCallback = NULL;
static ca_mutex g_bleReqRespCbMutex = NULL;
static ca_mutex g_bleClientBDAddressMutex = NULL;
static ca_mutex g_connectedDeviceListMutex = NULL;
ca_mutex_lock(g_bleClientBDAddressMutex);
uint32_t sentLength = 0;
- g_CABLEServerDataReceivedCallback(address, OIC_GATT_SERVICE_UUID, requestData, length,
+ g_CABLEServerDataReceivedCallback(address, requestData, length,
&sentLength);
ca_mutex_unlock(g_bleClientBDAddressMutex);
OIC_LOG(DEBUG, TAG, "OUT");
}
-void CASetLEReqRespServerCallback(CABLEServerDataReceivedCallback callback)
+void CASetLEReqRespServerCallback(CABLEDataReceivedCallback callback)
{
OIC_LOG(DEBUG, TAG, "IN");
g_serverErrorCallback = callback;
}
-CAResult_t CAUpdateCharacteristicsToGattClient(const char* address, const char *charValue,
- const uint32_t charValueLen)
+CAResult_t CAUpdateCharacteristicsToGattClient(const char *address,
+ const char *charValue,
+ uint32_t charValueLen)
{
CAResult_t result = CA_SEND_FAILED;
OIC_LOG(DEBUG, TAG, "IN");
#include "cacommon.h"
#include "cathreadpool.h"
+#include "cagattservice.h"
#include "uarraylist.h"
#include "jni.h"
#endif
/* Service UUID */
-static const char OIC_GATT_SERVICE_UUID[] = "ADE3D529-C784-4F63-A987-EB69F70EE816";
-static const char OIC_GATT_CHARACTERISTIC_REQUEST_UUID[] = "AD7B334F-4637-4B86-90B6-9D787F03D218";
-static const char OIC_GATT_CHARACTERISTIC_RESPONSE_UUID[] = "E9241982-4580-42C4-8831-95048216B256";
+static const char OIC_GATT_SERVICE_UUID[] = CA_GATT_SERVICE_UUID;
+static const char OIC_GATT_CHARACTERISTIC_REQUEST_UUID[] = CA_GATT_REQUEST_CHRC_UUID;
+static const char OIC_GATT_CHARACTERISTIC_RESPONSE_UUID[] = CA_GATT_RESPONSE_CHRC_UUID;
static const char OIC_GATT_CHARACTERISTIC_CONFIG_UUID[] = "00002902-0000-1000-8000-00805f9b34fb";
static const uint32_t GATT_SUCCESS = 0;
OIC_LOG(DEBUG, TAG, "IN");
OIC_LOG(DEBUG, TAG, "OUT");
}
-void CASetLEReqRespClientCallback(CABLEClientDataReceivedCallback callback)
+void CASetLEReqRespClientCallback(CABLEDataReceivedCallback callback)
{
OIC_LOG(DEBUG, TAG, "IN");
OIC_LOG(DEBUG, TAG, "OUT");
return CA_STATUS_OK;
}
+CAResult_t CAStartLEAdapter()
+{
+ // Nothing to do.
+
+ return CA_STATUS_OK;
+}
+
CAResult_t CAGetLEAdapterState()
{
OIC_LOG(DEBUG, TAG, "IN");
* @brief Maintains the callback to be notified on receival of network packets from other
* BLE devices
*/
-static CABLEServerDataReceivedCallback g_bleServerDataReceivedCallback = NULL;
+static CABLEDataReceivedCallback g_bleServerDataReceivedCallback = NULL;
/**
* @def MAX_EVENT_COUNT
OIC_LOG_V(DEBUG, TAG, "recv dataLen=%d", g_receivedDataLen);
uint32_t sentLength = 0;
// g_coapBuffer getting freed by CAMesssageHandler
- g_bleServerDataReceivedCallback("", "", g_coapBuffer,
+ g_bleServerDataReceivedCallback("", g_coapBuffer,
g_receivedDataLen, &sentLength);
}
return CA_STATUS_OK;
}
-void CASetLEReqRespServerCallback(CABLEServerDataReceivedCallback callback)
+void CASetLEReqRespServerCallback(CABLEDataReceivedCallback callback)
{
OIC_LOG(DEBUG, TAG, "IN");
g_bleServerDataReceivedCallback = callback;
*/
#define CALEADAPTER_TAG "LAD"
+
+/**
+ * Stores the information of the Data to be sent from the queues.
+ *
+ * This structure will be pushed to the sender/receiver queue for
+ * processing.
+ */
+typedef struct
+{
+ CAEndpoint_t *remoteEndpoint; /**< Remote endpoint contains the
+ information of remote device. */
+ void *data; /**< Data to be transmitted over LE transport. */
+ uint32_t dataLen; /**< Length of the data being transmitted. */
+} CALEData_t;
+
+/**
+ * Stores information of all the senders.
+ *
+ * This structure will be used to track and defragment all incoming
+ * data packet.
+ */
+typedef struct
+{
+ uint32_t recvDataLen;
+ uint32_t totalDataLen;
+ char *defragData;
+ CAEndpoint_t *remoteEndpoint;
+ } CABLESenderInfo_t;
+
/**
* Callback to provide the status of the network change to CA layer.
*/
static CANetworkChangeCallback g_networkCallback = NULL;
/**
- * bleAddress of the local adapter. Value will be initialized to zero, and will
- * be updated later.
+ * bleAddress of the local adapter. Value will be initialized to zero,
+ * and will be updated later.
*/
-static char g_localBLEAddress[18] = {0};
+static char g_localBLEAddress[18] = { 0 };
/**
* Variable to differentiate btw GattServer and GattClient.
static bool g_isServer = false;
/**
- * Mutex to synchronize the task to be executed on the GattServer function
- * calls.
+ * Mutex to synchronize the task to be executed on the GattServer
+ * function calls.
*/
static ca_mutex g_bleIsServerMutex = NULL;
/**
- * Mutex to synchronize the callback to be called for the network changes.
+ * Mutex to synchronize the callback to be called for the network
+ * changes.
*/
static ca_mutex g_bleNetworkCbMutex = NULL;
/**
- * Mutex to synchronize the updation of the local LE address of the adapter.
+ * Mutex to synchronize the updates of the local LE address of the
+ * adapter.
*/
static ca_mutex g_bleLocalAddressMutex = NULL;
/**
- * reference to threadpool.
+ * Reference to thread pool.
*/
static ca_thread_pool_t g_bleAdapterThreadPool = NULL;
static ca_mutex g_bleServerSendDataMutex = NULL;
/**
- * Mutex to synchronize the callback to be called for the adapterReqResponse.
+ * Mutex to synchronize the callback to be called for the
+ * adapterReqResponse.
*/
static ca_mutex g_bleAdapterReqRespCbMutex = NULL;
/**
- * Callback to be called when network packet recieved from either
+ * Callback to be called when network packet received from either
* GattServer or GattClient.
*/
static CANetworkPacketReceivedCallback g_networkPacketReceivedCallback = NULL;
static CAAdapterState_t g_bleAdapterState = CA_ADAPTER_DISABLED;
/**
- * status of BLE Server Status.
- * This ENUM provides information of LE Adapter Server status.
+ * BLE Server Status.
+ *
+ * This enumeration provides information of LE Adapter Server status.
*/
typedef enum
{
} CALeServerStatus;
/**
- * structure to maintain the status of the server.
+ * Structure to maintain the status of the server.
*/
static CALeServerStatus gLeServerStatus = CA_SERVER_NOTSTARTED;
/**
-* This function is used to register network change notification callback.
-*
-* @param[in] netCallback CANetworkChangeCallback callback which will be
-* set for the change in nwk.
-*
-* @return 0 on success otherwise a positive error value.
-* @retval CA_STATUS_OK Successful.
-* @retval CA_STATUS_INVALID_PARAM Invalid input argumets.
-* @retval CA_STATUS_FAILED Operation failed.
-*
-*/
-CAResult_t CALERegisterNetworkNotifications(CANetworkChangeCallback netCallback);
+ * Register network change notification callback.
+ *
+ * @param[in] netCallback CANetworkChangeCallback callback which will
+ * be set for the change in network.
+ *
+ * @return 0 on success otherwise a positive error value.
+ * @retval ::CA_STATUS_OK Successful.
+ * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
+ * @retval ::CA_STATUS_FAILED Operation failed.
+ *
+ */
+static CAResult_t CALERegisterNetworkNotifications(CANetworkChangeCallback netCallback);
/**
-* Used to Set the gThreadPool handle which is required for spawning new thread.
-*
-* @param[in] handle - Thread pool handle which is given by above layer for
-* using thread creation task.
-*
-*/
-void CASetLEAdapterThreadPoolHandle(ca_thread_pool_t handle);
+ * Set the thread pool handle which is required for spawning new
+ * thread.
+ *
+ * @param[in] handle Thread pool handle which is given by above layer
+ * for using thread creation task.
+ *
+ */
+static void CASetLEAdapterThreadPoolHandle(ca_thread_pool_t handle);
/**
-* This function is used to call the callback to the upper layer when the
-* device state gets changed.
-*
-* @param[in] adapter_state New state of the adapter to be notified to the
-* upper layer.
-*
-*/
-void CALEDeviceStateChangedCb( CAAdapterState_t adapter_state);
+ * Call the callback to the upper layer when the device state gets
+ * changed.
+ *
+ * @param[in] adapter_state New state of the adapter to be notified to
+ * the upper layer.
+ */
+static void CALEDeviceStateChangedCb( CAAdapterState_t adapter_state);
/**
-* Used to initialize all required mutex variable for LE Adapter implementation.
-*
-* @return 0 on success otherwise a positive error value.
-* @retval CA_STATUS_OK Successful.
-* @retval CA_STATUS_INVALID_PARAM Invalid input argumets.
-* @retval CA_STATUS_FAILED Operation failed.
-*
-*/
-CAResult_t CAInitLEAdapterMutex();
+ * Used to initialize all required mutex variable for LE Adapter
+ * implementation.
+ *
+ * @return 0 on success otherwise a positive error value.
+ * @retval ::CA_STATUS_OK Successful.
+ * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
+ * @retval ::CA_STATUS_FAILED Operation failed.
+ *
+ */
+static CAResult_t CAInitLEAdapterMutex();
/**
-* Used to terminate all required mutex variable for LE adapter implementation.
-*
-*/
-void CATerminateLEAdapterMutex();
+ * Terminate all required mutex variables for LE adapter
+ * implementation.
+ */
+static void CATerminateLEAdapterMutex();
/**
-* prepares and notify error through error callback.
-*
-*/
-static void CALEErrorHandler(const char *remoteAddress, const void *data, uint32_t dataLen,
+ * Prepares and notify error through error callback.
+ *
+ */
+static void CALEErrorHandler(const char *remoteAddress,
+ const void *data,
+ uint32_t dataLen,
CAResult_t result);
#ifndef SINGLE_THREAD
static bool g_dataReceiverHandlerState = false;
/**
- * Sender informations to be stored here
+ * Sender information.
*/
static u_arraylist_t *g_senderInfo = NULL;
static CAQueueingThread_t *g_bleServerSendQueueHandle = NULL;
/**
-* Used to free data.
-*
-*/
-static void CALEDataDestroyer(void *data, uint32_t size);
+ * Starting LE connectivity adapters.
+ *
+ * As its peer to peer it does not require to start any servers.
+ *
+ * @return ::CA_STATUS_OK or Appropriate error code.
+ */
+static CAResult_t CAStartLE();
-void CAInitLEQueues()
-{
- OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
+/**
+ * Start listening server for receiving multicast search requests.
+ *
+ * Transport Specific Behavior:
+ * LE Starts GATT Server with prefixed UUID and Characteristics
+ * per OIC Specification.
+ * @return ::CA_STATUS_OK or Appropriate error code.
+ */
+static CAResult_t CAStartLEListeningServer();
- CAResult_t result = CAInitLEServerQueues();
- if (CA_STATUS_OK != result)
- {
- OIC_LOG(ERROR, CALEADAPTER_TAG, "CAInitBleServerQueues failed");
- return;
- }
+/**
+ * Sarting discovery of servers for receiving multicast
+ * advertisements.
+ *
+ * Transport Specific Behavior:
+ * LE Starts GATT Server with prefixed UUID and Characteristics
+ * per OIC Specification.
+ *
+ * @return ::CA_STATUS_OK or Appropriate error code
+ */
+static CAResult_t CAStartLEDiscoveryServer();
- result = CAInitLEClientQueues();
- if (CA_STATUS_OK != result)
- {
- OIC_LOG(ERROR, CALEADAPTER_TAG, "CAInitBleClientQueues failed");
- return;
- }
+/**
+ * Send data to the endpoint using the adapter connectivity.
+ *
+ * @param[in] endpoint Remote Endpoint information (like MAC address,
+ * reference URI and connectivity type) to which
+ * the unicast data has to be sent.
+ * @param[in] data Data which required to be sent.
+ * @param[in] dataLen Size of data to be sent.
+ *
+ * @note dataLen must be > 0.
+ *
+ * @return The number of bytes sent on the network, or -1 on error.
+ */
+static int32_t CASendLEUnicastData(const CAEndpoint_t *endpoint,
+ const void *data,
+ uint32_t dataLen);
- OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT");
-}
+/**
+ * Send multicast data to the endpoint using the LE connectivity.
+ *
+ * @param[in] endpoint Remote Endpoint information to which the
+ * multicast data has to be sent.
+ * @param[in] data Data which required to be sent.
+ * @param[in] dataLen Size of data to be sent.
+ *
+ * @note dataLen must be > 0.
+ *
+ * @return The number of bytes sent on the network, or -1 on error.
+ */
+static int32_t CASendLEMulticastData(const CAEndpoint_t *endpoint,
+ const void *data,
+ uint32_t dataLen);
+
+/**
+ * Get LE Connectivity network information.
+ *
+ * @param[out] info Local connectivity information structures.
+ * @param[out] size Number of local connectivity structures.
+ *
+ * @return ::CA_STATUS_OK or Appropriate error code.
+ */
+static CAResult_t CAGetLEInterfaceInformation(CAEndpoint_t **info,
+ uint32_t *size);
+
+/**
+ * Read Synchronous API callback.
+ *
+ * @return ::CA_STATUS_OK or Appropriate error code.
+ */
+static CAResult_t CAReadLEData();
+
+/**
+ * Stopping the adapters and close socket connections.
+ *
+ * LE Stops all GATT servers and GATT Clients.
+ *
+ * @return ::CA_STATUS_OK or Appropriate error code.
+ */
+static CAResult_t CAStopLE();
+
+/**
+ * Terminate the LE connectivity adapter.
+ *
+ * Configuration information will be deleted from further use.
+ */
+static void CATerminateLE();
+
+/**
+ * This function will receive the data from the GattServer and add the
+ * data to the Server receiver queue.
+ *
+ * @param[in] remoteAddress Remote address of the device from where
+ * data is received.
+ * @param[in] data Actual data recevied from the remote
+ * device.
+ * @param[in] dataLength Length of the data received from the
+ * remote device.
+ * @param[in] sentLength Length of the data sent from the remote
+ * device.
+ *
+ * @return ::CA_STATUS_OK or Appropriate error code.
+ * @retval ::CA_STATUS_OK Successful.
+ * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
+ * @retval ::CA_STATUS_FAILED Operation failed.
+ *
+ */
+static CAResult_t CALEAdapterServerReceivedData(const char *remoteAddress,
+ const void *data,
+ uint32_t dataLength,
+ uint32_t *sentLength);
+
+/**
+ * This function will receive the data from the GattClient and add the
+ * data into the Client receiver queue.
+ *
+ * @param[in] remoteAddress Remote address of the device from where
+ * data is received.
+ * @param[in] data Actual data recevied from the remote
+ * device.
+ * @param[in] dataLength Length of the data received from the
+ * remote device.
+ * @param[in] sentLength Length of the data sent from the remote
+ * device.
+ *
+ * @return ::CA_STATUS_OK or Appropriate error code.
+ * @retval ::CA_STATUS_OK Successful.
+ * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
+ * @retval ::CA_STATUS_FAILED Operation failed.
+ */
+static CAResult_t CALEAdapterClientReceivedData(const char *remoteAddress,
+ const void *data,
+ uint32_t dataLength,
+ uint32_t *sentLength);
+
+/**
+ * Set the NetworkPacket received callback to CA layer from adapter
+ * layer.
+ *
+ * @param[in] callback Callback handle sent from the upper layer.
+ */
+static void CASetLEReqRespAdapterCallback(CANetworkPacketReceivedCallback callback);
-CAResult_t CAInitLEServerQueues()
+/**
+ * Push the data from CA layer to the Sender processor queue.
+ *
+ * @param[in] remoteEndpoint Remote endpoint information of the
+ * server.
+ * @param[in] data Data to be transmitted from LE.
+ * @param[in] dataLen Length of the Data being transmitted.
+ *
+ * @return ::CA_STATUS_OK or Appropriate error code.
+ * @retval ::CA_STATUS_OK Successful.
+ * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
+ * @retval ::CA_STATUS_FAILED Operation failed.
+ */
+static CAResult_t CALEAdapterServerSendData(const CAEndpoint_t *remoteEndpoint,
+ const void *data,
+ uint32_t dataLen);
+
+/**
+ * Push the data from CA layer to the Sender processor queue.
+ *
+ * @param[in] remoteEndpoint Remote endpoint information of the
+ * server.
+ * @param[in] data Data to be transmitted from LE.
+ * @param[in] dataLen Length of the Data being transmitted.
+ *
+ * @return ::CA_STATUS_OK or Appropriate error code.
+ * @retval ::CA_STATUS_OK Successful.
+ * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
+ * @retval ::CA_STATUS_FAILED Operation failed.
+ */
+static CAResult_t CALEAdapterClientSendData(const CAEndpoint_t *remoteEndpoint,
+ const void *data,
+ uint32_t dataLen);
+
+/**
+ * This function will be associated with the sender queue for
+ * GattServer.
+ *
+ * This function will fragment the data to the MTU of the transport
+ * and send the data in fragments to the adapters. The function will
+ * be blocked until all data is sent out from the adapter.
+ *
+ * @param[in] threadData Data pushed to the queue which contains the
+ * info about RemoteEndpoint and Data.
+ */
+static void CALEServerSendDataThread(void *threadData);
+
+/**
+ * This function will be associated with the sender queue for
+ * GattClient.
+ *
+ * This function will fragment the data to the MTU of the transport
+ * and send the data in fragments to the adapters. The function will
+ * be blocked until all data is sent out from the adapter.
+ *
+ * @param[in] threadData Data pushed to the queue which contains the
+ * info about RemoteEndpoint and Data.
+ */
+static void CALEClientSendDataThread(void *threadData);
+
+/**
+ * This function will be associated with the receiver queue.
+ *
+ * This function will defragment the received data from each sender
+ * respectively and will send it up to CA layer. Respective sender's
+ * header will provide the length of the data sent.
+ *
+ * @param[in] threadData Data pushed to the queue which contains the
+ * info about RemoteEndpoint and Data.
+ */
+static void CALEDataReceiverHandler(void *threadData);
+
+/**
+ * This function will stop all queues created for GattServer and
+ * GattClient. All four queues will be be stopped with this function
+ * invocations.
+ */
+static void CAStopLEQueues();
+
+/**
+ * This function will terminate all queues created for GattServer and
+ * GattClient. All four queues will be be terminated with this
+ * function invocations.
+ */
+static void CATerminateLEQueues();
+
+/**
+ * This function will initalize the Receiver and Sender queues for
+ * GattServer. This function will in turn call the functions
+ * CAInitBleServerReceiverQueue() and CAInitBleServerSenderQueue() to
+ * initialize the queues.
+ *
+ * @return ::CA_STATUS_OK or Appropriate error code.
+ * @retval ::CA_STATUS_OK Successful.
+ * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
+ * @retval ::CA_STATUS_FAILED Operation failed.
+ */
+static CAResult_t CAInitLEServerQueues();
+
+/**
+ * This function will initalize the Receiver and Sender queues for
+ * GattClient. This function will inturn call the functions
+ * CAInitBleClientReceiverQueue() and CAInitBleClientSenderQueue() to
+ * initialize the queues.
+ *
+ * @return ::CA_STATUS_OK or Appropriate error code.
+ * @retval ::CA_STATUS_OK Successful.
+ * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
+ * @retval ::CA_STATUS_FAILED Operation failed.
+ *
+ */
+static CAResult_t CAInitLEClientQueues();
+
+/**
+ * This function will initalize the Receiver queue for
+ * GattServer. This will initialize the queue to process the function
+ * CABLEServerSendDataThread() when ever the task is added to this
+ * queue.
+ *
+ * @return ::CA_STATUS_OK or Appropriate error code.
+ * @retval ::CA_STATUS_OK Successful.
+ * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
+ * @retval ::CA_STATUS_FAILED Operation failed.
+ */
+static CAResult_t CAInitLEServerSenderQueue();
+
+/**
+ * This function will initalize the Receiver queue for
+ * GattClient. This will initialize the queue to process the function
+ * CABLEClientSendDataThread() when ever the task is added to this
+ * queue.
+ *
+ * @return ::CA_STATUS_OK or Appropriate error code.
+ * @retval ::CA_STATUS_OK Successful.
+ * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
+ * @retval ::CA_STATUS_FAILED Operation failed.
+ */
+static CAResult_t CAInitLEClientSenderQueue();
+
+/**
+ * This function will initialize the Receiver queue for
+ * LEAdapter. This will initialize the queue to process the function
+ * CABLEDataReceiverHandler() when ever the task is added to this
+ * queue.
+ *
+ * @return ::CA_STATUS_OK or Appropriate error code
+ * @retval ::CA_STATUS_OK Successful
+ * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments
+ * @retval ::CA_STATUS_FAILED Operation failed
+ *
+ */
+static CAResult_t CAInitLEReceiverQueue();
+
+/**
+ * This function will create the Data required to send it in the
+ * queue.
+ *
+ * @param[in] remoteEndpoint Remote endpoint information of the
+ * server.
+ * @param[in] data Data to be transmitted from LE.
+ * @param[in] dataLength Length of the Data being transmitted.
+ *
+ * @return ::CA_STATUS_OK or Appropriate error code.
+ * @retval ::CA_STATUS_OK Successful.
+ * @retval ::CA_STATUS_INVALID_PARAM Invalid input arguments.
+ * @retval ::CA_STATUS_FAILED Operation failed.
+ */
+static CALEData_t *CACreateLEData(const CAEndpoint_t *remoteEndpoint,
+ const void *data,
+ uint32_t dataLength);
+
+/**
+ * Used to free the BLE information stored in the sender/receiver
+ * queues.
+ *
+ * @param[in] bleData Information for a particular data segment.
+ */
+static void CAFreeLEData(CALEData_t *bleData);
+
+/**
+ * Free data.
+ */
+static void CALEDataDestroyer(void *data, uint32_t size);
+
+static CAResult_t CAInitLEServerQueues()
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
return CA_STATUS_OK;
}
-CAResult_t CAInitLEClientQueues()
+static CAResult_t CAInitLEClientQueues()
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
return CA_STATUS_OK;
}
-CAResult_t CAInitLEReceiverQueue()
+static CAResult_t CAInitLEReceiverQueue()
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
// Check if the message queue is already initialized
return CA_STATUS_OK;
}
-CAResult_t CAInitLEServerSenderQueue()
+static CAResult_t CAInitLEServerSenderQueue()
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
// Check if the message queue is already initialized
return CA_STATUS_OK;
}
-void CALEClearSenderInfo()
+static void CALEClearSenderInfo()
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT");
}
-CAResult_t CAInitLEClientSenderQueue()
+static CAResult_t CAInitLEClientSenderQueue()
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
return CA_STATUS_OK;
}
-void CAStopLEQueues()
+static void CAStopLEQueues()
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT");
}
-void CATerminateLEQueues()
+static void CATerminateLEQueues()
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT");
}
-CAResult_t CALEGetSenderInfo(char *leAddress, CABLESenderInfo_t **senderInfo,
- uint32_t *senderIndex)
+static CAResult_t CALEGetSenderInfo(char *leAddress,
+ CABLESenderInfo_t **senderInfo,
+ uint32_t *senderIndex)
{
VERIFY_NON_NULL_RET(leAddress, CALEADAPTER_TAG, "Ble-Address in-param NULL", CA_STATUS_FAILED);
VERIFY_NON_NULL_RET(senderIndex, CALEADAPTER_TAG, "Index in-param NULL", CA_STATUS_FAILED);
return CA_STATUS_FAILED;
}
-void CALEDataReceiverHandler(void *threadData)
+static void CALEDataReceiverHandler(void *threadData)
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT");
}
-void CALEServerSendDataThread(void *threadData)
+static void CALEServerSendDataThread(void *threadData)
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT");
}
-void CALEClientSendDataThread(void *threadData)
+static void CALEClientSendDataThread(void *threadData)
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT - CABLEClientSendDataThread");
}
-CALEData_t *CACreateLEData(const CAEndpoint_t *remoteEndpoint, const void *data,
- uint32_t dataLength)
+static CALEData_t *CACreateLEData(const CAEndpoint_t *remoteEndpoint,
+ const void *data,
+ uint32_t dataLength)
{
CALEData_t *bleData = (CALEData_t *) OICMalloc(sizeof(CALEData_t));
if (!bleData)
return bleData;
}
-void CAFreeLEData(CALEData_t *bleData)
+static void CAFreeLEData(CALEData_t *bleData)
{
VERIFY_NON_NULL_VOID(bleData, CALEADAPTER_TAG, "Param bleData is NULL");
OICFree(bleData);
}
-void CALEDataDestroyer(void *data, uint32_t size)
+static void CALEDataDestroyer(void *data, uint32_t size)
{
CALEData_t *ledata = (CALEData_t *) data;
}
#endif
-CAResult_t CAInitLEAdapterMutex()
+static CAResult_t CAInitLEAdapterMutex()
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
return CA_STATUS_OK;
}
-void CATerminateLEAdapterMutex()
+static void CATerminateLEAdapterMutex()
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
CAResult_t CAInitializeLE(CARegisterConnectivityCallback registerCallback,
CANetworkPacketReceivedCallback reqRespCallback,
CANetworkChangeCallback netCallback,
- CAErrorHandleCallback errorCallback, ca_thread_pool_t handle)
+ CAErrorHandleCallback errorCallback,
+ ca_thread_pool_t handle)
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
g_errorHandler = errorCallback;
- CAConnectivityHandler_t connHandler;
- connHandler.startAdapter = CAStartLE;
- connHandler.stopAdapter = CAStopLE;
- connHandler.startListenServer = CAStartLEListeningServer;
- connHandler.startDiscoveryServer = CAStartLEDiscoveryServer;
- connHandler.sendData = CASendLEUnicastData;
- connHandler.sendDataToAll = CASendLEMulticastData;
- connHandler.GetnetInfo = CAGetLEInterfaceInformation;
- connHandler.readData = CAReadLEData;
- connHandler.terminate = CATerminateLE;
+ static const CAConnectivityHandler_t connHandler =
+ {
+ .startAdapter = CAStartLE,
+ .stopAdapter = CAStopLE,
+ .startListenServer = CAStartLEListeningServer,
+ .startDiscoveryServer = CAStartLEDiscoveryServer,
+ .sendData = CASendLEUnicastData,
+ .sendDataToAll = CASendLEMulticastData,
+ .GetnetInfo = CAGetLEInterfaceInformation,
+ .readData = CAReadLEData,
+ .terminate = CATerminateLE
+ };
+
registerCallback(connHandler, CA_ADAPTER_GATT_BTLE);
OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT");
return CA_STATUS_OK;
}
-CAResult_t CAStartLE()
+static CAResult_t CAStartLE()
{
- OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
- OIC_LOG(DEBUG, CALEADAPTER_TAG, "CAStartLE, not implemented");
- OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT");
- return CA_STATUS_OK;
+ OIC_LOG(DEBUG, CALEADAPTER_TAG, __func__);
+
+ return CAStartLEAdapter();
}
-CAResult_t CAStopLE()
+static CAResult_t CAStopLE()
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
#ifndef SINGLE_THREAD
return CA_STATUS_OK;
}
-void CATerminateLE()
+static void CATerminateLE()
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT");
}
-CAResult_t CAStartLEListeningServer()
+static CAResult_t CAStartLEListeningServer()
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
CAResult_t result = CA_STATUS_OK;
return CA_STATUS_OK;
}
-CAResult_t CAStartLEDiscoveryServer()
+static CAResult_t CAStartLEDiscoveryServer()
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
CAResult_t result = CA_STATUS_OK;
return CA_STATUS_OK;
}
-CAResult_t CAStartLENotifyServer()
-{
- OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
-
- OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT");
- return CA_STATUS_OK;
-}
-
-uint32_t CASendLENotification(const CAEndpoint_t *endpoint, const void *data,
- uint32_t dataLen)
-{
- OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
-
- OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT");
- return 0;
-}
-
-CAResult_t CAReadLEData()
+static CAResult_t CAReadLEData()
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
#ifdef SINGLE_THREAD
return CA_STATUS_OK;
}
-int32_t CASendLEUnicastData(const CAEndpoint_t *endpoint, const void *data, uint32_t dataLen)
+static int32_t CASendLEUnicastData(const CAEndpoint_t *endpoint,
+ const void *data,
+ uint32_t dataLen)
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
return dataLen;
}
-int32_t CASendLEMulticastData(const CAEndpoint_t *endpoint, const void *data, uint32_t dataLen)
+static int32_t CASendLEMulticastData(const CAEndpoint_t *endpoint,
+ const void *data,
+ uint32_t dataLen)
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
return dataLen;
}
-CAResult_t CAGetLEInterfaceInformation(CAEndpoint_t **info, uint32_t *size)
+static CAResult_t CAGetLEInterfaceInformation(CAEndpoint_t **info, uint32_t *size)
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
return CA_STATUS_OK;
}
-CAResult_t CALERegisterNetworkNotifications(CANetworkChangeCallback netCallback)
+static CAResult_t CALERegisterNetworkNotifications(CANetworkChangeCallback netCallback)
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
return res;
}
-void CALEDeviceStateChangedCb( CAAdapterState_t adapter_state)
+static void CALEDeviceStateChangedCb( CAAdapterState_t adapter_state)
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT");
}
-CAResult_t CALEAdapterClientSendData(const CAEndpoint_t *remoteEndpoint,
- const void *data,
- uint32_t dataLen)
+static CAResult_t CALEAdapterClientSendData(const CAEndpoint_t *remoteEndpoint,
+ const void *data,
+ uint32_t dataLen)
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
return CA_STATUS_OK;
}
-
-CAResult_t CALEAdapterServerSendData(const CAEndpoint_t *remoteEndpoint,
- const void *data,
- uint32_t dataLen)
+static CAResult_t CALEAdapterServerSendData(const CAEndpoint_t *remoteEndpoint,
+ const void *data,
+ uint32_t dataLen)
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
return CA_STATUS_OK;
}
-CAResult_t CALEAdapterServerReceivedData(const char *remoteAddress, const char *serviceUUID,
- const void *data, uint32_t dataLength,
- uint32_t *sentLength)
+static CAResult_t CALEAdapterServerReceivedData(const char *remoteAddress,
+ const void *data,
+ uint32_t dataLength,
+ uint32_t *sentLength)
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
//Input validation
- VERIFY_NON_NULL(serviceUUID, CALEADAPTER_TAG, "service UUID is null");
VERIFY_NON_NULL(data, CALEADAPTER_TAG, "Data is null");
VERIFY_NON_NULL(sentLength, CALEADAPTER_TAG, "Sent data length holder is null");
}
CAFreeEndpoint(remoteEndpoint);
- // Add message to send queue
+ // Add message to receiver queue
CAQueueingThreadAddData(g_bleReceiverQueue, bleData, sizeof(CALEData_t));
*sentLength = dataLength;
return CA_STATUS_OK;
}
-CAResult_t CALEAdapterClientReceivedData(const char *remoteAddress, const char *serviceUUID,
- const void *data, uint32_t dataLength,
- uint32_t *sentLength)
+static CAResult_t CALEAdapterClientReceivedData(const char *remoteAddress,
+ const void *data,
+ uint32_t dataLength,
+ uint32_t *sentLength)
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
//Input validation
- VERIFY_NON_NULL(serviceUUID, CALEADAPTER_TAG, "service UUID is null");
VERIFY_NON_NULL(data, CALEADAPTER_TAG, "Data is null");
VERIFY_NON_NULL(sentLength, CALEADAPTER_TAG, "Sent data length holder is null");
#ifndef SINGLE_THREAD
}
CAFreeEndpoint(remoteEndpoint);
- // Add message to send queue
+ // Add message to receiver queue
CAQueueingThreadAddData(g_bleReceiverQueue, bleData, sizeof(CALEData_t));
*sentLength = dataLength;
return CA_STATUS_OK;
}
-void CASetLEAdapterThreadPoolHandle(ca_thread_pool_t handle)
+static void CASetLEAdapterThreadPoolHandle(ca_thread_pool_t handle)
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
- ca_mutex_unlock(g_bleAdapterThreadPoolMutex);
+ ca_mutex_lock(g_bleAdapterThreadPoolMutex);
g_bleAdapterThreadPool = handle;
ca_mutex_unlock(g_bleAdapterThreadPoolMutex);
OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT");
}
-void CASetLEReqRespAdapterCallback(CANetworkPacketReceivedCallback callback)
+static void CASetLEReqRespAdapterCallback(CANetworkPacketReceivedCallback callback)
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "IN");
OIC_LOG(DEBUG, CALEADAPTER_TAG, "OUT");
}
-void CALEErrorHandler(const char *remoteAddress, const void *data, uint32_t dataLen,
- CAResult_t result)
+static void CALEErrorHandler(const char *remoteAddress,
+ const void *data,
+ uint32_t dataLen,
+ CAResult_t result)
{
OIC_LOG(DEBUG, CALEADAPTER_TAG, "CALEErrorHandler IN");
VERIFY_NON_NULL_VOID(data, CALEADAPTER_TAG, "Data is null");
- CAEndpoint_t *rep = CACreateEndpointObject(CA_DEFAULT_FLAGS, CA_ADAPTER_GATT_BTLE,
- remoteAddress, 0);
+ CAEndpoint_t *rep = CACreateEndpointObject(CA_DEFAULT_FLAGS,
+ CA_ADAPTER_GATT_BTLE,
+ remoteAddress,
+ 0);
//if required, will be used to build remote end point
g_errorHandler(rep, data, dataLen, result);
--- /dev/null
+ OIC GATT Transport Service for Linux
+ ====================================
+
+This is an IoTivity BLE adapter implementation of the OIC GATT
+Transport specification for Linux. It relies on the GATT and LE
+advertisement functionality found in BlueZ 5.31 or better. As of
+BlueZ 5.31, the LE advertisement APIs are still considered
+experimental, meaning specific steps must be taken to enable them for
+that version of BlueZ, as described below.
+
+LE advertisement support requires Linux kernel 4.1 better. If a LE
+peripheral won't be started, Linux kernel 3.19 is sufficient for LE
+central-only use cases.
+
+Enable LE Advertisement Support in BlueZ 5.31
+---------------------------------------------
+1. Backport the advertising patch that was created after BlueZ 5.31
+was released. If you've cloned the BlueZ git repository this can be
+achieved by cherry picking the patch as follows:
+
+ git clone git://git.kernel.org/pub/scm/bluetooth/bluez.git
+ cd bluez
+ git checkout 5.31
+ git cherry-pick c73c1002331b2c0de0eaf555b5f1d05149f9a3b0
+
+Otherwise apply to BlueZ 5.31 the patch file found in the IoTivity
+`extlibs/bluez' directory:
+
+ 0001-core-advertising-Fix-using-wrong-instance-id.patch
+
+2. Enable experimental APIs when building BlueZ 5.31, e.g.:
+
+ ./configure --enable-experimental ...
+
+3. Enable experimental APIs at run-time by starting bluetoothd with
+ the "--experimental" command line flag. If you've installed BlueZ
+ on a platform with Systemd support, this can be done by appending
+ "--experimental" to the [Service] ExecStart option in the
+ bluetooth.service unit file (e.g.
+ /lib/systemd/system/bluetooth.service). If you're going to build
+ and install BlueZ from source often, you may want to patch the
+ `bluetooth.service.in' in your bluez source directory instead so
+ that you won't have to modify the unit file after installation:
+
+diff --git a/src/bluetooth.service.in b/src/bluetooth.service.in
+index 35e9457..368df4c 100644
+--- a/src/bluetooth.service.in
++++ b/src/bluetooth.service.in
+@@ -5,7 +5,7 @@ Documentation=man:bluetoothd(8)
+ [Service]
+ Type=dbus
+ BusName=org.bluez
+-ExecStart=@libexecdir@/bluetoothd
++ExecStart=@libexecdir@/bluetoothd --experimental
+ NotifyAccess=main
+ #WatchdogSec=10
+ #Restart=on-failure
+# ------------------------------------------------------------------------
+# Copyright 2015 Intel Corporation
+#
+# 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.
+# ------------------------------------------------------------------------
+
##########################################
# Build BLE adapter for Linux
##########################################
Import('env')
-src_files = [ 'caleadapter.c']
+import os.path
+
+# Make sure the <bluetooth/bluetooth.h> and <bluetooth/hci.h> headers
+# necessary for use of the kernel Bluetooth management API are
+# available. Only those headers are required. There is no need to
+# build BlueZ in order to build IoTivity's BLE transport for Linux.
+env.SConscript('#extlibs/bluez/SConscript')
+
+# Top-level build (variant) directory.
+root_build_dir = env['BUILD_DIR']
+
+# Build (variant) directory corresponding to this source directory.
+this_build_dir = os.path.join(root_build_dir,
+ os.path.dirname(File(SConscript).srcnode().path))
+
+# The Linux BLE adapter implementation uses GDBus to make D-Bus based
+# method calls to BlueZ. Pull in the necessary dependencies.
+env.ParseConfig("pkg-config gio-unix-2.0 --cflags --libs")
+
+# Set up commands to generate GDBus code from the D-Bus introspection
+# XML.
+freedesktop_prefix = 'org.freedesktop.DBus.'
+bluez_prefix = 'org.bluez.'
+
+dbus_introspection_xml = {
+ 'object_manager' : freedesktop_prefix,
+ 'bluez' : bluez_prefix,
+}
+
+# The source files to be compiled as part of the connectivity
+# abstraction library.
+glue_files = []
+
+for file, prefix in dbus_introspection_xml.items():
+ source_xml = file + '.xml'
+ glue = file + '-glue'
+ glue_source = glue + '.c'
+ glue_header = glue + '.h'
+ targets = [ glue_source, glue_header ]
+
+ # Include the glue header in the list as well to make sure it is
+ # generated before other source files that depend on it are
+ # compiled.
+ glue_files += targets
+
+ # Generate GDBus skeletons in the variant (build) directory.
+ env.Command(targets,
+ source_xml,
+ 'cd %s '
+ '&& gdbus-codegen --generate-c-code %s --interface-prefix %s %s '
+ '&& cd -'
+ % (this_build_dir,
+ glue,
+ prefix,
+ os.path.join(env['SRC_DIR'], '$SOURCE')))
+
+ # Mark generated file for cleaning when running "scons -c".
+ #
+ # @todo Verify that the generated *-glue.[ch] files are removed on
+ # running "scons -c" once that is working in the master
+ # branch again.
+ for target in targets:
+ generated_target = os.path.join(this_build_dir, target)
+ env.Clean(target, generated_target)
+
+# The generated "glue" headers are found in the build directory
+# corresponding to this source directory.
+env.AppendUnique(CPPPATH = this_build_dir)
+
+# The Linux BLE transport exports its GATT and LE advertisement
+# related D-Bus interfaces to the D-Bus system bus so that they may be
+# accessed by BlueZ. Set the bus names here, i.e. in one place, to
+# avoid potential mismatches, and generate the D-Bus policy
+# configuration file and related C preprocessor symbol definitions.
+service_name = '\"org.iotivity.gatt.service\"'
+
+dbus_policy_in = 'org.iotivity.gatt.service.conf.in'
+
+conf_dict = {}
+subst_env = env.Clone(tools = [ 'default', 'textfile' ],
+ SUBST_DICT = conf_dict)
+
+conf_dict = { '@service_name@' : service_name }
+
+subst_env.Substfile(dbus_policy_in, SUBST_DICT = conf_dict)
+
+# The resulting D-Bus policy file should go in to the appropriate
+# D-Bus configuration directory, such as /etc/dbus-1/system.d/.
+
+dbus_policy = os.path.splitext(dbus_policy_in)[0] # Drop '.in' extension.
+generated_dbus_policy = os.path.join(this_build_dir, dbus_policy)
+env.Clean(dbus_policy, generated_dbus_policy)
+
+# Define the D-Bus bus name as a preprocessor symbol. Note the
+# multiple quote levels to ensure that the double quotes surrounding
+# the string are included as part of the preprocess symbol.
+#
+# Also add a minimum required version of GLib 2.32, which is what the
+# older GNU/Linux distributions supported by IoTivity shipped with.
+env.AppendUnique(
+ CPPDEFINES = [
+ ('CA_DBUS_GATT_SERVICE_NAME', "'%s'" % service_name),
+ ('GLIB_VERSION_MIN_REQUIRED', 'GLIB_VERSION_2_32')
+ ])
+
+src_files = [ 'characteristic.c',
+ 'descriptor.c',
+ 'service.c',
+ 'advertisement.c',
+ 'utils.c',
+ 'central.c',
+ 'peripheral.c',
+ 'client.c',
+ 'server.c',
+ 'recv.c',
+ 'caleinterface.c'
+ ]
+src_files = glue_files + src_files
Return('src_files')
+
+
+# Local Variables:
+# mode:python
+# indent-tabs-mode: nil
+# End:
--- /dev/null
+/* ****************************************************************
+ *
+ * Copyright 2015 Intel Corporation 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.
+ *
+ ******************************************************************/
+
+#include "advertisement.h"
+#include "gatt_dbus.h"
+
+#include "cagattservice.h"
+#include "logger.h"
+
+#include <assert.h>
+
+
+// Logging tag.
+static char const TAG[] = "BLE_ADVERTISEMENT";
+
+bool CALEAdvertisementInitialize(CALEAdvertisement * a,
+ GDBusConnection * connection,
+ GList * managers)
+{
+ assert(a != NULL);
+ assert(connection != NULL);
+
+ /*
+ D-Bus object path for the LEAdvertisement1 object of the form
+ /org/iotivity/gatt/advertisement0.
+
+ The same object path is registered with all
+ org.bluez.LEAdvertisingManager1 objects since the advertisement
+ data is same for all Bluetooth hardware adapters.
+ */
+ static char const object_path[] =
+ CA_GATT_SERVICE_ROOT_PATH "/" CA_LE_ADVERTISEMENT_PATH;
+
+ assert(g_variant_is_object_path(object_path));
+
+ a->advertisement = leadvertisement1_skeleton_new();
+
+ /*
+ Setting the BlueZ advertisement type to "peripheral" causes the
+ Bluetooth adapter to go into LE connectable and general
+ discoverable modes upon successful registration of the
+ advertisement with BlueZ.
+ */
+ leadvertisement1_set_type_(a->advertisement, "peripheral");
+
+ static char const * service_uuids[] = {
+ CA_GATT_SERVICE_UUID, // Advertise OIC Transport Profile
+ NULL
+ };
+
+ leadvertisement1_set_service_uuids(a->advertisement, service_uuids);
+ leadvertisement1_set_manufacturer_data(a->advertisement, NULL);
+ leadvertisement1_set_solicit_uuids(a->advertisement, NULL);
+ leadvertisement1_set_service_data(a->advertisement, NULL);
+ leadvertisement1_set_include_tx_power(a->advertisement, FALSE);
+
+ a->managers = managers;
+
+ // Export the LEAdvertisement1 interface skeleton.
+ GError * error = NULL;
+ if (!g_dbus_interface_skeleton_export(
+ G_DBUS_INTERFACE_SKELETON(a->advertisement),
+ connection,
+ object_path,
+ &error))
+ {
+ OIC_LOG_V(ERROR,
+ TAG,
+ "Unable to export LE advertisement interface: %s\n",
+ error->message);
+
+ return false;
+ }
+
+ return true;
+}
+
+void CALEAdvertisementDestroy(CALEAdvertisement * a)
+{
+ if (a->advertisement != NULL)
+ {
+ char const * const advertisement_path =
+ g_dbus_interface_skeleton_get_object_path(
+ G_DBUS_INTERFACE_SKELETON(
+ a->advertisement));
+
+ if (advertisement_path != NULL)
+ {
+ for (GList * l = a->managers; l != NULL; l = l->next)
+ {
+ GDBusProxy * const manager = G_DBUS_PROXY(l->data);
+
+ GVariant * const parameters =
+ g_variant_new("(o)", advertisement_path, NULL);
+
+ /*
+ Unregister our LE advertisement from the BlueZ LE
+ advertising manager.
+ */
+ g_dbus_proxy_call(
+ manager,
+ "UnregisterAdvertisement",
+ parameters,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, // timeout (default == -1),
+ NULL, // cancellable
+ NULL, // callback
+ NULL);
+ }
+ }
+
+ g_clear_object(&a->advertisement);
+ }
+
+ g_list_free_full(a->managers, g_object_unref);
+ a->managers = NULL;
+}
--- /dev/null
+/* ****************************************************************
+ *
+ * Copyright 2015 Intel Corporation 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.
+ *
+ ******************************************************************/
+
+#ifndef CA_BLE_LINUX_ADVERTISEMENT_H
+#define CA_BLE_LINUX_ADVERTISEMENT_H
+
+#include "bluez-glue.h"
+
+#include <stdbool.h>
+
+
+/**
+ * Information needed for registering an LE advertisement with BlueZ.
+ */
+typedef struct CALEAdvertisement
+{
+ /// OIC LE advertisement D-Bus interface skeleton object.
+ LEAdvertisement1 * advertisement;
+
+ /**
+ * Proxies to the BlueZ D-Bus objects that implement the
+ * "org.bluez.LEAdvertisingManager1" interface with which the @c
+ * advertisement is registered.
+ */
+ GList * managers;
+
+} CALEAdvertisement;
+
+/**
+ * Initialize LE advertisement fields.
+ *
+ * This function initializes the @c CALEAdvertisement object fields.
+ *
+ * @param[out] a LE advertisement information to be
+ * initialized.
+ * @param[in] connection D-Bus connection to the bus on which the
+ * advertisement will be exported.
+ * @param[in] managers List of @c org.bluez.LEAdvertisingManager1
+ * proxies.
+ *
+ * @return @c true on success, @c false otherwise.
+ *
+ * @note This function does not allocate the @a adv object itself.
+ * The caller is responsible for allocating that memory.
+ */
+bool CALEAdvertisementInitialize(CALEAdvertisement * a,
+ GDBusConnection * connection,
+ GList * managers);
+
+/**
+ * Destroy LE advertisement fields.
+ *
+ * This function finalizes the @c CALEAdvertisement object fields.
+ *
+ * @param[in] adv LE advertisement information to be finalized.
+ *
+ * @note This function does not deallocate the @a adv object itself.
+ * The caller is responsible for deallocating that memory.
+ */
+void CALEAdvertisementDestroy(CALEAdvertisement * adv);
+
+
+#endif // CA_BLE_LINUX_ADVERTISEMENT_H
+
--- /dev/null
+/******************************************************************
+ *
+ * Copyright 2015 Intel Corporation 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.
+ *
+ ******************************************************************/
+
+#ifndef CA_BLE_LINUX_BLUEZ_H
+#define CA_BLE_LINUX_BLUEZ_H
+
+
+/// BlueZ D-Bus service name.
+#define BLUEZ_NAME "org.bluez"
+
+/// BlueZ D-Bus adapter interface name.
+static char const BLUEZ_ADAPTER_INTERFACE[] = BLUEZ_NAME ".Adapter1";
+
+/// BlueZ D-Bus device interface name.
+static char const BLUEZ_DEVICE_INTERFACE[] = BLUEZ_NAME ".Device1";
+
+/// BlueZ D-Bus LE advertising manager interface.
+static char const BLUEZ_ADVERTISING_MANAGER_INTERFACE[] =
+ BLUEZ_NAME ".LEAdvertisingManager1";
+
+/// BlueZ D-Bus GATT manager interface.
+static char const BLUEZ_GATT_MANAGER_INTERFACE[] =
+ BLUEZ_NAME ".GattManager1";
+
+/// BlueZ D-Bus adapter GATT service interface name.
+static char const BLUEZ_GATT_SERVICE_INTERFACE[] =
+ BLUEZ_NAME ".GattService1";
+
+/// BlueZ D-Bus adapter GATT characteristic interface name.
+static char const BLUEZ_GATT_CHARACTERISTIC_INTERFACE[] =
+ BLUEZ_NAME ".GattCharacteristic1";
+
+/// BlueZ D-Bus adapter GATT service interface name.
+static char const BLUEZ_GATT_DESCRIPTOR_INTERFACE[] =
+ BLUEZ_NAME ".GattDescriptor1";
+
+
+#endif // CA_BLE_LINUX_BLUEZ_H
--- /dev/null
+<!--
+ Copyright 2015 Intel Corporation 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.
+
+ ******************************************************************
+
+ The introspection XML found in this file is used to generate GDBus
+ skeleton code that will be used by the IoTivity BlueZ-based GATT
+ Service (OIC Transport Profile) implementation.
+
+ See the GATT and LE Advertisement API documentation in the BlueZ
+ gatt-api.txt and advertisement-api.txt documents, respectively for
+ further details.
+-->
+
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node name="/org/iotivity/gatt/service">
+
+ <!--
+ ***********************************************
+ BlueZ GATT Service interface introspection XML.
+ ***********************************************
+ -->
+ <interface name="org.bluez.GattService1">
+ <annotation name="org.freedesktop.DBus.Properties.PropertiesChanged" value="const"/>
+
+ <property name="UUID" type="s" access="read"/>
+ <property name="Primary" type="b" access="read"/>
+ <property name="Characteristics" type="ao" access="read"/>
+
+ <!--
+ ===========================================================
+ The "Device" property is only exposed on the client side by
+ BlueZ itself. It isn't set by the IoTivity server side.
+ Don't bother generating skeleton code for it.
+ ===========================================================
+ <property name="Device" type="o" access="read">
+ <annotation name="org.freedesktop.DBus.Properties.PropertiesChanged" value="true"/>
+ </property>
+
+ ===========================================================
+ The "Includes" property is not supported as of BlueZ 5.30.
+ It also isn't used by the IoTivity server side
+ implementation. Don't bother generating skeleton code for
+ it.
+ ===========================================================
+ <property name="Includes" type="ao" access="read"/>
+ -->
+
+ </interface>
+
+ <!--
+ ******************************************************
+ BlueZ GATT Characteristic interface introspection XML.
+ ******************************************************
+ -->
+ <interface name="org.bluez.GattCharacteristic1">
+ <!--
+ ============================================================
+ None of the OIC GATT characteristics support the "ReadValue"
+ method. Don't bother generating skeleton code for it.
+ ============================================================
+ <method name="ReadValue">
+ <arg name="value" type="ay" direction="out"/>
+ <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+ </method>
+ -->
+
+ <method name="WriteValue">
+ <arg name="value" type="ay" direction="in">
+ <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+ </arg>
+ </method>
+
+ <method name="StartNotify"/>
+
+ <method name="StopNotify"/>
+
+ <annotation name="org.freedesktop.DBus.Properties.PropertiesChanged" value="const"/>
+
+ <property name="UUID" type="s" access="read"/>
+ <property name="Service" type="o" access="read"/>
+
+ <property name="Value" type="ay" access="read">
+ <annotation name="org.freedesktop.DBus.Properties.PropertiesChanged" value="true"/>
+ <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+ </property>
+
+ <property name="Notifying" type="b" access="read">
+ <annotation name="org.freedesktop.DBus.Properties.PropertiesChanged" value="true"/>
+ </property>
+ <property name="Flags" type="as" access="read"/>
+ <property name="Descriptors" type="ao" access="read"/>
+ </interface>
+
+ <!--
+ **************************************************
+ BlueZ GATT Descriptor interface introspection XML.
+ **************************************************
+ -->
+ <interface name="org.bluez.GattDescriptor1">
+ <method name="ReadValue">
+ <arg name="value" type="ay" direction="out">
+ <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+ </arg>
+ </method>
+
+ <!--
+ ============================================================
+ None of the OIC GATT descriptors directly supported by
+ IoTivity support the "WriteValue" method. The OIC Client
+ Characteristic Configuration Descriptor supports writes, but
+ that descriptor is handled by BlueZ, not IoTivity, when the
+ "notify" property is set on a given GATT characteristic.
+ Don't bother generating skeleton code for it.
+ ============================================================
+
+ <method name="WriteValue">
+ <arg name="value" type="ay" direction="in">
+ <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+ </arg>
+ </method>
+ -->
+
+ <annotation name="org.freedesktop.DBus.Properties.PropertiesChanged" value="const"/>
+
+ <property name="UUID" type="s" access="read"/>
+ <property name="Characteristic" type="o" access="read"/>
+
+ <property name="Value" type="ay" access="read">
+ <annotation name="org.freedesktop.DBus.Properties.PropertiesChanged" value="true"/>
+ <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+ </property>
+
+ <property name="Flags" type="as" access="read"/>
+ </interface>
+
+ <!--
+ ***************************************************
+ BlueZ LE Advertisement interface introspection XML.
+ ***************************************************
+ -->
+ <interface name="org.bluez.LEAdvertisement1">
+ <method name="Release">
+ <annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
+ </method>
+
+ <annotation name="org.freedesktop.DBus.Properties.PropertiesChanged" value="const"/>
+
+ <property name="Type" type="s" access="read"/>
+ <property name="ServiceUUIDs" type="as" access="read"/>
+ <property name="ManufacturerData" type="a{sv}" access="read"/>
+ <property name="SolicitUUIDs" type="as" access="read"/>
+ <property name="ServiceData" type="a{sv}" access="read"/>
+ <property name="IncludeTxPower" type="b" access="read"/>
+ </interface>
+
+</node>
+++ /dev/null
-/******************************************************************
- *
- * Copyright 2014 Samsung Electronics 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.
- *
- ******************************************************************/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "caleadapter.h"
-#include "logger.h"
-
-#define TAG PCF("CA")
-
-static CANetworkPacketReceivedCallback g_leReceivedCallback = NULL;
-static ca_thread_pool_t g_threadPoolHandle = NULL;
-
-CAResult_t CAInitializeLE(CARegisterConnectivityCallback registerCallback,
- CANetworkPacketReceivedCallback reqRespCallback,
- CANetworkChangeCallback netCallback,
- CAErrorHandleCallback errorCallback, ca_thread_pool_t handle)
-{
- OIC_LOG(DEBUG, TAG, "CAInitializeLE");
-
- g_leReceivedCallback = reqRespCallback;
- g_threadPoolHandle = handle;
-
- // register handlers
- CAConnectivityHandler_t handler = {};
-
- handler.startAdapter = CAStartLE;
- handler.startListenServer = CAStartLEListeningServer;
- handler.startDiscoveryServer = CAStartLEDiscoveryServer;
- handler.sendData = CASendLEUnicastData;
- handler.sendDataToAll = CASendLEMulticastData;
- handler.GetnetInfo = CAGetLEInterfaceInformation;
- handler.readData = CAReadLEData;
- handler.stopAdapter = CAStopLE;
- handler.terminate = CATerminateLE;
-
- registerCallback(handler, CA_ADAPTER_GATT_BTLE);
-
- return CA_STATUS_OK;
-}
-
-CAResult_t CAStartLE()
-{
- OIC_LOG(DEBUG, TAG, "CAStartLE");
-
- return CA_STATUS_OK;
-}
-
-CAResult_t CAStartLEListeningServer()
-{
- OIC_LOG(DEBUG, TAG, "CAStartLEListeningServer");
-
- return CA_STATUS_OK;
-}
-
-CAResult_t CAStartLEDiscoveryServer()
-{
- OIC_LOG(DEBUG, TAG, "CAStartLEDiscoveryServer");
-
- return CA_STATUS_OK;
-}
-
-int32_t CASendLEUnicastData(const CAEndpoint_t *endpoint, const void *data, uint32_t dataLen)
-{
- OIC_LOG(DEBUG, TAG, "CASendLEUnicastData");
-
- return -1;
-}
-
-int32_t CASendLEMulticastData(const CAEndpoint_t *endpoint, const void *data, uint32_t dataLen)
-{
- OIC_LOG(DEBUG, TAG, "CASendLEMulticastData");
-
- return -1;
-}
-
-CAResult_t CAGetLEInterfaceInformation(CAEndpoint_t **info, uint32_t *size)
-{
- OIC_LOG(DEBUG, TAG, "CAGetLEInterfaceInformation");
-
- return CA_STATUS_OK;
-}
-
-CAResult_t CAReadLEData()
-{
- OIC_LOG(DEBUG, TAG, "Read LE Data");
-
- return CA_STATUS_OK;
-}
-
-CAResult_t CAStopLE()
-{
- OIC_LOG(DEBUG, TAG, "CAStopLE");
-
- return CA_STATUS_OK;
-}
-
-void CATerminateLE()
-{
- OIC_LOG(DEBUG, TAG, "TerminatLE");
-}
-
--- /dev/null
+/******************************************************************
+ *
+ * Copyright 2015 Intel Corporation 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.
+ *
+ ******************************************************************/
+
+#include "caleinterface.h"
+#include "bluez.h"
+#include "central.h"
+#include "peripheral.h"
+#include "client.h"
+#include "server.h"
+#include "utils.h"
+
+#include "cagattservice.h"
+#include "oic_malloc.h"
+#include "oic_string.h"
+#include "logger.h"
+
+#include <string.h>
+#include <strings.h> // For strcasecmp().
+#include <assert.h>
+
+
+#define MICROSECS_PER_SEC 1000000
+
+// Logging tag.
+static char const TAG[] = "BLE_INTERFACE";
+
+/*
+ The IoTivity adapter interface currently doesn't provide a means to
+ pass context down to the transport layer so rely on a file scope
+ context instead.
+*/
+static CALEContext g_context = {
+ .lock = NULL
+};
+
+// -----------------------------------------------------------------------
+// Functions internal to this BLE adapter implementation.
+// -----------------------------------------------------------------------
+static bool CALESetUpBlueZObjects(CALEContext * context);
+
+static bool CALECheckStarted()
+{
+ ca_mutex_lock(g_context.lock);
+
+ bool const started = (g_context.event_loop != NULL);
+
+ ca_mutex_unlock(g_context.lock);
+
+ /**
+ * @todo Fix potential TOCTOU race condition. A LE transport
+ * adapter could have been started or stopped between the
+ * mutex unlock and boolean check.
+ */
+ return started;
+}
+
+static void CALEDumpDBusSignalParameters(char const * sender_name,
+ char const * object_path,
+ char const * interface_name,
+ char const * signal_name,
+ GVariant * parameters)
+{
+#ifdef TB_LOG
+ gchar * const param_dump =
+ g_variant_print(parameters, TRUE);
+
+ OIC_LOG_V(DEBUG,
+ TAG,
+ "%s()\n"
+ "\tsender_name: %s\n"
+ "\tobject_path: %s\n"
+ "\tinterface_name: %s\n"
+ "\tsignal_name: %s\n"
+ "\tparameters: %s\n",
+ __func__,
+ sender_name,
+ object_path,
+ interface_name,
+ signal_name,
+ param_dump);
+
+ g_free(param_dump);
+#endif // TB_LOG
+}
+
+static void CALEOnInterfaceProxyPropertiesChanged(
+ GDBusObjectManagerClient * manager,
+ GDBusObjectProxy * object_proxy,
+ GDBusProxy * interface_proxy,
+ GVariant * changed_properties,
+ gchar const * const * invalidated_properties,
+ gpointer user_data)
+{
+ OIC_LOG_V(DEBUG,
+ TAG,
+ "Properties Changed on %s:\n",
+ g_dbus_object_get_object_path(
+ G_DBUS_OBJECT(object_proxy)));
+
+ char const * const interface_name =
+ g_dbus_proxy_get_interface_name(interface_proxy);
+
+ bool const is_adapter_interface =
+ (strcmp(BLUEZ_ADAPTER_INTERFACE, interface_name) == 0);
+
+ if (!is_adapter_interface)
+ {
+ /*
+ Only specific org.bluez.Adapter1 property changes are
+ currently supported.
+ */
+ return;
+ }
+
+ CALEContext * const context = user_data;
+
+ GVariantIter iter;
+ gchar const * key = NULL;
+ GVariant * value = NULL;
+
+ g_variant_iter_init(&iter, changed_properties);
+ while (g_variant_iter_next(&iter, "{&sv}", &key, &value))
+ {
+ if (strcmp(key, "Powered") == 0)
+ {
+ /*
+ Report a change in the availability of the bluetooth
+ adapter.
+ */
+
+ gboolean const powered = g_variant_get_boolean(value);
+ CAAdapterState_t const status =
+ (powered ? CA_ADAPTER_ENABLED : CA_ADAPTER_DISABLED);
+
+ CAEndpoint_t info =
+ {
+ .adapter = CA_ADAPTER_GATT_BTLE,
+ };
+
+ GVariant * const prop =
+ g_dbus_proxy_get_cached_property(interface_proxy,
+ "Address");
+
+ gchar const * const address = g_variant_get_string(prop, NULL);
+
+ OICStrcpy(info.addr, sizeof(info.addr), address);
+
+ g_variant_unref(prop);
+
+ /**
+ * @todo Should we acquire the context lock here to
+ * prevent the @c CALEDeviceStateChangedCallback
+ * from being potentially yanked out from under us
+ * if the CA adapters are stopped/terminated as
+ * we're about to invoke this callback?
+ *
+ * @todo Unfortunately the CA LE interface defined in
+ * caleinterface.h assumes that only one BLE adapter
+ * will exist on a given host. However, this
+ * implementation can handle multiple BLE adapters.
+ * The CA LE interface should be updated so that it
+ * can handle multiple BLE adapters.
+ */
+ context->on_device_state_changed(status);
+ }
+
+#ifdef TB_LOG
+ gchar * const s = g_variant_print(value, TRUE);
+ OIC_LOG_V(DEBUG, TAG, " %s -> %s", key, s);
+ g_free(s);
+#endif // TB_LOG
+
+ g_variant_unref(value);
+ }
+}
+
+static void CALEHandleInterfaceAdded(GList ** proxy_list,
+ char const * interface,
+ GVariant * parameters)
+{
+ /**
+ * @note The @a parameters are of the form "(oa{sv})".
+ */
+ GDBusProxy * const proxy =
+ CAGetBlueZInterfaceProxy(parameters,
+ interface,
+ g_context.object_manager);
+
+ if (proxy == NULL)
+ {
+ return;
+ }
+
+ ca_mutex_lock(g_context.lock);
+
+ /*
+ Add the object information to the list.
+
+ Note that we prepend instead of append in this case since it
+ is more efficient to do so for linked lists like the one used
+ here.
+ */
+ *proxy_list = g_list_prepend(*proxy_list, proxy);
+
+ ca_mutex_unlock(g_context.lock);
+
+ /**
+ * Let the thread that may be blocked waiting for Devices to be
+ * discovered know that at least one was found.
+ *
+ * @todo It doesn't feel good putting this @c org.bluez.Device1
+ * specific code here since this function is meant to be
+ * BlueZ interface neutral. Look into ways of moving this
+ * out of here.
+ */
+ if (strcmp(interface, BLUEZ_DEVICE_INTERFACE) == 0)
+ {
+ ca_cond_signal(g_context.condition);
+ }
+}
+
+static void CALEOnInterfacesAdded(GDBusConnection * connection,
+ char const * sender_name,
+ char const * object_path,
+ char const * interface_name,
+ char const * signal_name,
+ GVariant * parameters,
+ gpointer user_data)
+{
+ CALEDumpDBusSignalParameters(sender_name,
+ object_path,
+ interface_name,
+ signal_name,
+ parameters);
+
+ // The signal should always be InterfacesAdded.
+ assert(strcmp(signal_name, "InterfacesAdded") == 0);
+
+ // Handle addition of a new org.bluez.Adapter1 interface.
+ CALEHandleInterfaceAdded(&g_context.adapters,
+ BLUEZ_ADAPTER_INTERFACE,
+ parameters);
+
+ // Handle addition of a new org.bluez.Device1 interface.
+ CALEHandleInterfaceAdded(&g_context.devices,
+ BLUEZ_DEVICE_INTERFACE,
+ parameters);
+}
+
+static void CALEOnInterfacesRemoved(GDBusConnection * connection,
+ char const * sender_name,
+ char const * object_path,
+ char const * interface_name,
+ char const * signal_name,
+ GVariant * parameters,
+ gpointer user_data)
+{
+ CALEDumpDBusSignalParameters(sender_name,
+ object_path,
+ interface_name,
+ signal_name,
+ parameters);
+
+ // The signal should always be InterfacesRemoved.
+ assert(strcmp(signal_name, "InterfacesRemoved") == 0);
+
+ /*
+ The object path is first tuple element, and the interface names
+ the second. Check if "org.bluez.Adapter1" exists in the
+ interface array. If it does, remove the corresponding
+ information from the adapter_infos list.
+ */
+ GVariant * const interfaces =
+ g_variant_get_child_value(parameters, 1);
+
+ GVariantIter * iter = NULL;
+ g_variant_get(interfaces, "as", &iter);
+
+ /**
+ * Iterate over the array and remove all BlueZ interface proxies
+ * with a matching D-Bus object path from the corresponding list.
+ *
+ * @todo Determine whether we should optimize this nested loop.
+ * It may not be worthwhile to do so since the lists being
+ * iterated over should be very short.
+ */
+ for (GVariant * child = g_variant_iter_next_value(iter);
+ child != NULL;
+ child = g_variant_iter_next_value(iter))
+ {
+ char const * interface = NULL;
+ g_variant_get(child, "&s", &interface);
+
+ GList ** list = NULL;
+
+ if (strcmp(interface, BLUEZ_ADAPTER_INTERFACE) == 0)
+ {
+ list = &g_context.adapters;
+ }
+ else if (strcmp(interface, BLUEZ_DEVICE_INTERFACE) == 0)
+ {
+ list = &g_context.devices;
+ }
+ else
+ {
+ continue;
+ }
+
+ // The object path is the first tuple element.
+ gchar const * path = NULL;
+ g_variant_get_child(parameters, 0, "&o", &path);
+
+ ca_mutex_lock(g_context.lock);
+
+ for (GList * l = *list; l != NULL; l = g_list_next(l))
+ {
+ GDBusProxy * const proxy = G_DBUS_PROXY(l->data);
+
+ if (strcmp(path,
+ g_dbus_proxy_get_object_path(proxy)) == 0)
+ {
+ // Found a match!
+ g_object_unref(proxy);
+
+ *list = g_list_delete_link(*list, l);
+
+ break;
+ }
+ }
+
+ ca_mutex_unlock(g_context.lock);
+
+ g_variant_unref(child);
+ }
+
+ if (iter != NULL)
+ g_variant_iter_free(iter);
+}
+
+static void CALEOnPropertiesChanged(GDBusConnection * connection,
+ char const * sender_name,
+ char const * object_path,
+ char const * interface_name,
+ char const * signal_name,
+ GVariant * parameters,
+ gpointer user_data)
+{
+ CALEDumpDBusSignalParameters(sender_name,
+ object_path,
+ interface_name,
+ signal_name,
+ parameters);
+}
+
+static void CALEOnPropertyChanged(GDBusConnection * connection,
+ char const * sender_name,
+ char const * object_path,
+ char const * interface_name,
+ char const * signal_name,
+ GVariant * parameters,
+ gpointer user_data)
+{
+ CALEDumpDBusSignalParameters(sender_name,
+ object_path,
+ interface_name,
+ signal_name,
+ parameters);
+}
+
+static void CALESubscribeToSignals(CALEContext * context,
+ GDBusConnection * connection,
+ GDBusObjectManager * object_manager)
+{
+ static char const om_interface[] =
+ "org.freedesktop.DBus.ObjectManager";
+
+ /*
+ Subscribe to D-Bus signals that will allow us to detect changes
+ in BlueZ adapter and device properties.
+ */
+ guint const interfaces_added_sub_id =
+ g_dbus_connection_signal_subscribe(
+ connection,
+ NULL, // sender
+ om_interface,
+ "InterfacesAdded",
+ NULL, // object path
+ NULL, // arg0
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ CALEOnInterfacesAdded,
+ NULL, // user_data
+ NULL);
+
+ guint const interfaces_removed_sub_id =
+ g_dbus_connection_signal_subscribe(
+ connection,
+ NULL, // sender
+ om_interface,
+ "InterfacesRemoved",
+ NULL, // object path
+ NULL, // arg0
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ CALEOnInterfacesRemoved,
+ NULL, // user_data
+ NULL);
+
+#if GLIB_CHECK_VERSION(2,38,0)
+ /*
+ The G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH flag was introduced in
+ GLib 2.38.
+ */
+ static GDBusSignalFlags const device_signal_flags =
+ G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH;
+#else
+ static GDBusSignalFlags const device_signal_flags =
+ G_DBUS_SIGNAL_FLAGS_NONE;
+#endif
+
+ /**
+ * @todo Verify that this signal subscription is needed.
+ *
+ * @bug The arg0 argument below should be a D-Bus object path, not
+ * interface name.
+ */
+ guint const properties_changed_sub_id =
+ g_dbus_connection_signal_subscribe(
+ connection,
+ NULL, // sender
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ NULL, // object path
+ "org.bluez.Device1", // arg0
+ device_signal_flags,
+ CALEOnPropertiesChanged,
+ NULL, // user_data
+ NULL);
+
+ /**
+ * @todo Verify that this signal subscription is needed.
+ */
+ guint const property_changed_sub_id =
+ g_dbus_connection_signal_subscribe(connection,
+ NULL, // sender
+ BLUEZ_ADAPTER_INTERFACE,
+ "PropertyChanged",
+ NULL, // object path
+ NULL, // arg0
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ CALEOnPropertyChanged,
+ NULL, // user_data
+ NULL);
+
+ g_signal_connect(object_manager,
+ "interface-proxy-properties-changed",
+ G_CALLBACK(CALEOnInterfaceProxyPropertiesChanged),
+ context);
+
+ ca_mutex_lock(context->lock);
+
+ context->interfaces_added_sub_id = interfaces_added_sub_id;
+ context->interfaces_removed_sub_id = interfaces_removed_sub_id;
+ context->properties_changed_sub_id = properties_changed_sub_id;
+ context->property_changed_sub_id = property_changed_sub_id;
+
+ ca_mutex_unlock(context->lock);
+}
+
+static bool CALESetUpDBus(CALEContext * context)
+{
+ assert(context != NULL);
+
+ bool success = false;
+
+ GError * error = NULL;
+
+ /*
+ Set up connection to the D-Bus system bus, where the BlueZ
+ daemon is found.
+ */
+ GDBusConnection * const connection =
+ g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
+
+ if (connection == NULL)
+ {
+ OIC_LOG_V(ERROR,
+ TAG,
+ "Connection to D-Bus system bus failed: %s.",
+ error->message);
+
+ g_error_free(error);
+
+ return success;
+ }
+
+ // Create a proxy to the BlueZ D-Bus ObjectManager.
+ static char const object_manager_path[] = "/";
+
+ GDBusObjectManager * const object_manager =
+ g_dbus_object_manager_client_new_sync(
+ connection,
+ G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
+ BLUEZ_NAME,
+ object_manager_path,
+ NULL, // get_proxy_type_func
+ NULL, // get_proxy_type_user_data
+ NULL, // get_proxy_type_destroy_notify
+ NULL, // cancellable
+ &error);
+
+ if (object_manager == NULL)
+ {
+ OIC_LOG_V(ERROR,
+ TAG,
+ "Unable to create D-Bus ObjectManager client: %s",
+ error->message);
+
+ g_error_free(error);
+
+ g_object_unref(connection);
+
+ return success;
+ }
+
+ CALESubscribeToSignals(context, connection, object_manager);
+
+ ca_mutex_lock(context->lock);
+ context->connection = connection;
+ context->object_manager = object_manager;
+ ca_mutex_unlock(context->lock);
+
+ success = CALESetUpBlueZObjects(context);
+
+ return success;
+}
+
+static void CALETearDownDBus(CALEContext * context)
+{
+ assert(context != NULL);
+
+ /*
+ Minimize the time we hold the global lock by only clearing the
+ global state, and pushing resource finalization outside the global
+ lock.
+ */
+ ca_mutex_lock(context->lock);
+
+ GDBusConnection * const connection = context->connection;
+ context->connection = NULL;
+
+ GDBusObjectManager * const object_manager = context->object_manager;
+ context->object_manager = NULL;
+
+ GList * const objects = context->objects;
+ context->objects = NULL;
+
+ GList * const adapters = context->adapters;
+ context->adapters = NULL;
+
+ GList * const devices = context->devices;
+ context->devices = NULL;
+
+ guint const interfaces_added = context->interfaces_added_sub_id;
+ guint const interfaces_removed = context->interfaces_removed_sub_id;
+ guint const properties_changed = context->properties_changed_sub_id;
+ guint const property_changed = context->property_changed_sub_id;
+
+ context->interfaces_added_sub_id = 0;
+ context->interfaces_removed_sub_id = 0;
+ context->properties_changed_sub_id = 0;
+ context->property_changed_sub_id = 0;
+
+ ca_mutex_unlock(context->lock);
+
+ // Destroy the device proxies list.
+ g_list_free_full(devices, g_object_unref);
+
+ // Destroy the adapter proxies list.
+ g_list_free_full(adapters, g_object_unref);
+
+ // Destroy the list of objects obtained from the ObjectManager.
+ g_list_free_full(objects, g_object_unref);
+
+ // Destroy the ObjectManager proxy.
+ if (object_manager != NULL)
+ {
+ g_object_unref(object_manager);
+ }
+
+ // Tear down the D-Bus connection to the system bus.
+ if (connection != NULL)
+ {
+ g_dbus_connection_signal_unsubscribe(connection,
+ interfaces_added);
+ g_dbus_connection_signal_unsubscribe(connection,
+ interfaces_removed);
+ g_dbus_connection_signal_unsubscribe(connection,
+ properties_changed);
+ g_dbus_connection_signal_unsubscribe(connection,
+ property_changed);
+ g_object_unref(connection);
+ }
+}
+
+static bool CALEDeviceFilter(GDBusProxy * device)
+{
+ bool accepted = false;
+
+ /*
+ Filter out any devices that don't support the OIC Transport
+ Profile service.
+ */
+ GVariant * const prop =
+ g_dbus_proxy_get_cached_property(device, "UUIDs");
+
+ if (prop == NULL)
+ {
+ // No remote services available on the device.
+ return accepted;
+ }
+
+ char const ** const UUIDs = g_variant_get_strv(prop, NULL);
+
+ /**
+ * @note It would have been nice to use @c g_strv_contains() here,
+ * but we would need to run it twice: once for the uppercase
+ * form of the UUID and once for for the lowercase form.
+ * Just run the loop manually, and use @c strcasecmp()
+ * instead.
+ */
+ for (char const * const * u = UUIDs; u != NULL; ++u)
+ {
+ if (strcasecmp(*u, CA_GATT_SERVICE_UUID) == 0)
+ {
+ accepted = true;
+ break;
+ }
+ }
+
+ g_free(UUIDs);
+ g_variant_unref(prop);
+
+ return accepted;
+}
+
+
+static bool CALESetUpBlueZObjects(CALEContext * context)
+{
+ bool success = false;
+
+ // Get the list of BlueZ D-Bus objects.
+ GList * const objects =
+ g_dbus_object_manager_get_objects(context->object_manager);
+
+ if (objects == NULL) {
+ OIC_LOG(ERROR,
+ TAG,
+ "Unable to get objects from ObjectManager.");
+
+ return success;
+ }
+
+ ca_mutex_lock(context->lock);
+ context->objects = objects;
+ ca_mutex_unlock(context->lock);
+
+ /*
+ Create a proxies to the org.bluez.Adapter1 D-Bus objects that
+ will later be used to obtain local bluetooth adapter properties,
+ as well as by the BLE central code to discover peripherals.
+ */
+ GList * adapters = NULL;
+ success = CAGetBlueZManagedObjectProxies(&adapters,
+ BLUEZ_ADAPTER_INTERFACE,
+ context,
+ NULL);
+
+ // An empty adapters list is NULL.
+ if (success && adapters != NULL)
+ {
+ ca_mutex_lock(context->lock);
+ context->adapters = adapters;
+ ca_mutex_unlock(context->lock);
+ }
+
+ /*
+ Create a proxies to the org.bluez.Device1 D-Bus objects that
+ will later be used to establish connections.
+ */
+ GList * devices = NULL;
+ success = CAGetBlueZManagedObjectProxies(&devices,
+ BLUEZ_DEVICE_INTERFACE,
+ context,
+ CALEDeviceFilter);
+
+ // An empty device list is NULL.
+ if (success && devices != NULL)
+ {
+ ca_mutex_lock(context->lock);
+ context->devices = devices;
+ ca_mutex_unlock(context->lock);
+ }
+
+ /* success = CAGattClientInitialize(context); */
+
+ return success;
+}
+
+static void CALEStartEventLoop(void * data)
+{
+ CALEContext * const context = data;
+
+ assert(context != NULL);
+
+ // Create the event loop.
+ GMainContext * const loop_context = g_main_context_new();
+ GMainLoop * const event_loop = g_main_loop_new(loop_context, FALSE);
+
+ ca_mutex_lock(context->lock);
+
+ assert(context->event_loop == NULL);
+ context->event_loop = event_loop;
+
+ ca_mutex_unlock(context->lock);
+
+ g_main_context_push_thread_default(loop_context);
+
+ /*
+ We have to do the BlueZ object manager client initialization and
+ signal subscription here so that the corresponding asynchronous
+ signal handling occurs in the same thread as the one running the
+ GLib event loop.
+ */
+ if (!CALESetUpDBus(&g_context))
+ return;
+
+ ca_cond_signal(g_context.condition);
+
+ g_main_loop_run(event_loop);
+}
+
+static void CALEStopEventLoop(CALEContext * context)
+{
+ ca_mutex_lock(context->lock);
+
+ GMainLoop * const event_loop = context->event_loop;
+ context->event_loop = NULL;
+
+ ca_mutex_unlock(context->lock);
+
+ if (event_loop != NULL)
+ {
+ g_main_loop_quit(event_loop);
+
+ GMainContext * const loop_context =
+ g_main_loop_get_context(event_loop);
+
+ if (loop_context != NULL)
+ {
+ g_main_context_wakeup(loop_context);
+ g_main_context_unref(loop_context);
+ }
+
+ g_main_loop_unref(event_loop);
+ }
+}
+
+/**
+ * Wait for @a list to be non-empty.
+ *
+ * @param[in] list List that should not be empty.
+ * @param[in] retries Number of times to retry if the @a timeout is
+ * reached.
+ * @param[in] timeout Timeout in microseconds to wait between retries.
+ */
+static bool CALEWaitForNonEmptyList(GList * const * list,
+ int retries,
+ uint64_t timeout)
+{
+ bool success = false;
+
+ ca_mutex_lock(g_context.lock);
+
+ for (int i = 0; *list == NULL && i < retries; ++i)
+ {
+ if (ca_cond_wait_for(g_context.condition,
+ g_context.lock,
+ timeout) == 0)
+ {
+ /*
+ Condition variable was signaled before the timeout was
+ reached.
+ */
+ success = true;
+ }
+ }
+
+ ca_mutex_unlock(g_context.lock);
+
+ return success;
+}
+
+static CAResult_t CALEStop()
+{
+ CAResult_t result = CA_STATUS_FAILED;
+
+ OIC_LOG(DEBUG, TAG, "Stop Linux BLE adapter.");
+
+ // Only stop if we were previously started.
+ if (!CALECheckStarted())
+ return result;
+
+ // Stop the event loop thread regardless of previous errors.
+ CALEStopEventLoop(&g_context);
+
+ CALETearDownDBus(&g_context);
+
+ return result;
+}
+
+static void CALETerminate()
+{
+ OIC_LOG(DEBUG, TAG, "Terminate BLE adapter.");
+
+ CAPeripheralFinalize();
+
+ ca_mutex_lock(g_context.lock);
+
+ g_context.on_device_state_changed = NULL;
+ g_context.on_server_received_data = NULL;
+ g_context.on_client_received_data = NULL;
+ g_context.client_thread_pool = NULL;
+ g_context.server_thread_pool = NULL;
+ g_context.on_client_error = NULL;
+ g_context.on_server_error = NULL;
+
+ ca_mutex_unlock(g_context.lock);
+
+ ca_cond_free(g_context.condition);
+ ca_mutex_free(g_context.lock);
+}
+
+// -----------------------------------------------------------------------
+
+CAResult_t CAInitializeLEAdapter()
+{
+#if !GLIB_CHECK_VERSION(2,36,0)
+ /*
+ Initialize the GLib type system.
+
+ As of GLib 2.36, it is no longer necessary to explicitly call
+ g_type_init().
+ */
+ g_type_init();
+#endif
+
+ g_context.lock = ca_mutex_new();
+ g_context.condition = ca_cond_new();
+
+ CAPeripheralInitialize();
+
+ return CA_STATUS_OK;
+}
+
+CAResult_t CAStartLEAdapter()
+{
+ /*
+ This function is called by the connectivity abstraction when
+ CASelectNetwork(CA_ADAPTER_GATT_BTLE) is called by the user.
+ */
+
+ OIC_LOG(DEBUG, TAG, __func__);
+
+ CAResult_t result = CA_STATUS_FAILED;
+
+ // Only start if we were previously stopped.
+ if (CALECheckStarted())
+ return result;
+
+ /**
+ * Spawn a thread to run the GLib event loop that will drive D-Bus
+ * signal handling.
+ *
+ * @note Ideally this should be done in the @c CAInitializeLE()
+ * function so that we can detect local bluetooth adapter
+ * changes right away, without having to first start this LE
+ * adapter/transport via @cCASelectNetwork(). However, a
+ * limitation in the CA termination code that destroys the
+ * thread pool before the transport adapters prevents us
+ * from doing that without potentially triggering a
+ * @c pthread_join() call that blocks indefinitely due to
+ * this event loop not be stopped. See the comments in the
+ * @c CAGetLEInterfaceInformation() function below for
+ * further details.
+ */
+ result = ca_thread_pool_add_task(g_context.client_thread_pool,
+ CALEStartEventLoop,
+ &g_context);
+
+ if (result != CA_STATUS_OK)
+ return result;
+
+ /*
+ Wait until initialization completes before continuing, basically
+ until some Bluetooth adapters were found.
+ */
+
+ // Number of times to wait for initialization to complete.
+ static int const retries = 2;
+
+ static uint64_t const timeout =
+ 2 * MICROSECS_PER_SEC; // Microseconds
+
+ if (CALEWaitForNonEmptyList(&g_context.adapters, retries, timeout))
+ {
+ result = CA_STATUS_OK;
+ }
+
+ return result;
+}
+
+CAResult_t CAGetLEAdapterState()
+{
+ /**
+ * @todo To be implemented shortly as part of the effort to
+ * address a critical code review that stated this BLE
+ * transport should implement the interface defined in
+ * caleinterface.h.
+ */
+ return CA_NOT_SUPPORTED;
+}
+
+CAResult_t CAInitializeLENetworkMonitor()
+{
+ /**
+ * @todo To be implemented shortly as part of the effort to
+ * address a critical code review that stated this BLE
+ * transport should implement the interface defined in
+ * caleinterface.h.
+ */
+ return CA_STATUS_OK;
+}
+
+void CATerminateLENetworkMonitor()
+{
+ /**
+ * @todo To be implemented shortly as part of the effort to
+ * address a critical code review that stated this BLE
+ * transport should implement the interface defined in
+ * caleinterface.h.
+ */
+}
+
+CAResult_t CASetLEAdapterStateChangedCb(CALEDeviceStateChangedCallback callback)
+{
+ ca_mutex_lock(g_context.lock);
+ g_context.on_device_state_changed = callback;
+ ca_mutex_unlock(g_context.lock);
+
+ return CA_STATUS_OK;
+}
+
+CAResult_t CAInitLENetworkMonitorMutexVariables()
+{
+ /*
+ This CA LE interface implementation doesn't use a network
+ monitor as the other platform implementationd do.
+ */
+ return CA_STATUS_OK;
+}
+
+void CATerminateLENetworkMonitorMutexVariables()
+{
+ /*
+ This CA LE interface implementation doesn't use a network
+ monitor as the other platform implementationd do.
+ */
+}
+
+CAResult_t CAGetLEAddress(char **local_address)
+{
+ OIC_LOG(DEBUG, TAG, "Get Linux BLE local device information.");
+
+ if (local_address == NULL)
+ {
+ return CA_STATUS_INVALID_PARAM;
+ }
+
+ /**
+ * @bug Attempting to get LE interface information before this
+ * connectivity abstraction adapter has started (e.g. via
+ * @c CASelectNetwork()) could result in an inaccurate list
+ * of LE interfaces. For example, detection of hot-plugged
+ * bluetooth adapters will only work after the LE IoTivity
+ * network has been selected. If the LE IoTivity network
+ * hasn't been selected, the hot-plugged bluetooth adapter
+ * will not be reflected in the @c CALocalConnectivity_t list
+ * returned by this function.
+ * @par
+ * This issue caused by the fact that the event loop that
+ * handles such events can only be run after this LE
+ * adapter/transport is started. The event loop cannot be
+ * started earlier, e.g. during @c CAInitialize(), due to a
+ * limitation in the CA transport termination code that
+ * requires thread pools to be destroyed before transport
+ * termination. If the event loop was added as a task to the
+ * thread pool during @c CAInitialize(), it's possible that
+ * the thread running that event loop could be blocked
+ * waiting for events during a later call to
+ * @c CATerminate() if @c CASelectNetwork() was not called
+ * beforehand. The thread pool destruction that occurs
+ * during @c CATerminate() waits for threads in the pool to
+ * exit. However, in this case the event loop thread is
+ * still blocked waiting for events since it may not have
+ * been stopped since the transport itself was not stopped,
+ * meaning termination will also be blocked.
+ * @par
+ * Other than refactoring to the termination code to allow
+ * thread pools to be started during @c CAInitialize(), the
+ * only other choice we have to prevent the hang at
+ * termination is to move the event loop creation and
+ * termination to the @c CAAdapterStart() and
+ * @c CAAdapterStop() implementations, respectively, which is
+ * why we have this bug.
+ */
+ if (!CALECheckStarted())
+ return CA_ADAPTER_NOT_ENABLED;
+
+ *local_address = NULL;
+
+ ca_mutex_lock(g_context.lock);
+
+ for (GList * l = g_context.adapters; l != NULL; l = l->next)
+ {
+ GDBusProxy * const adapter = G_DBUS_PROXY(l->data);
+
+ /*
+ The local bluetooth adapter MAC address is stored in the
+ org.bluez.Adapter1.Address property.
+ */
+ GVariant * const prop =
+ g_dbus_proxy_get_cached_property(adapter, "Address");
+
+ /*
+ Unless the org.bluez.Adapter1.Address property no longer
+ exists, prop should not be NULL! We have bigger problems if
+ this assert() is ever tripped since that would mean the
+ org.bluez.Adapter1 D-Bus interface changed.
+ */
+ assert(prop != NULL);
+
+ gchar const * const address = g_variant_get_string(prop, NULL);
+
+ *local_address = OICStrdup(address);
+
+ /*
+ No longer need the property variant. The address has been
+ copied.
+ */
+ g_variant_unref(prop);
+
+ /**
+ * @todo Unfortunately the CA LE interface defined in
+ * caleinterface.h assumes that only one BLE adapter
+ * will exist on a given host. However, this
+ * implementation can handle multiple BLE adapters. The
+ * CA LE interface should be updated so that it can
+ * handle multiple BLE adapters. For now we'll just
+ * return the address for the first BLE adapter in the
+ * list.
+ */
+ break;
+ }
+
+ ca_mutex_unlock(g_context.lock);
+
+ return *local_address != NULL ? CA_STATUS_OK : CA_STATUS_FAILED;
+}
+
+CAResult_t CAStartLEGattServer()
+{
+ return CAPeripheralStart(&g_context);
+}
+
+CAResult_t CAStopLEGattServer()
+{
+ CAResult_t result = CAPeripheralStop();
+ CAResult_t const tmp = CALEStop();
+
+ if (result == CA_STATUS_OK && tmp != CA_STATUS_OK)
+ {
+ result = tmp;
+ }
+
+ return result;
+}
+
+void CATerminateLEGattServer()
+{
+ CALETerminate();
+}
+
+void CASetLEReqRespServerCallback(CABLEDataReceivedCallback callback)
+{
+ ca_mutex_lock(g_context.lock);
+ g_context.on_server_received_data = callback;
+ ca_mutex_unlock(g_context.lock);
+}
+
+CAResult_t CAUpdateCharacteristicsToGattClient(char const * address,
+ char const * charValue,
+ uint32_t charValueLen)
+{
+ /**
+ * @todo To be implemented shortly as part of the effort to
+ * address a critical code review that stated this BLE
+ * transport should implement the interface defined in
+ * caleinterface.h.
+ */
+ return CA_NOT_SUPPORTED;
+}
+
+CAResult_t CAUpdateCharacteristicsToAllGattClients(char const * charValue,
+ uint32_t charValueLen)
+{
+ /**
+ * @todo To be implemented shortly as part of the effort to
+ * address a critical code review that stated this BLE
+ * transport should implement the interface defined in
+ * caleinterface.h.
+ */
+ return CA_NOT_SUPPORTED;
+}
+
+CAResult_t CAStartLEGattClient()
+{
+ return CACentralStart(&g_context);
+}
+
+void CAStopLEGattClient()
+{
+ (void) CACentralStop(&g_context);
+ (void) CALEStop();
+}
+
+void CATerminateLEGattClient()
+{
+ CALETerminate();
+}
+
+void CACheckLEData()
+{
+ /*
+ This function is only used in single-threaded builds, but this
+ CA LE adapter implementation is multi-threaded. Consequently,
+ this function is a no-op.
+ */
+}
+
+CAResult_t CAUpdateCharacteristicsToGattServer(
+ char const * remoteAddress,
+ char const * data,
+ uint32_t dataLen,
+ CALETransferType_t type,
+ int32_t position)
+{
+ /**
+ * @todo To be implemented shortly as part of the effort to
+ * address a critical code review that stated this BLE
+ * transport should implement the interface defined in
+ * caleinterface.h.
+ */
+ return CA_NOT_SUPPORTED;
+}
+
+CAResult_t CAUpdateCharacteristicsToAllGattServers(char const * data,
+ uint32_t length)
+{
+ OIC_LOG(DEBUG, TAG, "Send data to all");
+
+ /*
+ Multicast data is only sent when a request is sent from a client
+ across all endpoints. We need not worry about sending a
+ response from a server here.
+ */
+
+ CAResult_t result = CA_STATUS_FAILED;
+
+ ca_mutex_lock(g_context.lock);
+ bool found_peripherals = (g_context.devices != NULL);
+ ca_mutex_unlock(g_context.lock);
+
+ if (!found_peripherals)
+ {
+ /*
+ Start discovery of LE peripherals that advertise the OIC
+ Transport Profile.
+ */
+ result = CACentralStartDiscovery(&g_context);
+
+ if (result != CA_STATUS_OK)
+ {
+ return -1;
+ }
+
+ // Wait for LE peripherals to be discovered.
+
+ // Number of times to wait for discovery to complete.
+ static int const retries = 5;
+
+ static uint64_t const timeout =
+ 2 * MICROSECS_PER_SEC; // Microseconds
+
+ if (!CALEWaitForNonEmptyList(&g_context.devices,
+ retries,
+ timeout))
+ {
+ return -1;
+ }
+
+ ca_mutex_lock(g_context.lock);
+ found_peripherals = (g_context.devices == NULL);
+ ca_mutex_unlock(g_context.lock);
+
+ if (!found_peripherals)
+ {
+ // No peripherals discovered!
+ return -1;
+ }
+ }
+
+ /*
+ Stop discovery so that we can connect to LE peripherals.
+ Otherwise, the bluetooth subsystem will claim the adapter is
+ busy.
+ */
+
+ result = CACentralStopDiscovery(&g_context);
+
+ if (result != CA_STATUS_OK)
+ {
+ return -1;
+ }
+
+ bool const connected = CACentralConnectToAll(&g_context);
+
+ if (!connected)
+ {
+ return -1;
+ }
+
+ /**
+ * @todo Start notifications on all response characteristics.
+ */
+
+ /*
+ Now send the request through all BLE connections through the
+ corresponding OIC GATT request characterstics.
+ */
+
+ CAGattRequestInfo const info =
+ {
+ .characteristic_info = NULL, // g_context.characteristics
+ .context = &g_context
+ };
+
+ return CAGattClientSendDataToAll(&info, data, length);
+
+ /**
+ * @todo Should we restart discovery after the send?
+ */
+}
+
+void CASetLEReqRespClientCallback(CABLEDataReceivedCallback callback)
+{
+ ca_mutex_lock(g_context.lock);
+ g_context.on_client_received_data = callback;
+ ca_mutex_unlock(g_context.lock);
+}
+
+void CASetLEServerThreadPoolHandle(ca_thread_pool_t handle)
+{
+ ca_mutex_lock(g_context.lock);
+ g_context.server_thread_pool = handle;
+ ca_mutex_unlock(g_context.lock);
+}
+
+void CASetLEClientThreadPoolHandle(ca_thread_pool_t handle)
+{
+ ca_mutex_lock(g_context.lock);
+ g_context.client_thread_pool = handle;
+ ca_mutex_unlock(g_context.lock);
+}
+
+CAResult_t CAUnSetLEAdapterStateChangedCb()
+{
+ ca_mutex_lock(g_context.lock);
+ g_context.on_device_state_changed = NULL;
+ ca_mutex_unlock(g_context.lock);
+
+ return CA_STATUS_OK;
+}
+
+void CASetBLEClientErrorHandleCallback(CABLEErrorHandleCallback callback)
+{
+ ca_mutex_lock(g_context.lock);
+ g_context.on_client_error = callback;
+ ca_mutex_unlock(g_context.lock);
+}
+
+void CASetBLEServerErrorHandleCallback(CABLEErrorHandleCallback callback)
+{
+ ca_mutex_lock(g_context.lock);
+ g_context.on_server_error = callback;
+ ca_mutex_unlock(g_context.lock);
+}
--- /dev/null
+/******************************************************************
+ *
+ * Copyright 2015 Intel Corporation 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.
+ *
+ ******************************************************************/
+
+#include "central.h"
+#include "utils.h"
+#include "bluez.h"
+
+#include "cagattservice.h" // For CA_GATT_SERVICE_UUID.
+#include "logger.h"
+
+#include <stdbool.h>
+#include <assert.h>
+
+
+// Logging tag.
+static char const TAG[] = "BLE_CENTRAL";
+
+static bool CACentralGetBooleanProperty(GDBusProxy * device,
+ char const * property)
+{
+ GVariant * const cached_property =
+ g_dbus_proxy_get_cached_property(device, property);
+
+ if (cached_property == NULL)
+ {
+ return false;
+ }
+
+ bool const value = g_variant_get_boolean(cached_property);
+
+ g_variant_unref(cached_property);
+
+ return value;
+}
+
+static void CACentralStartDiscoveryImpl(gpointer proxy, gpointer user_data)
+{
+ assert(proxy != NULL);
+ assert(user_data != NULL);
+
+ GDBusProxy * const adapter = G_DBUS_PROXY(proxy);
+ CAResult_t * const result = user_data;
+
+ *result = CA_STATUS_FAILED;
+
+ bool const is_discovering =
+ CACentralGetBooleanProperty(adapter, "Discovering");
+
+ if (is_discovering)
+ {
+ // Nothing to do. Avoid invoking a method over D-Bus.
+ *result = CA_STATUS_OK;
+ return;
+ }
+
+
+ // Make sure the adapter is powered on before starting discovery.
+ if (!CASetBlueZObjectProperty(adapter,
+ BLUEZ_ADAPTER_INTERFACE,
+ "Powered",
+ g_variant_new_boolean(TRUE)))
+ {
+ OIC_LOG(ERROR,
+ TAG,
+ "Unable to power on LE central adapter.");
+
+ return;
+ }
+
+ /*
+ Only scan for LE peripherals that advertise the OIC Transport
+ Profile GATT service UUID by setting a discovery filter on the BlueZ
+ org.bluez.Adapter1 object with two parameters:
+
+ (1) "UUIDs": set to an array containing that OIC Transport
+ Profile GATT service UUID
+ (2) "Transport": set to "le"
+
+ See the documentation for the SetDiscoveryFilter() method in the
+ BlueZ `adapter-api.txt' document for more details.
+ */
+ GVariantBuilder builder;
+ g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
+
+ static char const * const UUIDs[] =
+ {
+ CA_GATT_SERVICE_UUID
+ };
+
+ g_variant_builder_add(&builder,
+ "{sv}",
+ "UUIDs",
+ g_variant_new_strv(
+ UUIDs,
+ sizeof(UUIDs) / sizeof(UUIDs[0])));
+
+ g_variant_builder_add(&builder,
+ "{sv}",
+ "Transport",
+ g_variant_new_string("le"));
+
+ GVariant * const filter = g_variant_builder_end(&builder);
+
+ /*
+ SetDiscoveryFilter() expects a dictionary but it must be packed
+ into a tuple for the actual call through the proxy.
+ */
+ GVariant * const filter_parameters =
+ g_variant_new("(@a{sv})", filter);
+
+ GError * error = NULL;
+
+ /*
+ This is a synchronous call, but the actually discovery is
+ performed asynchronously. org.bluez.Device1 objects will
+ reported through the
+ org.freedesktop.DBus.ObjectManager.InterfacesAdded signal as
+ peripherals that match our discovery filter criteria are found.
+ */
+ GVariant * ret =
+ g_dbus_proxy_call_sync(adapter,
+ "SetDiscoveryFilter",
+ filter_parameters,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, // timeout (default == -1),
+ NULL, // cancellable
+ &error);
+
+ if (ret == NULL)
+ {
+ OIC_LOG_V(ERROR,
+ TAG,
+ "SetDiscoveryFilter() call failed: %s",
+ error->message);
+
+ g_error_free(error);
+
+ return;
+ }
+
+ g_variant_unref(ret);
+
+ // Start device discovery.
+ ret = g_dbus_proxy_call_sync(adapter,
+ "StartDiscovery",
+ NULL, // parameters
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, // timeout (default == -1),
+ NULL, // cancellable
+ &error);
+
+ if (ret == NULL)
+ {
+ OIC_LOG_V(ERROR,
+ TAG,
+ "StartDiscovery() call failed: %s",
+ error->message);
+
+ g_error_free(error);
+
+ return;
+ }
+
+ g_variant_unref(ret);
+
+ *result = CA_STATUS_OK;
+}
+
+static void CACentralStopDiscoveryImpl(gpointer proxy, gpointer user_data)
+{
+ assert(proxy != NULL);
+ assert(user_data != NULL);
+
+ GDBusProxy * const adapter = G_DBUS_PROXY(proxy);
+ CAResult_t * const result = user_data;
+
+ bool const is_discovering =
+ CACentralGetBooleanProperty(adapter, "Discovering");
+
+ if (!is_discovering)
+ {
+ // Nothing to do. Avoid invoking a method over D-Bus.
+ *result = CA_STATUS_OK;
+ return;
+ }
+
+ *result = CA_STATUS_FAILED;
+
+
+ GError * error = NULL;
+
+ // Stop discovery sessions.
+ GVariant * const ret =
+ g_dbus_proxy_call_sync(adapter,
+ "StopDiscovery",
+ NULL, // parameters
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, // timeout (default == -1)
+ NULL, // cancellable
+ &error);
+
+ if (ret == NULL)
+ {
+ OIC_LOG_V(ERROR,
+ TAG,
+ "StopDiscovery() call failed: %s",
+ error->message);
+
+ g_error_free(error);
+
+ return;
+ }
+
+ g_variant_unref(ret);
+
+ *result = CA_STATUS_OK;
+}
+
+static void CACentralConnectToDevice(gpointer data, gpointer user_data)
+{
+ assert(data != NULL);
+ assert(user_data != NULL);
+
+ GDBusProxy * const device = G_DBUS_PROXY(data);
+ bool * const connected = user_data;
+
+ if (!CACentralConnect(device))
+ {
+ *connected = false;
+ }
+}
+
+/**
+ * Disconnect from all LE peripherals.
+ *
+ * @param[in] context Context containing BlueZ device information.
+ */
+static void CACentralDisconnect(CALEContext * context)
+{
+ assert(context != NULL);
+
+ ca_mutex_lock(context->lock);
+
+ for (GList * l = context->devices; l != NULL; l = l->next)
+ {
+ GDBusProxy * const device = G_DBUS_PROXY(l->data);
+
+ bool const is_connected =
+ CACentralGetBooleanProperty(device, "Connected");
+
+ if (is_connected)
+ {
+ /*
+ Asynchronously disconnect. We don't care about the
+ result so don't bother passing in a callback.
+ */
+ g_dbus_proxy_call(device,
+ "Disconnect",
+ NULL, // parameters
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, // timeout (default == -1),
+ NULL, // cancellable
+ NULL, // callback
+ NULL); // user data
+ }
+ }
+
+ ca_mutex_unlock(context->lock);
+}
+
+// -----------------------------------------------------------------------
+
+CAResult_t CACentralStart(CALEContext * context)
+{
+ assert(context != NULL);
+
+ CAResult_t result = CA_STATUS_FAILED;
+
+ /*
+ Synchronize access to the adapter information using the base
+ context lock since we don't own the adapters.
+ */
+ ca_mutex_lock(context->lock);
+
+ /**
+ * Start discovery on all detected adapters.
+ *
+ * @todo The current start start_discovery() implementation makes
+ * two synchronous D-Bus calls to each BlueZ @c Adapter1
+ * object in the @a adapters list. We may want to make them
+ * asynchronous to minimize blocking.
+ */
+ g_list_foreach(context->adapters,
+ CACentralStartDiscoveryImpl,
+ &result);
+
+ ca_mutex_unlock(context->lock);
+
+ return result;
+}
+
+CAResult_t CACentralStop(CALEContext * context)
+{
+ assert(context != NULL);
+
+ CAResult_t result = CA_STATUS_FAILED;
+
+ // Stop discovery on all detected adapters.
+ result = CACentralStopDiscovery(context);
+
+ // Disconnect from all adapters.
+ CACentralDisconnect(context);
+
+ /**
+ * @todo Stop notifications on all response characteristics.
+ */
+
+ return result;
+}
+
+CAResult_t CACentralStartDiscovery(CALEContext * context)
+{
+ assert(context != NULL);
+
+ CAResult_t result = CA_STATUS_FAILED;
+
+ /*
+ Synchronize access to the adapter information using the base
+ context lock since we don't own the adapters.
+ */
+ ca_mutex_lock(context->lock);
+
+ // Start discovery on all detected adapters.
+ g_list_foreach(context->adapters,
+ CACentralStartDiscoveryImpl,
+ &result);
+
+ ca_mutex_unlock(context->lock);
+
+ return result;
+}
+
+CAResult_t CACentralStopDiscovery(CALEContext * context)
+{
+ assert(context != NULL);
+
+ CAResult_t result = CA_STATUS_FAILED;
+
+ /*
+ Synchronize access to the adapter information using the base
+ context lock since we don't own the adapters.
+ */
+ ca_mutex_lock(context->lock);
+
+ // Stop discovery on all detected adapters.
+ g_list_foreach(context->adapters,
+ CACentralStopDiscoveryImpl,
+ &result);
+
+ /**
+ * @todo Stop notifications on all response characteristics.
+ */
+
+ ca_mutex_unlock(context->lock);
+
+ return result;
+}
+
+bool CACentralConnect(GDBusProxy * device)
+{
+ assert(device != NULL);
+
+ /*
+ Check if a connection to the LE peripheral was already
+ established. If not, establish a connection.
+ */
+ bool const is_connected =
+ CACentralGetBooleanProperty(device, "Connected");
+
+ if (is_connected)
+ {
+ return true;
+ }
+
+ GError * error = NULL;
+
+ // Connect to the discovered LE peripheral asynchronously.
+ GVariant * const ret =
+ g_dbus_proxy_call_sync(device,
+ "Connect",
+ NULL, // parameters
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, // timeout (default == -1),
+ NULL, // cancellable
+ &error);
+
+ if (ret == NULL)
+ {
+ OIC_LOG_V(ERROR,
+ TAG,
+ "%s.Connect() call failed: %s",
+ BLUEZ_DEVICE_INTERFACE,
+ error->message);
+
+ g_error_free(error);
+
+ return false;
+ }
+
+ g_variant_unref(ret);
+
+ return true;
+}
+
+bool CACentralConnectToAll(CALEContext * context)
+{
+ bool connected = true;
+
+ ca_mutex_lock(context->lock);
+
+ // Connect to the LE peripherals, if we're not already connected.
+ g_list_foreach(context->devices,
+ CACentralConnectToDevice,
+ &connected);
+
+ ca_mutex_unlock(context->lock);
+
+ return connected;
+}
--- /dev/null
+/******************************************************************
+ *
+ * Copyright 2015 Intel Corporation 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.
+ *
+ ******************************************************************/
+
+#ifndef CA_BLE_LINUX_CENTRAL_H
+#define CA_BLE_LINUX_CENTRAL_H
+
+#include "context.h"
+
+#include "cacommon.h"
+
+
+/**
+ * Initialize and start a Linux BLE "central".
+ *
+ * Initialize all Linux BLE "central" state (i.e. a global
+ * @c CACentralContext instance), as well as start
+ * discovery of OIC GATT transport service capable peripherals.
+ *
+ * @param[in] context Context containing BlueZ adapter information.
+ *
+ * @return @c CA_STATUS_OK on success.
+ */
+CAResult_t CACentralStart(CALEContext * context);
+
+/**
+ * Stop the Linux BLE "central".
+ *
+ * @param[in] context Context containing BlueZ adapter information.
+ *
+ * @return @c CA_STATUS_OK on success.
+ */
+CAResult_t CACentralStop(CALEContext * context);
+
+/**
+ * Start discovery of OIC Transport Profile capable LE peripherals.
+ *
+ * @param[in] context Context containing BlueZ adapter information.
+ *
+ * @return @c CA_STATUS_OK on success.
+ */
+CAResult_t CACentralStartDiscovery(CALEContext * context);
+
+/**
+ * Stop discovery of OIC Transport Profile capable LE peripherals.
+ *
+ * @param[in] context Context containing BlueZ adapter information.
+ *
+ * @return @c CA_STATUS_OK on success.
+ */
+CAResult_t CACentralStopDiscovery(CALEContext * context);
+
+/**
+ * Connect to the LE peripheral pointed by @a device.
+ *
+ * @param[in] device Proxy to the BlueZ @c org.bluez.Device1 object
+ * through which the connection to the LE peripheral
+ * will be established.
+ *
+ * @return @c true on success, @c false otherwise.
+ */
+bool CACentralConnect(GDBusProxy * device);
+
+/**
+ * Connect to all discovered LE peripherals.
+ *
+ * @param[in] context Context containing BlueZ adapter information.
+ *
+ * @return @c true on success, @c false otherwise.
+ */
+bool CACentralConnectToAll(CALEContext * context);
+
+
+#endif /* CA_BLE_LINUX_CENTRAL_H */
--- /dev/null
+/*****************************************************************
+ *
+ * Copyright 2015 Intel Corporation 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.
+ *
+ ******************************************************************/
+
+#include "characteristic.h"
+#include "service.h"
+#include "gatt_dbus.h"
+#include "utils.h"
+#include "bluez.h"
+#include "server.h"
+
+#include "oic_malloc.h"
+#include "oic_string.h"
+#include "logger.h"
+#include "cagattservice.h"
+#include "caremotehandler.h"
+
+#include <inttypes.h>
+#include <string.h>
+#include <assert.h>
+
+
+// Logging tag.
+static char const TAG[] = "BLE_CHARACTERISTIC";
+
+// ---------------------------------------------------------------------
+// GATT Request Handling
+// ---------------------------------------------------------------------
+/**
+ * Handle @c org.bluez.GattCharacterstic1.WriteValue() method call.
+ *
+ * This handler is triggered when the
+ * @c org.bluez.GattCharacterstic1.WriteValue() method is called by a
+ * client on the BlueZ-based OIC GATT request characteristic. In
+ * particular, IoTivity request data is sent by the GATT client to the
+ * GATT server through this call. The server will retrieve the data
+ * that was sent from the @a value argument. Reassembly of any
+ * request data fragments will begin here.
+ *
+ * @param[in] object @c org.bluez.GattCharacteristic1 skeleton
+ * object associated with this method call.
+ * @param[in] invocation D-Bus method invocation related object used
+ * when sending results/errors asynchronously.
+ * @param[in] value The @c GVariant containing the byte array
+ * (ay) with th request data inside.
+ * @param[in] user_data Pointer to request
+ * @c CAGattCharacteristic object.
+ *
+ * @return @c TRUE to indicate that
+ * @c org.bluez.Characteristic.WriteValue() method is
+ * implemented.
+ */
+static gboolean CAGattCharacteristicHandleWriteValue(
+ GattCharacteristic1 * object,
+ GDBusMethodInvocation * invocation,
+ GVariant * value,
+ gpointer user_data)
+{
+ /*
+ This method is only trigged in a GATT server when receiving
+ data sent by a GATT client, i.e. the client wrote a value to
+ the server, and the server is handling that write request.
+ */
+
+ gsize len = 0;
+ gconstpointer const data =
+ g_variant_get_fixed_array(value,
+ &len,
+ 1); // sizeof(guchar) == 1
+
+ CAGattCharacteristic * const c = user_data;
+
+ if (CAGattRecv(&c->recv_info, data, (uint32_t) len))
+ {
+ gatt_characteristic1_complete_write_value(object, invocation);
+ }
+ else
+ {
+ g_dbus_method_invocation_return_dbus_error(
+ invocation,
+ "org.bluez.Error.Failed",
+ "Error when handling GATT request data fragment");
+ }
+
+ return TRUE;
+}
+
+// ---------------------------------------------------------------------
+// GATT Response Handling
+// ---------------------------------------------------------------------
+/**
+ * Make the peer address corresponding to the given GATT
+ * characteristic.
+ *
+ * @param[in] c Information about GATT characteristic for which the
+ * peer (client) @c CAEndpoint_t object is being
+ * created.
+ *
+ * @return @c String containing an encoded address associated with the
+ * peer connected to the peripheral on which the characteristic
+ * implementation resides, or @c NULL on error.
+ */
+static char * CAGattCharacteristicMakePeerAddress(
+ CAGattCharacteristic * c)
+{
+ assert(c != NULL);
+
+ /*
+ Length of stringified pointer in hexadecimal format, plus one
+ for null terminator.
+ */
+ static size_t const PSEUDO_ADDR_LEN = sizeof(intptr_t) / 4 + 1;
+
+ assert(MAX_ADDR_STR_SIZE_CA > PSEUDO_ADDR_LEN);
+
+ /*
+ Since there is no direct way to obtain the client endpoint
+ associated with the GATT characterstics on the server side,
+ embed a stringified pointer to the response charactertistic of
+ the form "&ABCDEF01" is the CAEndpoint_t instead. This works
+ since:
+ 1) only one LE central is ever connected to an LE peripheral
+ 2) the CA layer doesn't directly interpret the address
+ */
+ char * const addr = OICMalloc(PSEUDO_ADDR_LEN);
+ int const count = snprintf(addr,
+ PSEUDO_ADDR_LEN,
+ "&%" PRIxPTR,
+ (uintptr_t) c);
+
+ if (count >= (int) PSEUDO_ADDR_LEN)
+ {
+ OIC_LOG(ERROR,
+ TAG,
+ "Error creating peer address on server side.");
+
+ return NULL;
+ }
+
+ return addr;
+}
+
+/**
+ * Handle @c org.bluez.GattCharacterstic1.StartNotify() method call.
+ *
+ * This handler is triggered when the
+ * @c org.bluez.GattCharacterstic1.StartNotify() method is called by a
+ * client on the BlueZ-based OIC GATT response characteristic. It
+ * sets the @c org.bluez.GattCharacterstic1.Notifying property to
+ * @c TRUE, and enables responses from the server.
+ *
+ * @note This handler is not available in the OIC GATT request
+ * characteristic implementation.
+ *
+ * @param[in] object @c org.bluez.GattCharacteristic1 skeleton
+ * object associated with this method call.
+ * @param[in] invocation D-Bus method invocation related object used
+ * when sending results/errors asynchronously.
+ * @param[in] user_data Pointer to the response
+ * @c CAGattCharacteristic object.
+ *
+ * @return @c TRUE to indicate that
+ * @c org.bluez.Characteristic.StartNotify() method is
+ * implemented.
+ */
+static gboolean CAGattCharacteristicHandleStartNotify(
+ GattCharacteristic1 * object,
+ GDBusMethodInvocation * invocation,
+ gpointer user_data)
+{
+ /**
+ * Only allow the client to start notifications once.
+ *
+ * @todo Does BlueZ already prevent redundant calls to
+ * @c org.bluez.GattCharacteristic1.StartNotify()?
+ */
+ if (gatt_characteristic1_get_notifying(object))
+ {
+ g_dbus_method_invocation_return_dbus_error(
+ invocation,
+ "org.bluez.Error.Failed",
+ "Notifications are already enabled.");
+
+ return TRUE;
+ }
+
+ // Retrieve the response characteristic information.
+ CAGattCharacteristic * const characteristic = user_data;
+
+ char * const peer =
+ CAGattCharacteristicMakePeerAddress(characteristic);
+
+ if (peer == NULL)
+ {
+ g_dbus_method_invocation_return_dbus_error(
+ invocation,
+ "org.bluez.Error.Failed",
+ "Error creating peer endpoint information");
+
+ return TRUE;
+ }
+
+ /*
+ Create an entry in the endpoint-to-characteristic map so that
+ responses may be sent to the GATT client through the OIC GATT
+ response characteristic through this BLE adapter's
+ CAAdapterSendUnicastData() implementation.
+ */
+ CALEContext * const context = characteristic->context;
+
+ ca_mutex_lock(context->lock);
+
+#if GLIB_CHECK_VERSION(2,40,0)
+ /*
+ GLib hash table functions started returning a boolean result in
+ version 2.40.x.
+ */
+ bool const inserted =
+#endif
+ g_hash_table_insert(context->characteristic_map,
+ peer,
+ characteristic);
+
+ ca_mutex_unlock(context->lock);
+
+#if GLIB_CHECK_VERSION(2,40,0)
+ if (!inserted)
+ {
+ g_dbus_method_invocation_return_dbus_error(
+ invocation,
+ "org.bluez.Error.Failed",
+ "Unable to set response endpoint.");
+
+ OICFree(peer);
+
+ return TRUE;
+ }
+#endif
+
+ /**
+ * @todo Do we need to explicitly emit the @c GObject @c notify or
+ * @c org.freedesktop.Dbus.Properties.PropertiesChanged
+ * signal here?
+ */
+ gatt_characteristic1_set_notifying(object, TRUE);
+
+ /*
+ Set the client endpoint field in the request characteristic so
+ that it may pass the appropriate endpoint object up the stack
+ through the CA request/response callback once a request has been
+ completely received and reassembled.
+ */
+ CAGattRecvInfo * const recv_info =
+ &characteristic->service->request_characteristic.recv_info;
+
+ recv_info->peer = peer;
+
+ ca_mutex_lock(context->lock);
+ recv_info->on_packet_received = context->on_server_received_data;
+ recv_info->context = context;
+ ca_mutex_unlock(context->lock);
+
+ gatt_characteristic1_complete_start_notify(object, invocation);
+
+ return TRUE;
+}
+
+/**
+ * Handle @c org.bluez.GattCharacterstic1.StopNotify() method call.
+ *
+ * This handler is triggered when the
+ * @c org.bluez.GattCharacterstic1.StopNotify() method is called by a
+ * client on the BlueZ-based OIC GATT response characteristic. It
+ * sets the @c org.bluez.GattCharacterstic1.Notifying property to
+ * @c FALSE, and disables responses from the server.
+ *
+ * @param[in] object @c org.bluez.GattCharacteristic1 skeleton
+ * object associated with this method call.
+ * @param[in] invocation D-Bus method invocation related object used
+ * when sending results/errors asynchronously.
+ * @param[in] user_data Pointer to the response
+ * @c CAGattCharacteristic object.
+ *
+ * @return @c TRUE to indicate that
+ * @c org.bluez.Characteristic.StopNotify() method is
+ * implemented.
+ */
+static gboolean CAGattCharacteristicHandleStopNotify(
+ GattCharacteristic1 * object,
+ GDBusMethodInvocation * invocation,
+ gpointer user_data)
+{
+ assert(user_data != NULL);
+
+ /**
+ * @todo Does BlueZ already prevent redundant calls to
+ * @c org.bluez.GattCharacteristic1.StopNotify()?
+ */
+ if (!gatt_characteristic1_get_notifying(object))
+ {
+ g_dbus_method_invocation_return_dbus_error(
+ invocation,
+ "org.bluez.Error.Failed",
+ "Notifications were not previously enabled.");
+
+ return TRUE;
+ }
+
+ CAGattCharacteristic * const characteristic = user_data;
+
+ // Clear the client endpoint from the request characteristic.
+ CAGattRecvInfo * const recv_info =
+ &characteristic->service->request_characteristic.recv_info;
+
+ /*
+ Remove the appropriate entry from the endpoint-to-characteristic
+ map so that attempts to send a response through it will fail.
+ */
+ CALEContext * const context = characteristic->context;
+ ca_mutex_lock(context->lock);
+
+ bool const removed =
+ g_hash_table_remove(context->characteristic_map, recv_info->peer);
+
+ ca_mutex_unlock(context->lock);
+
+ CAGattRecvInfoDestroy(recv_info);
+
+ /**
+ * @todo Do we need to explicitly emit the @c GObject @c notify or
+ * @c org.freedesktop.Dbus.Properties.PropertiesChanged
+ * signal here?
+ */
+ gatt_characteristic1_set_notifying(object, FALSE);
+
+ if (removed)
+ {
+ gatt_characteristic1_complete_stop_notify(object, invocation);
+ }
+ else
+ {
+ g_dbus_method_invocation_return_dbus_error(
+ invocation,
+ "org.bluez.Error.Failed",
+ "Error removing peer address information");
+ }
+
+ return TRUE;
+}
+
+// ---------------------------------------------------------------------
+// GATT Characteristic Lifecyle
+// ---------------------------------------------------------------------
+
+/**
+ * Initialize GATT characteristic fields.
+ *
+ * This function initializes the @c CAGattCharacteristic object fields.
+ *
+ * @param[out] c GATT characteristic information to
+ * be initialized.
+ * @param[in] context Object containing the D-Bus
+ * connection to the bus on which the
+ * characteristic will be exported, as
+ * well as the list of connected
+ * devices.
+ * @param[in] s Information about GATT service
+ * to which the characteristic
+ * belongs.
+ * @param[in] characteristic_path @c GattCharacteristic1 object
+ * path.
+ * @param[in] uuid GATT characteristic UUID.
+ * @param[in] flag GATT characteristic property flag,
+ * i.e. @c "write-without-response"
+ * for the request characteristic, and
+ * @c "notify" for the response
+ * characteristic.
+ *
+ * @note This function does not allocate the @a characteristic object
+ * itself. The caller is responsible for allocating that
+ * memory.
+ *
+ * @todo Too many parameters. Perhaps pass in a pointer to a struct
+ * instead.
+ */
+static bool CAGattCharacteristicInitialize(
+ CAGattCharacteristic * c,
+ CALEContext * context,
+ CAGattService * s,
+ char const * characteristic_path,
+ char const * uuid,
+ char const * flag)
+{
+ // Path of the form /org/iotivity/gatt/hci0/service0/char0.
+ c->object_path =
+ g_strdup_printf("%s/%s", s->object_path, characteristic_path);
+
+ assert(g_variant_is_object_path(c->object_path));
+
+ c->context = context;
+ c->service = s;
+
+ c->characteristic = gatt_characteristic1_skeleton_new();
+
+ gatt_characteristic1_set_uuid(c->characteristic, uuid);
+ gatt_characteristic1_set_service(c->characteristic, s->object_path);
+ gatt_characteristic1_set_notifying(c->characteristic, FALSE);
+
+ char const * flags[] = { flag, NULL };
+ gatt_characteristic1_set_flags(c->characteristic, flags);
+
+ CAGattRecvInfoInitialize(&c->recv_info);
+
+ // Export the characteristic interface on the bus.
+ GError * error = NULL;
+ if (!g_dbus_interface_skeleton_export(
+ G_DBUS_INTERFACE_SKELETON(c->characteristic),
+ context->connection,
+ c->object_path,
+ &error))
+ {
+ CAGattCharacteristicDestroy(c);
+
+ OIC_LOG_V(ERROR,
+ TAG,
+ "Unable to export D-Bus GATT characteristic "
+ "interface: %s",
+ error->message);
+
+ g_error_free(error);
+
+ return false;
+ }
+
+ return true;
+}
+
+// ------------------------------------------------------------
+
+bool CAGattRequestCharacteristicInitialize(struct CAGattService * s,
+ CALEContext * context)
+{
+ CAGattCharacteristic * const c = &s->request_characteristic;
+
+ if (!CAGattCharacteristicInitialize(c,
+ context,
+ s,
+ CA_GATT_REQUEST_CHRC_PATH,
+ CA_GATT_REQUEST_CHRC_UUID,
+ "write-without-response"))
+ {
+ return false;
+ }
+
+ if (!CAGattRequestDescriptorInitialize(s, context->connection))
+ {
+ CAGattCharacteristicDestroy(c);
+ return false;
+ }
+
+ /*
+ The descriptor object path is not fixed at compile-time.
+ Retrieve the object path that was set at run-time.
+ */
+ char const * descriptor_paths[] = {
+ c->descriptor.object_path,
+ NULL
+ };
+
+ gatt_characteristic1_set_descriptors(c->characteristic,
+ descriptor_paths);
+
+ // The request characteristic only handles writes.
+ g_signal_connect(c->characteristic,
+ "handle-write-value",
+ G_CALLBACK(CAGattCharacteristicHandleWriteValue),
+ c);
+
+ return true;
+}
+
+// ------------------------------------------------------------
+
+bool CAGattResponseCharacteristicInitialize(struct CAGattService * s,
+ CALEContext * context)
+{
+ CAGattCharacteristic * const c = &s->response_characteristic;
+
+ if (!CAGattCharacteristicInitialize(c,
+ context,
+ s,
+ CA_GATT_RESPONSE_CHRC_PATH,
+ CA_GATT_RESPONSE_CHRC_UUID,
+ "notify"))
+ {
+ return false;
+ }
+
+ c->service = s;
+
+ if (!CAGattResponseDescriptorInitialize(s, context->connection))
+ {
+ CAGattCharacteristicDestroy(c);
+ return false;
+ }
+
+ /*
+ The descriptor object path is not fixed at compile-time.
+ Retrieve the object path that was set at run-time.
+
+ Note that we don't explicitly add the Client Characteristic
+ Configuration Descriptor for the response characteristic since
+ that is done by BlueZ when the "notify" property is set.
+ Furthermore, a client requests notifications by calling the
+ org.bluez.GattCharacteristic1.StartNotify() method.
+ Consequently, there is no need for the client to explicitly
+ enable notifications by writing to the client characteristic
+ configuration descriptor.
+ */
+ char const * descriptor_paths[] = {
+ c->descriptor.object_path,
+ NULL
+ };
+
+ gatt_characteristic1_set_descriptors(c->characteristic,
+ descriptor_paths);
+
+ // The response characteristic only handles notifications.
+ g_signal_connect(
+ c->characteristic,
+ "handle-start-notify",
+ G_CALLBACK(CAGattCharacteristicHandleStartNotify),
+ c);
+
+ g_signal_connect(
+ c->characteristic,
+ "handle-stop-notify",
+ G_CALLBACK(CAGattCharacteristicHandleStopNotify),
+ c);
+
+ return true;
+}
+
+void CAGattCharacteristicDestroy(CAGattCharacteristic * c)
+{
+ assert(c != NULL); // As designed, c is always non-NULL.
+
+ CAGattRecvInfoDestroy(&c->recv_info);
+
+ CAGattDescriptorDestroy(&c->descriptor);
+
+ g_clear_object(&c->characteristic);
+
+ g_free(c->object_path);
+ c->object_path = NULL;
+
+ c->service = NULL;
+ c->context = NULL;
+}
+
+// ---------------------------------------------------------------------
+// GATT Characteristic Properties
+// ---------------------------------------------------------------------
+
+
+GVariant * CAGattCharacteristicGetProperties(
+ GattCharacteristic1 * characteristic)
+{
+ /**
+ * Create a variant containing the @c GattCharacteristic1
+ * properties, of the form @c a{sa{sv}}.
+ *
+ * @note We don't care about the "Value" property here since it is
+ * automatically made available by BlueZ on the client
+ * side.
+ *
+ * @todo Should we care about "Notifying" property here?
+ */
+
+ /*
+ Populate the property table, and create the variant to be
+ embedded in the results of the
+ org.freedesktop.Dbus.ObjectManager.GetManagedObjects() method
+ call.
+ */
+ CADBusSkeletonProperty const properties[] = {
+ { "UUID",
+ g_variant_new_string(
+ gatt_characteristic1_get_uuid(characteristic)) },
+ { "Service",
+ g_variant_new_object_path(
+ gatt_characteristic1_get_service(characteristic)) },
+ { "Flags",
+ g_variant_new_strv(
+ gatt_characteristic1_get_flags(characteristic),
+ -1) },
+ { "Descriptors",
+ g_variant_new_objv(
+ gatt_characteristic1_get_descriptors(characteristic),
+ -1) }
+ };
+
+ return
+ CAMakePropertyDictionary(
+ BLUEZ_GATT_CHARACTERISTIC_INTERFACE,
+ properties,
+ sizeof(properties) / sizeof(properties[0]));
+}
--- /dev/null
+/* ****************************************************************
+ *
+ * Copyright 2015 Intel Corporation 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.
+ *
+ ******************************************************************/
+
+#ifndef CA_BLE_LINUX_CHARACTERISTIC_H
+#define CA_BLE_LINUX_CHARACTERISTIC_H
+
+#include "bluez-glue.h"
+#include "descriptor.h"
+#include "recv.h"
+#include "context.h"
+
+
+/**
+ * OIC GATT Characteristic Information
+ *
+ * OIC GATT characteristics contain one user description descriptor.
+ *
+ * @note The response characteristic should also have a client
+ * characteristic configuration descriptor. However, BlueZ
+ * implicitly adds that descriptor to the characteristic when
+ * the characteristic "notify" property is set. There is no
+ * need to explicitly add that descriptor.
+ */
+typedef struct CAGattCharacteristic
+{
+
+ /**
+ * Object containing the D-Bus connection list of connected
+ * devices.
+ */
+ CALEContext * context;
+
+ /**
+ * IoTivity OIC GATT service information.
+ *
+ * @note This is currently only used by the response
+ * characteristic to gain access to the request
+ * characteristic @c client endpoint field.
+ *
+ * @todo It seems somewhat wasteful have a field available to both
+ * response and request characteristics, but used by only
+ * one of them.
+ */
+ struct CAGattService * service;
+
+ /// D-Bus object path for the GattCharacteristic1 object.
+ char * object_path;
+
+ /// OIC GATT service D-Bus interface skeleton object.
+ GattCharacteristic1 * characteristic;
+
+ /// OIC GATT user description descriptor information.
+ CAGattDescriptor descriptor;
+
+ /**
+ * Information used to keep track of received data fragments.
+ *
+ * @note This is only used by the OIC GATT request characteristic
+ * skeleton implementation. It is not needed by the
+ * response characteristic skeleton.
+ *
+ * @todo It seems somewhat wasteful have a field available to both
+ * response and request characteristics, but used by only
+ * one of them.
+ */
+ CAGattRecvInfo recv_info;
+
+} CAGattCharacteristic;
+
+/**
+ * Initialize GATT request characteristic fields.
+ *
+ * This function initializes the request @c CAGattCharacteristic
+ * object fields.
+ *
+ * @param[in,out] s Information about GATT service to which the
+ * characteristic belongs.
+ * @param[in] context Object containing the D-Bus connection to
+ * the bus on which the characteristic will be
+ * exported, as well as the list of connected
+ * devices.
+ *
+ * @return @c true on success, @c false otherwise.
+ */
+bool CAGattRequestCharacteristicInitialize(struct CAGattService * s,
+ CALEContext * context);
+
+/**
+ * Initialize GATT response characteristic fields.
+ *
+ * This function initializes the response @c CAGattCharacteristic
+ * object fields.
+ *
+ * @param[in,out] s Information about GATT service to which the
+ * characteristic belongs.
+ * @param[in] context Object containing the D-Bus connection to
+ * the bus on which the characteristic will be
+ * exported, as well as the list of connected
+ * devices.
+ *
+ * @return @c true on success, @c false otherwise.
+ */
+bool CAGattResponseCharacteristicInitialize(struct CAGattService * s,
+ CALEContext * context);
+
+/**
+ * Destroy GATT characteristic fields.
+ *
+ * This function finalizes the @c CAGattCharacteristic object fields.
+ *
+ * @param[in] characteristic GATT characteristic information to be
+ * finalized.
+ */
+void CAGattCharacteristicDestroy(CAGattCharacteristic * characteristic);
+
+/**
+ * Get all characteristic properties.
+ *
+ * @param[in] characteristic The D-Bus skeleton object from which the
+ * characteristic properties will be extracted.
+ * @return A variant of the form a{sa{sv}}, suitable for use in the
+ * result of the
+ * @c org.freedesktop.DBus.ObjectManager.GetManagedObjects()
+ * method provided by the IoTivity the
+ * @c org.bluez.GattService1 implementation.
+ */
+GVariant * CAGattCharacteristicGetProperties(
+ GattCharacteristic1 * characteristic);
+
+
+#endif // CA_BLE_LINUX_CHARACTERISTIC_H
--- /dev/null
+/******************************************************************
+ *
+ * Copyright 2015 Intel Corporation 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.
+ *
+ ******************************************************************/
+
+#include "client.h"
+#include "recv.h"
+#include "context.h"
+#include "bluez.h"
+#include "utils.h"
+
+#include "cafragmentation.h"
+#include "logger.h"
+#include "oic_malloc.h"
+#include "oic_string.h"
+
+#include <gio/gio.h>
+
+#include <string.h>
+#include <assert.h>
+
+
+// Logging tag.
+static char const TAG[] = "BLE_CLIENT";
+
+// ---------------------------------------------------------------------
+// GATT Client Set-up
+// ---------------------------------------------------------------------
+static bool CAGattClientServiceFilter(GDBusProxy * service)
+{
+ /*
+ On the client side, we only care about the GATT services on
+ remote devices. Ignore the locally created ones by checking for
+ the existence of the org.bluez.GattService1.Device property in
+ the service proxy. GATT services on remote devices will have
+ that property.
+ */
+ GVariant * const remote_device =
+ g_dbus_proxy_get_cached_property(service, "Device");
+
+ if (remote_device == NULL)
+ {
+ return false;
+ }
+
+ /*
+ org.bluez.GattService1.Device property exists, meaning the
+ GATT service was advertised from a remote object.
+ */
+ g_object_unref(remote_device);
+ return true;
+}
+
+bool CAGattClientsInitialize(CALEContext * context)
+{
+ /*
+ Create a proxies to the org.bluez.GattService1 D-Bus objects that
+ will later be used to send requests and receive responses on the
+ client side.
+ */
+ GList * services = NULL;
+ bool success =
+ CAGetBlueZManagedObjectProxies(&services,
+ BLUEZ_GATT_SERVICE_INTERFACE,
+ context,
+ CAGattClientServiceFilter);
+
+ /**
+ * @todo Is this really an error?
+ */
+ if (!success)
+ {
+ return success;
+ }
+
+ /*
+ Map Bluetooth MAC address to OIC Transport Profile
+ characteristics.
+ */
+ GHashTable * const characteristic_map =
+ g_hash_table_new_full(g_str_hash,
+ g_str_equal,
+ OICFree,
+ g_object_unref);
+
+ char const * const address = NULL; // OICMalloc(...);
+ GDBusProxy * const client = NULL;
+
+#if GLIB_CHECK_VERSION(2,40,0)
+ /*
+ GLib hash table functions started returning a boolean result in
+ version 2.40.x.
+ */
+ success =
+#endif
+ g_hash_table_insert(characteristic_map,
+ OICStrdup(address),
+ client);
+
+ // An empty services list is NULL.
+ if (success && services != NULL)
+ {
+ ca_mutex_lock(context->lock);
+ context->characteristic_map = characteristic_map;
+ ca_mutex_unlock(context->lock);
+ }
+
+ return success;
+}
+
+bool CAGattClientsDestroy(CALEContext * context)
+{
+ /* g_hash_table_destroy(...); */ // FIXME
+ return false;
+}
+
+// ---------------------------------------------------------------------
+// GATT Request Send
+// ---------------------------------------------------------------------
+/**
+ * Send data to the GATT server through the given request
+ * @a characteristic proxy.
+ *
+ * @param[in] characteristic The D-Bus proxy of the request
+ * characteristic through which the
+ * @c WriteValue() method will be invoked.
+ * @param[in] data The byte array to be sent.
+ * @param[in] length The number of elements in the byte
+ * array.
+ */
+static bool CAGattClientSendRequestData(GDBusProxy * characteristic,
+ CALEContext * context,
+ char const * data,
+ size_t length)
+{
+ assert(context != NULL);
+
+ GVariant * const value =
+ g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,
+ data,
+ length,
+ 1); // sizeof(data[0]) == 1
+
+ GError * error = NULL;
+
+ GVariant * const ret =
+ g_dbus_proxy_call_sync(characteristic,
+ "WriteValue",
+ value, // parameters
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, // timeout (default == -1),
+ NULL, // cancellable
+ &error);
+
+ if (ret == NULL)
+ {
+ OIC_LOG_V(ERROR,
+ TAG,
+ "[%p] WriteValue() call failed: %s",
+ characteristic,
+ error->message);
+
+ g_error_free(error);
+
+ ca_mutex_lock(context->lock);
+
+ if (context->on_client_error != NULL)
+ {
+ /*
+ At this point endpoint and send data information is
+ available.
+ */
+ context->on_client_error(NULL, // endpoint
+ data,
+ length,
+ CA_STATUS_FAILED);
+ }
+
+ ca_mutex_unlock(context->lock);
+
+ return false;
+ }
+
+ g_variant_unref(ret);
+
+ return true;
+}
+
+CAResult_t CAGattClientSendData(void const * method_info,
+ void const * data,
+ size_t length)
+{
+ assert(method_info != NULL);
+
+ CAGattRequestInfo const * const info = method_info;
+
+ GDBusProxy * const characteristic =
+ G_DBUS_PROXY(info->characteristic_info);
+
+ return CAGattClientSendRequestData(characteristic,
+ info->context,
+ (char const *) data,
+ length);
+}
+
+CAResult_t CAGattClientSendDataToAll(void const * method_info,
+ void const * data,
+ size_t length)
+{
+ assert(method_info != NULL);
+
+ CAResult_t result = CA_STATUS_OK;
+
+ CAGattRequestInfo const * const info = method_info;
+
+ for (GList const * l = info->characteristic_info;
+ l != NULL && result == CA_STATUS_OK;
+ l = l->next)
+ {
+ GDBusProxy * const characteristic = G_DBUS_PROXY(l->data);
+
+ result = CAGattClientSendRequestData(characteristic,
+ info->context,
+ (char const *) data,
+ length);
+ }
+
+ return result;
+}
+
+// ---------------------------------------------------------------------
+// GATT Response Receive
+// ---------------------------------------------------------------------
+void CAGattReceiveResponse(GDBusConnection * connection,
+ char const * sender_name,
+ char const * object_path,
+ char const * interface_name,
+ char const * signal_name,
+ GVariant * parameters,
+ gpointer user_data)
+{
+ /*
+ This handler is only trigged in a GATT client when receiving
+ data sent by a GATT server through a notification, e.g. such as
+ when a GATT server sent a response.
+ */
+ gsize fragment_len = 0;
+ gconstpointer const fragment =
+ g_variant_get_fixed_array(parameters,
+ &fragment_len,
+ 1); // sizeof(guchar) == 1
+
+ CAGattRecvInfo * const info = user_data;
+
+ if (CAGattRecv(info, fragment, fragment_len))
+ {
+ }
+ else
+ {
+ }
+}
--- /dev/null
+/******************************************************************
+ *
+ * Copyright 2015 Intel Corporation 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.
+ *
+ ******************************************************************/
+
+#ifndef CA_BLE_LINUX_CLIENT_H
+#define CA_BLE_LINUX_CLIENT_H
+
+#include "context.h"
+
+
+/**
+ * Information needed to when sending a request through a GATT
+ * client.
+ */
+typedef struct _CAGattRequestInfo
+{
+ /**
+ * Proxy or list of proxies to @c org.bluez.GattCharacteristic1
+ * object(s) through which request data will be sent to the GATT
+ * server.
+ *
+ * In the case of a unicast-style send, @c info will be a
+ * @c GDBusProxy* to an @c org.bluez.GattCharacteristic1 object.
+ * For a multicast-style send, @c info will be a * @c GList* of
+ * @c GDBusProxy* to @c GattCharacterstic1 objects on all GATT
+ * servers to which a connection exists.
+ */
+ void * const characteristic_info;
+
+ /**
+ * Context containing additional information that may be needed
+ * when sending a request.
+ */
+ CALEContext * const context;
+
+} CAGattRequestInfo;
+
+/**
+ * Send request data through a single user-specified BLE connection.
+ *
+ * @param[in] method_info Information necessary to send request.
+ * @param[in] data Octet array of request data to be sent.
+ * @param[in] length Length of the @a data octet array.
+ *
+ * @see @c CAGattSendMethod() for further details.
+ */
+bool CAGattSendRequest(void const * method_info,
+ void const * data,
+ size_t length);
+
+// ---------------------------------------------------------------
+// Multicast-style Request Send
+// ---------------------------------------------------------------
+/**
+ * Send request data through all BLE connections.
+ *
+ * Send the @a data to the GATT server found in all discovered LE
+ * peripherals.
+ *
+ * @param[in] method_info Information necessary to send request.
+ * @param[in] data Octet array of request data to be sent.
+ * @param[in] length Length of the @a data octet array.
+
+ * @note Since a multicast-like operation is being performed, an
+ * assumption is made that a GATT client is sending data to a
+ * server. It makes no sense to multicast a response from a
+ * single GATT server to multiple GATT clients in IoTivity's
+ * case.
+ *
+ * @return @c CA_STATUS_OK on success, @c CA_STATUS_FAILED otherwise.
+ */
+CAResult_t CAGattClientSendDataToAll(void const * method_info,
+ void const * data,
+ size_t length);
+
+
+#endif /* CA_BLE_LINUX_CLIENT_H */
--- /dev/null
+/******************************************************************
+ *
+ * Copyright 2015 Intel Corporation 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.
+ *
+ ******************************************************************/
+
+#ifndef CA_BLE_LINUX_CONTEXT_H
+#define CA_BLE_LINUX_CONTEXT_H
+
+#include "caadapterinterface.h"
+#include "camutex.h"
+#include "cathreadpool.h"
+#include "caleinterface.h"
+
+#include <gio/gio.h>
+
+
+/**
+ * @internal
+ *
+ * BLE Linux adapter base context.
+ */
+typedef struct _CALEContext
+{
+ /// Connection to the D-Bus system bus.
+ GDBusConnection * connection;
+
+ /**
+ * Proxy to the BlueZ D-Bus object that implements the
+ * "org.freedesktop.DBus.ObjectManager" interface.
+ *
+ * @todo There's probably no need to keep this around after we've
+ * retrieved the managed objects the first time around since
+ * we can rely on signals to alert us to any subsequent
+ * changes.
+ */
+ GDBusObjectManager * object_manager;
+
+ /**
+ * List of @c GDBusObject objects obtained from the BlueZ
+ * @c ObjectManager.
+ *
+ * @note This list will be updated later on as needed if changes
+ * in the BlueZ ObjectManager are detected.
+ */
+ GList * objects;
+
+ /**
+ * BlueZ adapter list.
+ *
+ * List of @c GDBusProxy objects for all BlueZ adapters (i.e.
+ * @c org.bluez.Adapter1). More than one adapter can exist if
+ * multiple Bluetooth hardware interfaces are detected by BlueZ.
+ */
+ GList * adapters;
+
+ /**
+ * BlueZ device list.
+ *
+ * List of @c GDBusProxy objects for all BlueZ devices (i.e.
+ * @c org.bluez.Device1), such as those that matched the discovery
+ * criteria.
+ */
+ GList * devices;
+
+ /**
+ * Bluetooth MAC address to GATT characteristic map.
+ *
+ * Hash table that maps Bluetooth MAC address to a OIC Transport
+ * Profile GATT characteristic. The key is a string containing
+ * the peer Bluetooth adapter MAC address. The value is an
+ * interface proxy (@c GDBusProxy) to an
+ * @c org.bluez.GattCharacteristic1 object.
+ *
+ * On the client side, this maps a Bluetooth peripheral MAC
+ * address to the corresponding request characteristic proxy. On
+ * the server side, this maps Bluetooth central MAC address to the
+ * corresponding response characteristic proxy.
+ *
+ * @note On the server side a map is overkill since only one
+ * client is ever connected to the server. No?
+ *
+ * @todo We may want to have a seperate server-side map to reduce
+ * contention on this map.
+ */
+ GHashTable * characteristic_map;
+
+ /**
+ * GATT characteristics to Bluetooth MAC address map.
+ *
+ * Hash table that maps OIC Transport Profile GATT characteristic
+ * to a Bluetooth MAC address. The key is an interface proxy
+ * (@c GDBusProxy) to an @c org.bluez.GattCharacteristic1 object.
+ * The value is a pointer to the peer @c CAEndpoint_t object.
+ *
+ * On the client side, this maps a response characteristic to the
+ * corresponding MAC address. On the server side, this maps
+ * request characteristic to the corresponding MAC address.
+ *
+ * @note On the server side a map is overkill since only one
+ * client is ever connected to the server. No?
+ *
+ * @todo We may want to have a seperate server-side map to reduce
+ * contention on this map.
+ */
+ GHashTable * address_map;
+
+ /**
+ * D-Bus signal subscription identifiers.
+ *
+ * The Linux BLE transport implementation subscribes to three
+ * D-Bus signals:
+ *
+ * @li @c org.freedesktop.DBus.ObjectManager.InterfacesAdded
+ * @li @c org.freedesktop.DBus.ObjectManager.InterfacesRemoved
+ * @li @c org.freedesktop.DBus.Properties.PropertiesChanged
+ * @li @c org.bluez.Adapter1.PropertyChanged
+ *
+ * These subscription identifiers are only used when unsubscribing
+ * from the signals when stopping the LE transport.
+ *
+ * @todo Verify if we need the two property related signals at
+ * this level.
+ */
+ //@{
+ guint interfaces_added_sub_id;
+ guint interfaces_removed_sub_id;
+ guint properties_changed_sub_id;
+ guint property_changed_sub_id;
+ //@}
+
+ /// Glib event loop that drives D-Bus signal handling.
+ GMainLoop * event_loop;
+
+ /**
+ * Callback invoked upon change in local Bluetooth adapter state.
+ */
+ CALEDeviceStateChangedCallback on_device_state_changed;
+
+ /// Callback invoked upon server receiving request data.
+ CABLEDataReceivedCallback on_server_received_data;
+
+ /// Callback invoked upon client receiving response data.
+ CABLEDataReceivedCallback on_client_received_data;
+
+ /**
+ * Handle to thread pool to which client side tasks will be
+ * added.
+ */
+ ca_thread_pool_t client_thread_pool;
+
+ /**
+ * Handle to thread pool to which server side tasks will be
+ * added.
+ */
+ ca_thread_pool_t server_thread_pool;
+
+ /// Callback invoked when reporting a client side error.
+ CABLEErrorHandleCallback on_client_error;
+
+ /// Callback invoked when reporting a server side error.
+ CABLEErrorHandleCallback on_server_error;
+
+ /// Mutex used to synchronize access to context fields.
+ ca_mutex lock;
+
+ /**
+ * BlueZ adapter list initialization condition variable.
+ *
+ * This condition variable is used to prevent the BLE adapter
+ * "start" from completing until the thread performing BlueZ
+ * adapter query completes. Initialization is performed in the
+ * same thread that will run the event loop. The condition
+ * variable is also used to wait for peripheral devices to be
+ * discovered.
+ *
+ * @see @c GMainLoop documentation for further details.
+ */
+ ca_cond condition;
+
+} CALEContext;
+
+
+#endif /* CA_BLE_LINUX_CONTEXT_H */
--- /dev/null
+/* ****************************************************************
+ *
+ * Copyright 2015 Intel Corporation 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.
+ *
+ ******************************************************************/
+
+#include "descriptor.h"
+#include "service.h"
+#include "gatt_dbus.h"
+#include "utils.h"
+#include "bluez.h"
+
+#include "logger.h"
+#include "cagattservice.h"
+
+#include <assert.h>
+
+
+// Logging tag.
+static char const TAG[] = "BLE_DESCRIPTOR";
+
+/**
+ * Implementation of the @c org.bluez.GattDescriptor1.ReadValue()
+ * method.
+ *
+ * This function is implemented as a GDBus signal handler that returns
+ * a byte array containing the @c Value property of the GATT
+ * descriptor.
+ *
+ * @param[in] object @c org.bluez.GattDescriptoror1 skeleton
+ * object associated with this method call.
+ * @param[in] invocation D-Bus method invocation related object used
+ * when sending results/errors asynchronously.
+ * @param[in] user_data Unused.
+ *
+ * @return @c TRUE to indicate that
+ * @c org.bluez.Descriptor.ReadValue()
+ * method is implemented.
+ */
+static gboolean CAGattDescriptorReadValue(
+ GattDescriptor1 * object,
+ GDBusMethodInvocation * invocation,
+ gpointer user_data)
+{
+ /**
+ * @todo The @c GattDescriptor1 object still owns the returned
+ * variant when using the below call. Should be we use
+ * @c gatt_descriptor1_dup_value() instead?
+ */
+ GVariant * const value = gatt_descriptor1_get_value(object);
+
+ gatt_descriptor1_complete_read_value(object, invocation, value);
+
+ return TRUE;
+}
+
+/**
+ * Initialize GATT descriptor fields.
+ *
+ * This function initializes the @c CAGattDescriptor object fields.
+ *
+ * @param[out] c Information about GATT characteristic
+ * to which the descriptor belongs.
+ * @param[in] connection D-Bus connection to the bus on which
+ * the descriptor will be exported.
+ * @param[in] s Information about GATT service
+ * information to which the descriptor
+ * belongs.
+ * @param[in] descriptor_path @c GattDescriptor1 object path for the
+ * user description descriptor.
+ * @param[in] value User description descriptor value,
+ * e.g. @c "OIC Node Request" or
+ * @c "OIC Node Response".
+ *
+ * @note This function does not allocate the @a descriptor object
+ * itself. The caller is responsible for allocating that
+ * memory.
+ *
+ * @todo Too many parameters. Perhaps pass in a pointer to a struct
+ * instead.
+ */
+static bool CAGattDescriptorInitialize(CAGattCharacteristic * c,
+ GDBusConnection * connection,
+ CAGattService * s,
+ char const * descriptor_path,
+ char const * value)
+{
+ CAGattDescriptor * const d = &c->descriptor;
+
+ // Path of the form /org/iotivity/gatt/hci0/service0/char0/desc0.
+ d->object_path =
+ g_strdup_printf("%s/%s", c->object_path, descriptor_path);
+ assert(g_variant_is_object_path(d->object_path));
+
+ d->descriptor = gatt_descriptor1_skeleton_new();
+
+ gatt_descriptor1_set_uuid(
+ d->descriptor,
+ CA_GATT_CHRC_USER_DESCRIPTION_DESC_UUID);
+
+ gatt_descriptor1_set_characteristic(d->descriptor,
+ c->object_path);
+
+ gatt_descriptor1_set_value (d->descriptor,
+ g_variant_new_bytestring(value));
+
+ // Readable, no encryption, no authorization.
+ static char const * flags[] = { "read", NULL };
+ gatt_descriptor1_set_flags(d->descriptor, flags);
+
+ /*
+ Connect the signal handler that implements the
+ orb.bluez.GattDescriptor1.ReadValue() method.
+ */
+ g_signal_connect(d->descriptor,
+ "handle-read-value",
+ G_CALLBACK(CAGattDescriptorReadValue),
+ NULL);
+
+ // Export the descriptor interface on the bus.
+ GError * error = NULL;
+ if (!g_dbus_interface_skeleton_export(
+ G_DBUS_INTERFACE_SKELETON(d->descriptor),
+ connection,
+ d->object_path,
+ &error))
+ {
+ CAGattDescriptorDestroy(d);
+
+ OIC_LOG_V(ERROR,
+ TAG,
+ "Unable to export D-Bus GATT descriptor "
+ "interface: %s",
+ error->message);
+
+ g_error_free(error);
+
+ return false;
+ }
+
+ return true;
+}
+
+bool CAGattRequestDescriptorInitialize(struct CAGattService * s,
+ GDBusConnection * connection)
+{
+ CAGattCharacteristic * const c = &s->request_characteristic;
+
+ return CAGattDescriptorInitialize(c,
+ connection,
+ s,
+ CA_GATT_REQUEST_USER_DESC_PATH,
+ CA_GATT_REQUEST_USER_DESCRIPTION);
+}
+
+bool CAGattResponseDescriptorInitialize(struct CAGattService * s,
+ GDBusConnection * connection)
+{
+ CAGattCharacteristic * const c = &s->response_characteristic;
+
+ return CAGattDescriptorInitialize(c,
+ connection,
+ s,
+ CA_GATT_RESPONSE_USER_DESC_PATH,
+ CA_GATT_RESPONSE_USER_DESCRIPTION);
+}
+
+void CAGattDescriptorDestroy(CAGattDescriptor * d)
+{
+ assert(d != NULL); // As designed, d is always non-NULL.
+
+ g_clear_object(&d->descriptor);
+
+ g_free(d->object_path);
+ d->object_path = NULL;
+}
+
+GVariant * CAGattDescriptorGetProperties(GattDescriptor1 * descriptor)
+{
+ /**
+ * Create a variant containing the @c GattDescriptor1 properties,
+ * of the form @c a{sa{sv}}.
+ *
+ * @note We don't care about the "Value" property here since it is
+ * automatically made available by BlueZ on the client
+ * side.
+ */
+
+ /*
+ Populate the property table, and create the variant to be
+ embedded in the results of the
+ org.freedesktop.Dbus.ObjectManager.GetManagedObjects() method
+ call.
+ */
+ CADBusSkeletonProperty const properties[] = {
+ { "UUID",
+ g_variant_new_string(
+ gatt_descriptor1_get_uuid(descriptor)) },
+ { "Characteristic",
+ g_variant_new_object_path(
+ gatt_descriptor1_get_characteristic(descriptor)) },
+ { "Flags",
+ g_variant_new_strv(
+ gatt_descriptor1_get_flags(descriptor),
+ -1) }
+ };
+
+ return
+ CAMakePropertyDictionary(
+ BLUEZ_GATT_DESCRIPTOR_INTERFACE,
+ properties,
+ sizeof(properties) / sizeof(properties[0]));
+}
--- /dev/null
+/* ****************************************************************
+ *
+ * Copyright 2015 Intel Corporation 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.
+ *
+ ******************************************************************/
+
+#ifndef CA_BLE_LINUX_DESCRIPTOR_H
+#define CA_BLE_LINUX_DESCRIPTOR_H
+
+#include "bluez-glue.h"
+
+#include <stdbool.h>
+
+
+/**
+ * OIC GATT Descriptor Information
+ */
+typedef struct CAGattDescriptor
+{
+ /// D-Bus object path for the GattCharacteristic1 object.
+ char * object_path;
+
+ /// OIC GATT service D-Bus interface skeleton object.
+ GattDescriptor1 * descriptor;
+
+} CAGattDescriptor;
+
+// Forward declarations.
+struct CAGattService;
+
+/**
+ * Initialize GATT request descriptor fields.
+ *
+ * This function initializes the request @c CAGattDescriptor object
+ * fields.
+ *
+ * @param[in,out] s Information about GATT service to which the
+ * characteristic and descriptor belong.
+ * @param[in] connection D-Bus connection to the bus on which the
+ * descriptor will be exported.
+ *
+ * @return @c true on success, @c false otherwise.
+ */
+bool CAGattRequestDescriptorInitialize(struct CAGattService * s,
+ GDBusConnection * connection);
+
+/**
+ * Initialize GATT response descriptor fields.
+ *
+ * This function initializes the response @c CAGattDescriptor object
+ * fields.
+ *
+ * @param[in,out] s Information about GATT service to which the
+ * characteristic and descriptor belong.
+ * @param[in] connection D-Bus connection to the bus on which the
+ * descriptor will be exported.
+ *
+ * @return @c true on success, @c false otherwise.
+ */
+bool CAGattResponseDescriptorInitialize(struct CAGattService *s,
+ GDBusConnection * connection);
+
+/**
+ * Destroy GATT descriptor fields.
+ *
+ * This function finalizes the @c CAGattDescriptor object fields.
+ *
+ * @param[in] descriptor GATT characteristic information to be
+ * finalized.
+ */
+void CAGattDescriptorDestroy(CAGattDescriptor * descriptor);
+
+/**
+ * Get all descriptor properties.
+ *
+ * @param[in] descriptor The D-Bus skeleton object from which the
+ * descriptor properties will be extracted.
+ *
+ * @return A variant of the form a{sa{sv}}, suitable for use in the
+ * result of the
+ * @c org.freedesktop.DBus.ObjectManager.GetManagedObjects()
+ * method provided by the IoTivity the
+ * @c org.bluez.GattService1 implementation.
+ */
+GVariant * CAGattDescriptorGetProperties(GattDescriptor1 * descriptor);
+
+
+#endif // CA_BLE_LINUX_DESCRIPTOR_H
--- /dev/null
+/******************************************************************
+ *
+ * Copyright 2015 Intel Corporation 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.
+ *
+ ******************************************************************/
+
+#ifndef CA_BLE_LINUX_GATT_DBUS_H
+#define CA_BLE_LINUX_GATT_DBUS_H
+
+
+/**
+ * @name BlueZ GATT Service D-Bus Object Paths
+ *
+ * The IoTivity BlueZ GATT Service hierarchy is the following:
+ *
+ * -> /org/iotivity/gatt/advertisement0
+ *
+ * -> /org/iotivity/gatt/<hciX>/service0
+ * | - OIC GATT Service
+ * |
+ * -> /org/iotivity/gatt/<hciX>/service0/char0
+ * | | - OIC GATT Request Characteristic Value
+ * | |
+ * | -> /org/iotivity/gatt/<hciX>/service0/char0/desc0
+ * | - OIC GATT Request User Description Descriptor
+ * |
+ * -> /org/iotivity/gatt/<hciX>/service0/char1
+ * | - OIC GATT Response Characteristic Value
+ * |
+ * -> /org/iotivity/gatt/<hciX>/service0/char1/desc0
+ * - OIC GATT Response User Description Descriptor
+ *
+ * where <hciX> corresponds to the bluetooth hardware adapter with
+ * which the GATT service is being registered, e.g. "hci0".
+ *
+ * @note The OIC GATT Client Characterstic Configuration Descriptor is
+ * implicitly added to the response characteristic hierarchy by
+ * BlueZ since its "notify" property is set.
+ */
+//@{
+
+/**
+ * Root object path of the GATT service hierarchy.
+ *
+ * The GATT service object manager (i.e. implementation of
+ * @c org.freedesktop.DBus.ObjectManager) is found at this object
+ * path.
+ */
+#define CA_GATT_SERVICE_ROOT_PATH "/org/iotivity/gatt"
+
+// ------------------------
+
+#define CA_LE_ADVERTISEMENT_PATH "advertisement0"
+
+// ------------------------
+
+/// GATT service object path basename.
+#define CA_GATT_SERVICE_PATH "service0"
+
+// ------------------------
+
+/// Request GATT characteristic object path basename.
+#define CA_GATT_REQUEST_CHRC_PATH "char0"
+
+/// Request GATT user description descriptor object path basename.
+#define CA_GATT_REQUEST_USER_DESC_PATH "desc0"
+
+// ------------------------
+
+/// Response GATT characteristic object path basename.
+#define CA_GATT_RESPONSE_CHRC_PATH "char1"
+
+/// Response GATT user description descriptor object path basename.
+#define CA_GATT_RESPONSE_USER_DESC_PATH "desc0"
+//@}
+
+#endif // CA_BLE_LINUX_GATT_DBUS_H
--- /dev/null
+<!--
+ Copyright 2015 Intel Corporation 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.
+
+ ******************************************************************
+
+ D-Bus introspection XML for the standard
+ org.freedesktop.DBus.ObjectManager interface.
+
+ The introspection XML found in this file is used to generate GDBus
+ skeleton code that will be used by the IoTivity BlueZ-based GATT
+ Service implementation.
+-->
+
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+ <interface name="org.freedesktop.DBus.ObjectManager">
+ <method name="GetManagedObjects">
+ <arg name="objects" type="a{oa{sa{sv}}}" direction="out"/>
+ </method>
+
+ <signal name="InterfacesAdded">
+ <arg name="object" type="o"/>
+ <arg name="interfaces" type="a{sa{sv}}"/>
+ </signal>
+
+ <signal name="InterfacesRemoved">
+ <arg name="object" type="o"/>
+ <arg name="interfaces" type="as"/>
+ </signal>
+ </interface>
+
+</node>
--- /dev/null
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+
+<!-- D-Bus policy for the IoTivity Linux BLE transport. -->
+<busconfig>
+ <policy context="default">
+ <allow own=@service_name@/>
+ <allow send_destination=@service_name@/>
+
+ <allow send_interface="org.freedesktop.DBus.ObjectManager"
+ send_type="method_call" send_member="GetManagedObjects"/>
+
+ <allow send_interface="org.bluez.GattService1"/>
+ <allow send_interface="org.bluez.GattCharacteristic1"/>
+ <allow send_interface="org.bluez.GattDescriptor1"/>
+
+ <allow send_interface="org.bluez.LEAdvertisement1"/>
+ </policy>
+</busconfig>
--- /dev/null
+/******************************************************************
+ *
+ * Copyright 2015 Intel Corporation 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.
+ *
+ ******************************************************************/
+
+#include "peripheral.h"
+#include "utils.h"
+#include "bluez.h"
+#include "service.h"
+#include "characteristic.h"
+#include "descriptor.h"
+
+#include "oic_malloc.h"
+#include "logger.h"
+
+#include <string.h>
+#include <assert.h>
+
+
+#define MICROSECS_PER_SEC 1000000
+
+// Logging tag.
+static char const TAG[] = "BLE_PERIPHERAL";
+
+static CAPeripheralContext g_context = {
+ .lock = NULL
+};
+
+static bool CAPeripheralCheckStarted()
+{
+ ca_mutex_lock(g_context.lock);
+
+ bool const started = (g_context.base != NULL);
+
+ ca_mutex_unlock(g_context.lock);
+
+ /**
+ * @todo Fix potential TOCTOU race condition. A peripheral could
+ * have been started or stopped between the mutex unlock and
+ * boolean check.
+ */
+ return started;
+}
+
+
+static bool CAPeripheralAdaptersFound(CALEContext * context)
+{
+ // Check if BlueZ detected bluetooth hardware adapters.
+ ca_mutex_lock(context->lock);
+
+ bool const found = (context->adapters != NULL);
+
+ ca_mutex_unlock(context->lock);
+
+ if (!found)
+ {
+ OIC_LOG(WARNING, TAG, "No bluetooth hardware found.");
+ }
+
+ return found;
+}
+
+static void CAPeripheralDestroyGattServices(gpointer data)
+{
+ CAGattService * const service = data;
+
+ CAGattServiceDestroy(service);
+
+ OICFree(service);
+}
+
+static GList * CAPeripheralInitializeGattServices(CALEContext * context)
+{
+ GList * result = NULL;
+
+ /*
+ Create a proxies to the org.bluez.GattManager1 D-Bus objects that
+ will later be used to register the OIC GATT service.
+ */
+ GList * gatt_managers = NULL;
+ if (!CAGetBlueZManagedObjectProxies(&gatt_managers,
+ BLUEZ_GATT_MANAGER_INTERFACE,
+ context,
+ NULL))
+ return result;
+
+ GList * gatt_services = NULL;
+
+ for (GList * l = gatt_managers; l != NULL; )
+ {
+ CAGattService * const service = OICCalloc(1, sizeof(*service));
+
+ if (service == NULL)
+ {
+ g_list_free_full(gatt_services,
+ CAPeripheralDestroyGattServices);
+ g_list_free_full(gatt_managers, g_object_unref);
+ return result;
+ }
+
+ /*
+ Use the HCI device name (e.g. hci0) in GattManager1 object
+ path (e.g. /org/bluez/hci0) as a means to differentiate the
+ GATT service hierarchy for each GattManager1 object.
+ */
+ GDBusProxy * const manager = G_DBUS_PROXY(l->data);
+ char const * const path = g_dbus_proxy_get_object_path(manager);
+
+ /*
+ The return value will actually be a pointer to a substring
+ starting with the '/' before the HCI name. We add 1 later
+ on to skip that character.
+ */
+ char const * const hci_name = strrchr(path, '/');
+
+ if (hci_name == NULL
+ || !CAGattServiceInitialize(service, context, hci_name + 1))
+ {
+ g_list_free_full(gatt_services,
+ CAPeripheralDestroyGattServices);
+ g_list_free_full(gatt_managers, g_object_unref);
+ return result;
+ }
+
+ service->gatt_manager = manager;
+
+ /*
+ The GattManager1 proxies are now owned by the CAGattService
+ objects.
+ */
+ GList * const tmp = l;
+ l = l->next;
+ gatt_managers = g_list_delete_link(gatt_managers, tmp);
+
+ // Prepend for efficiency.
+ gatt_services = g_list_prepend(gatt_services, service);
+ }
+
+ result = gatt_services;
+
+ return result;
+}
+
+static bool CAPeripheralRegisterGattServices(
+ CAPeripheralContext * context)
+{
+ assert(context != NULL);
+
+ bool success = false;
+
+ ca_mutex_lock(context->lock);
+
+ for (GList * l = context->gatt_services; l != NULL; l = l->next)
+ {
+ CAGattService * const service = l->data;
+
+ // Register the OIC service with the corresponding BlueZ Gatt
+ // Manager.
+
+ /*
+ org.bluez.GattManager1.RegisterService() accepts two
+ parameters: the service object path, and an options
+ dictionary. No options are used so pass a NULL pointer to
+ reflect an empty dictionary.
+ */
+ GVariant * const parameters =
+ g_variant_new("(oa{sv})", service->object_path, NULL);
+
+ GError * error = NULL;
+
+ GVariant * const ret =
+ g_dbus_proxy_call_sync(
+ service->gatt_manager,
+ "RegisterService",
+ parameters,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, // timeout (default == -1),
+ NULL, // cancellable
+ &error);
+
+ if (ret == NULL)
+ {
+ OIC_LOG_V(ERROR,
+ TAG,
+ "GATT service registration failed: %s",
+ error->message);
+
+ g_error_free(error);
+
+ success = false;
+
+ break;
+ }
+
+ g_variant_unref(ret);
+ }
+
+ ca_mutex_unlock(context->lock);
+
+ success = true;
+
+ return success;
+}
+
+static bool CAPeripheralRegisterAdvertisements(
+ CAPeripheralContext * context)
+{
+ bool success = false;
+
+ /*
+ Register the OIC LE advertisement service with each BlueZ
+ LE Advertisement Manager.
+ */
+
+ ca_mutex_lock(context->lock);
+
+ char const * const advertisement_path =
+ g_dbus_interface_skeleton_get_object_path(
+ G_DBUS_INTERFACE_SKELETON(
+ context->advertisement.advertisement));
+
+ GList * managers = context->advertisement.managers;
+
+ for (GList * l = managers; l != NULL; )
+ {
+ GDBusProxy * const manager = G_DBUS_PROXY(l->data);
+
+ /**
+ * @c org.bluez.LEAdvertisingManager1.RegisterService()
+ * accepts two parameters: the advertisement object path, and
+ * an options dictionary. No options are used so pass a NULL
+ * pointer to reflect an empty dictionary.
+ *
+ * @todo The parameters don't change between loop iterations.
+ * Ideally we should initialize this variable before
+ * the loop is executed, but g_dbus_proxy_call_sync()
+ * takes ownership of the variant. Is there anyway to
+ * create a non-floating GVariant and pass it to that
+ * function?
+ */
+ GVariant * const parameters =
+ g_variant_new("(oa{sv})", advertisement_path, NULL);
+
+ GError * error = NULL;
+
+ GVariant * const ret =
+ g_dbus_proxy_call_sync(
+ manager,
+ "RegisterAdvertisement",
+ parameters,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, // timeout (default == -1),
+ NULL, // cancellable
+ &error);
+
+ if (ret == NULL)
+ {
+ OIC_LOG_V(WARNING,
+ TAG,
+ "LE advertisement registration on %s failed: %s",
+ g_dbus_proxy_get_object_path(manager),
+ error->message);
+
+ g_error_free(error);
+
+ // We can't use the LE advertising manager. Drop it.
+ g_object_unref(manager);
+
+ GList * const tmp = l;
+ l = l->next;
+ managers = g_list_delete_link(managers, tmp);
+
+ continue;
+ }
+
+ g_variant_unref(ret);
+
+ /*
+ Note that we can do this in the for-statement because of
+ the similar code in the error case above.
+ */
+ l = l->next;
+ }
+
+ // Use the updated list of managers.
+ context->advertisement.managers = managers;
+
+ if (managers == NULL) // Empty
+ {
+ OIC_LOG(ERROR,
+ TAG,
+ "LE advertisment registration failed for all "
+ "Bluetooth adapters.");
+ }
+ else
+ {
+
+ success = true;
+ }
+
+ ca_mutex_unlock(context->lock);
+
+ return success;
+}
+
+static void CAPeripheralSetDiscoverable(gpointer data,
+ gpointer user_data,
+ gboolean discoverable)
+{
+ assert(data != NULL);
+ assert(user_data != NULL);
+
+ GDBusProxy * const adapter = G_DBUS_PROXY(data);
+ CAResult_t * const result = (CAResult_t *) user_data;
+ *result = CA_STATUS_FAILED;
+
+ /*
+ Make sure the adapter is powered on before making it
+ discoverable.
+ */
+ if (!CASetBlueZObjectProperty(adapter,
+ BLUEZ_ADAPTER_INTERFACE,
+ "Powered",
+ g_variant_new_boolean(discoverable)))
+ {
+ OIC_LOG_V(ERROR,
+ TAG,
+ "Unable to power %s LE peripheral adapter.",
+ discoverable ? "on" : "off");
+
+ return;
+ }
+
+ /**
+ * @note Enabling LE advertising used to be done here using the
+ * kernel bluetooth management API. However, we now
+ * leverage the BlueZ LE Advertisment D-Bus API instead
+ * since it handles all of the desired advertising
+ * operations without need of the calling process to have
+ * @c CAP_NET_ADMIN capabilities. Advertisment registration
+ * is performed in this source file.
+ */
+
+ *result = CA_STATUS_OK;
+}
+
+static void CAPeripheralMakeDiscoverable(gpointer adapter,
+ gpointer result)
+{
+ CAPeripheralSetDiscoverable(adapter, result, TRUE);
+}
+
+static void CAPeripheralMakeUndiscoverable(gpointer adapter,
+ gpointer result)
+{
+ CAPeripheralSetDiscoverable(adapter, result, FALSE);
+}
+
+static CAResult_t CAPeripheralSetDiscoverability(
+ CALEContext * context,
+ GFunc discoverability_func)
+{
+ CAResult_t result = CA_STATUS_FAILED;
+
+ /*
+ Synchronize access to the adapter information using the base
+ context lock since we don't own the adapter_infos.
+ */
+ ca_mutex_lock(context->lock);
+
+ // Make all detected adapters discoverable.
+ g_list_foreach(context->adapters,
+ discoverability_func,
+ &result);
+
+ ca_mutex_unlock(context->lock);
+
+ return result;
+}
+
+/**
+ * Callback function invoked when a D-Bus bus name is acquired.
+ *
+ * @param[in] connection The D-Bus connection on which the name was
+ * acquired.
+ * @param[in] name The bus name that was acquired.
+ * @param[in] user_data User-provided data.
+ */
+static void CAPeripheralOnNameAcquired(GDBusConnection * connection,
+ gchar const * name,
+ gpointer user_data)
+{
+ OIC_LOG_V(DEBUG,
+ TAG,
+ "Name \"%s\" acquired on D-Bus.", name);
+}
+
+/**
+ * Callback function invoked when a D-Bus bus name is no longer owned
+ * or the D-Bus connection has been closed.
+ *
+ * @param[in] connection The D-Bus connection on which the bus name
+ * should have been acquired.
+ * @param[in] name The bus name that was not acquired.
+ * @param[in] user_data User-provided data.
+ */
+static void CAPeripheralOnNameLost(GDBusConnection * connection,
+ gchar const * name,
+ gpointer user_data)
+{
+ /*
+ This can happen if the appropriate D-Bus policy is not
+ installed, for example.
+ */
+ OIC_LOG_V(WARNING,
+ TAG,
+ "Lost name \"%s\" on D-Bus!", name);
+}
+
+static void CAPeripheralStartEventLoop(void * data)
+{
+ CALEContext * const context = data;
+
+ assert(context != NULL);
+
+ // Create the event loop.
+ GMainContext * const loop_context = g_main_context_new();
+ GMainLoop * const event_loop = g_main_loop_new(loop_context, FALSE);
+
+ g_main_context_push_thread_default(loop_context);
+
+ // Acquire the bus name after exporting our D-Bus objects.
+ guint const owner_id =
+ g_bus_own_name_on_connection(context->connection,
+ CA_DBUS_GATT_SERVICE_NAME,
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ CAPeripheralOnNameAcquired,
+ CAPeripheralOnNameLost,
+ NULL, // user_data,
+ NULL);
+
+ /**
+ * Create proxies to the @c org.bluez.LEAdvertisingManager1 D-Bus
+ * objects that will later be used to register the OIC LE
+ * advertisement data.
+ *
+ * @todo Failure to retrieve the LE advertising managers is
+ * currently ignored. We should propagate the failure to
+ * the thread that spawned this one.
+ *
+ * @note Retrieval of the @c org.bluez.LEAdvertisingManager1
+ * proxies must be done in a thread separate from the one
+ * that makes calls through those proxies since the
+ * underlying GDBusObjectManagerClient sets up signal
+ * subscriptions that are used when dispatching D-Bus method
+ * handling calls (e.g. property retrieval, etc).
+ * Otherwise, a distributed deadlock situation could occur
+ * if a synchronous D-Bus proxy call is made that causes the
+ * recipient (like BlueZ) to call back in to the thread that
+ * handles signals. For example, registration of our LE
+ * advertisment with BlueZ causes BlueZ itself to make a
+ * call to our own @c org.bluez.LEAdvertisement1 object.
+ * However, the thread that initiated the advertisement
+ * registration is blocked waiting for BlueZ to respond, but
+ * BlueZ is blocked waiting for that same thread to respond
+ * to its own advertisement property retrieval call.
+ */
+ GList * advertising_managers = NULL;
+ if (!CAGetBlueZManagedObjectProxies(
+ &advertising_managers,
+ BLUEZ_ADVERTISING_MANAGER_INTERFACE,
+ context,
+ NULL))
+ {
+ OIC_LOG(ERROR,
+ TAG,
+ "Failed to retrieve BlueZ LE advertising "
+ "manager interface.");
+ }
+
+ /**
+ * Initialize all GATT services.
+ *
+ * @todo Failure to initialize the OIC GATT services is currently
+ * ignored. We should propagate the failure to the thread
+ * that spawned this one.
+ *
+ * @note See the @c org.bluez.LEAdvertisingManager1 note above to
+ * understand why the GATT services must be initialized in
+ * a thread seperate from the one that initiates GATT
+ * service registration.
+ */
+ GList * const gatt_services =
+ CAPeripheralInitializeGattServices(context);
+
+ ca_mutex_lock(g_context.lock);
+
+ assert(g_context.event_loop == NULL);
+ g_context.event_loop = event_loop;
+
+ g_context.base = context;
+
+ g_context.owner_id = owner_id;
+
+ CALEAdvertisementInitialize(&g_context.advertisement,
+ context->connection,
+ advertising_managers);
+
+ g_context.gatt_services = gatt_services;
+
+ ca_mutex_unlock(g_context.lock);
+
+ ca_cond_signal(g_context.condition);
+
+ g_main_loop_run(event_loop);
+}
+
+static void CAPeripheralStopEventLoop(CAPeripheralContext * context)
+{
+ ca_mutex_lock(context->lock);
+
+ GMainLoop * const event_loop = context->event_loop;
+ context->event_loop = NULL;
+
+ ca_mutex_unlock(context->lock);
+
+ if (event_loop != NULL)
+ {
+ g_main_loop_quit(event_loop);
+
+ GMainContext * const loop_context =
+ g_main_loop_get_context(event_loop);
+
+ if (loop_context != NULL)
+ {
+ g_main_context_wakeup(loop_context);
+ g_main_context_unref(loop_context);
+ }
+
+ g_main_loop_unref(event_loop);
+ }
+}
+
+// ------------------------------------------------------
+
+void CAPeripheralInitialize()
+{
+ g_context.lock = ca_mutex_new();
+ g_context.condition = ca_cond_new();
+}
+
+void CAPeripheralFinalize()
+{
+ ca_cond_free(g_context.condition);
+ ca_mutex_free(g_context.lock);
+}
+
+CAResult_t CAPeripheralStart(CALEContext * context)
+{
+ /**
+ * @todo Bluetooth adapters that are hot-plugged after the
+ * peripheral has started will not be started!
+ */
+
+ CAResult_t result = CA_STATUS_FAILED;
+
+ // Only start if we were previously stopped.
+ if (CAPeripheralCheckStarted())
+ {
+ result = CA_SERVER_STARTED_ALREADY;
+ return result;
+ }
+
+ if (!CAPeripheralAdaptersFound(context))
+ {
+ // No Bluetooth adapters. Don't bother continuing.
+ return result;
+ }
+
+ /*
+ Spawn a thread to run the Glib event loop that will drive D-Bus
+ signal handling.
+ */
+ result = ca_thread_pool_add_task(context->server_thread_pool,
+ CAPeripheralStartEventLoop,
+ context);
+
+ if (result != CA_STATUS_OK)
+ {
+ return result;
+ }
+
+ /*
+ Wait until initialization completes before proceeding to
+ service and advertisement registration.
+ */
+
+ // Number of times to wait for initialization to complete.
+ static int const max_retries = 2;
+
+ static uint64_t const timeout =
+ 2 * MICROSECS_PER_SEC; // Microseconds
+
+ ca_mutex_lock(g_context.lock);
+
+ for (int i = 0;
+ g_context.gatt_services == NULL && i < max_retries;
+ ++i)
+ {
+ if (ca_cond_wait_for(g_context.condition,
+ g_context.lock,
+ timeout) == 0)
+ {
+ result = CA_STATUS_OK;
+ }
+ }
+
+ ca_mutex_unlock(g_context.lock);
+
+ if (result == CA_STATUS_FAILED)
+ {
+ return result;
+ }
+
+ /**
+ * First register the GATT services, then register the LE
+ * advertisments with BlueZ to make sure the service we're
+ * advertising actually exists.
+ */
+ if (result == CA_STATUS_OK
+ && !(CAPeripheralRegisterGattServices(context)
+ && CAPeripheralRegisterAdvertisements(&g_context)))
+ {
+ result = CA_STATUS_FAILED;
+ }
+
+ /*
+ Make the local bluetooth adapters discoverable over LE by
+ enabling LE, enabling advertising, and making the LE device
+ connectable.
+ */
+ result = CAPeripheralSetDiscoverability(context,
+ CAPeripheralMakeDiscoverable);
+
+ return result;
+}
+
+CAResult_t CAPeripheralStop()
+{
+ CAResult_t result = CA_STATUS_FAILED;
+
+ // Only stop if we were previously started.
+ if (!CAPeripheralCheckStarted())
+ {
+ result = CA_STATUS_OK;
+ return result;
+ }
+
+ /*
+ Make the local bluetooth adapters undiscoverable.
+
+ This function also sets the base context to NULL.
+ */
+ result =
+ CAPeripheralSetDiscoverability(g_context.base,
+ CAPeripheralMakeUndiscoverable);
+
+ CAPeripheralStopEventLoop(&g_context);
+
+ ca_mutex_lock(g_context.lock);
+
+ guint const owner_id = g_context.owner_id;
+ g_context.owner_id = 0;
+
+ GList * const gatt_services = g_context.gatt_services;
+ g_context.gatt_services = NULL;
+
+ g_context.base = NULL;
+
+ ca_mutex_unlock(g_context.lock);
+
+ CALEAdvertisementDestroy(&g_context.advertisement);
+
+ g_list_free_full(gatt_services, CAPeripheralDestroyGattServices);
+
+ g_bus_unown_name(owner_id);
+
+ return result;
+}
--- /dev/null
+/******************************************************************
+ *
+ * Copyright 2015 Intel Corporation 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.
+ *
+ ******************************************************************/
+
+#ifndef CA_BLE_LINUX_PERIPHERAL_H
+#define CA_BLE_LINUX_PERIPHERAL_H
+
+#include "context.h"
+#include "advertisement.h"
+
+#include "cacommon.h"
+
+
+/**
+ * @internal
+ *
+ * Linux BLE "peripheral" context.
+ */
+typedef struct _CAPeripheralContext
+{
+ /**
+ * Base context.
+ *
+ * The base context contains core state used throughout the Linux
+ * BLE adapter implementation.
+ */
+ CALEContext * base;
+
+ /// D-Bus bus name owner ID.
+ guint owner_id;
+
+ /**
+ * LE advertising information.
+ *
+ * The LE advertising information will be registered with BlueZ,
+ * which BlueZ will then use to add advertising data to each of
+ * the detected Bluetooth hardware adapters. Only one set of
+ * advertising data is needed since the data is the same for all
+ * adapters.
+ */
+ CALEAdvertisement advertisement;
+
+ /**
+ * List of @c CAGattService objects containing information for all
+ * exported and registered IoTivity BlueZ GATT related D-Bus
+ * objects.
+ */
+ GList * gatt_services;
+
+ /**
+ * Glib event loop that drives peripheral D-Bus signal
+ * handling.
+ *
+ * @note A seperate thread drives this event loop. This is
+ * necessitated by the need for signal subscriptions to be
+ * done in what the GLib documentation refers to as the
+ * "thread-default main context". By the time the
+ * peripheral is started it's too late the use the main loop
+ * that was run when this Linux BLE transport adapter itself
+ * was started through @c CASelectNetwork() and the @c
+ * CAAdapterStart() callback the peripheral must have its
+ * own main loop.
+ */
+ GMainLoop * event_loop;
+
+ /// Mutex used to synchronize access to context fields.
+ ca_mutex lock;
+
+ /**
+ * Service registration condition variable.
+ *
+ * This condition variable is used to delay service registration
+ * until the thread performing service initialization completes.
+ * Initialization is performed in the same thread that will run
+ * the peripheral's event loop.
+ *
+ * @see @c GMainLoop documentation for further details.
+ */
+ ca_cond condition;
+
+} CAPeripheralContext;
+
+/**
+ * Initialize global state.
+ */
+void CAPeripheralInitialize();
+
+/**
+ * Finalize global state.
+ */
+void CAPeripheralFinalize();
+
+/**
+ * Initialize and start a Linux BLE "peripheral".
+ *
+ * Initialize all Linux BLE "peripheral" state (i.e. a global
+ * @c CAPeripheralContext instance), as well as register the OIC
+ * GATT transport service with BlueZ to begin advertising.
+ *
+ * @param[in] context Base context.
+ *
+ * @return @c CA_STATUS_OK on success.
+ */
+CAResult_t CAPeripheralStart(CALEContext * context);
+
+/**
+ * Stop the Linux BLE "peripheral".
+ *
+ * @return @c CA_STATUS_OK on success.
+ */
+CAResult_t CAPeripheralStop();
+
+
+#endif /* CA_BLE_LINUX_PERIPHERAL_H */
--- /dev/null
+/******************************************************************
+ *
+ * Copyright 2015 Intel Corporation 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.
+ *
+ ******************************************************************/
+
+#include "recv.h"
+
+#include "caremotehandler.h"
+#include "cafragmentation.h"
+#include "oic_malloc.h"
+#include "oic_string.h"
+
+#include <string.h>
+#include <assert.h>
+
+
+// Logging tag.
+static char const TAG[] = "BLE_RECV";
+
+static CAGattRecvInfo const g_null_info =
+ {
+ .peer = NULL
+ };
+
+void CAGattRecvInfoInitialize(CAGattRecvInfo * info)
+{
+ *info = g_null_info;
+}
+
+void CAGattRecvInfoDestroy(CAGattRecvInfo * info)
+{
+ OICFree(info->peer);
+ *info = g_null_info;
+}
+
+bool CAGattRecv(CAGattRecvInfo * info, char const * data, uint32_t length)
+{
+ uint32_t sent_length = 0;
+
+ ca_mutex_lock(info->context->lock);
+
+ bool const success =
+ info->on_packet_received(info->peer,
+ data,
+ length,
+ &sent_length) == CA_STATUS_OK;
+
+ ca_mutex_unlock(info->context->lock);
+
+ return success && length == sent_length;
+}
--- /dev/null
+/******************************************************************
+ *
+ * Copyright 2015 Intel Corporation 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.
+ *
+ ******************************************************************/
+
+#ifndef CA_BLE_LINUX_RECV_H
+#define CA_BLE_LINUX_RECV_H
+
+#include "context.h"
+
+#include "caleinterface.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+
+/**
+ * Information used to keep track of received data fragments.
+ */
+typedef struct _CAGattRecvInfo
+{
+
+ /**
+ * @name Peer Connection-specific Fields
+ *
+ * These fields are valid only as long as the peer is connected.
+ */
+ //@{
+ /// Peer address.
+ char * peer;
+
+ /// Callback invoked upon receiving all data from GATT peer.
+ CABLEDataReceivedCallback on_packet_received;
+
+ /**
+ * Context object containing lock used for synchronized access to
+ * the @c on_packet_received callback since that callback is actually
+ * owned by it.
+ */
+ CALEContext * context;
+ //@}
+
+} CAGattRecvInfo;
+
+/**
+ * Initialize a @c CAGattRecvInfo object.
+ *
+ * @param[in] info Pointer to @c CAGattRecvInfo object being
+ * initialized. No memory is allocated by this
+ * function. The caller is responsible for
+ * instantiating the object prior to calling this
+ * function.
+ */
+void CAGattRecvInfoInitialize(CAGattRecvInfo * info);
+
+/**
+ * Destroy a @c CAGattRecvInfo object.
+ *
+ * Destruction of @a info involves deallocating and clearing out all
+ * fields, as necessary.
+ *
+ * @param[in] info Pointer to @c CAGattRecvInfo object being
+ * destroyed. Only the @a info fields are finalized.
+ * Memory of @a info itself is retained by the
+ * caller.
+ */
+void CAGattRecvInfoDestroy(CAGattRecvInfo * info);
+
+/**
+ * Handle data received from GATT a peer.
+ *
+ * @param[in] info Information required to complete the receive
+ * operation.
+ * @param[in] data Octet array containing received data
+ * fragment.
+ * @param[in] length Length of the @a data array.
+ *
+ * @return @c true on success, @c false otherwise.
+ */
+bool CAGattRecv(CAGattRecvInfo * info,
+ char const * data,
+ uint32_t length);
+
+
+#endif /* CA_BLE_LINUX_RECV_H */
--- /dev/null
+/******************************************************************
+ *
+ * Copyright 2015 Intel Corporation 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.
+ *
+ ******************************************************************/
+
+#include "server.h"
+
+#include "cacommon.h"
+#include "logger.h"
+
+#include <assert.h>
+
+
+// Logging tag.
+static char const TAG[] = "BLE_SERVER";
+
+// ---------------------------------------------------------------------
+// GATT Request Handling
+// ---------------------------------------------------------------------
+void CAGattServerHandleRequestData()
+{
+}
+
+// ---------------------------------------------------------------------
+// GATT Response Handling
+// ---------------------------------------------------------------------
+/**
+ * Send response data to the GATT client.
+ *
+ * Respone data will be sent to the client through the given response
+ * @a characteristic proxy as a GATT characteristic notification.
+ *
+ * @param[in] characteristic The D-Bus proxy for the response
+ * characteristic through which the
+ * notification will be sent.
+ * @param[in] data The byte array to be sent.
+ * @param[in] length The number of elements in the byte
+ * array.
+ */
+static bool CAGattServerSendResponseNotification(
+ GattCharacteristic1 * characteristic,
+ char const * data,
+ size_t length)
+{
+ if (!gatt_characteristic1_get_notifying(characteristic))
+ {
+ OIC_LOG(WARNING,
+ TAG,
+ "Attempt to send response with notifications "
+ "disabled.\n"
+ "Client must enable notifications. "
+ "No response was sent.");
+
+ return false;
+ }
+
+ GVariant * const value =
+ g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,
+ data,
+ length,
+ sizeof(data[0]));
+
+ /**
+ * Send the response fragment by setting the "Value" property on
+ * the response characteristic, and emitting the
+ * @c org.freedesktop.Dbus.Properties.PropertiesChanged signal,
+ * accordingly.
+ *
+ * @todo Do we need to explicitly emit the @c GObject @c notify or
+ * @c org.freedesktop.Dbus.Properties.PropertiesChanged
+ * signal here?
+ */
+ gatt_characteristic1_set_value(characteristic, value);
+
+ return true;
+}
+
+bool CAGattServerSendResponse(void const * method_info,
+ void const * data,
+ size_t length)
+{
+ assert(method_info != NULL);
+
+ CAGattResponseInfo const * const info = method_info;
+
+ GattCharacteristic1 * const characteristic =
+ info->characteristic;
+
+ if (!CAGattServerSendResponseNotification(characteristic,
+ (char const *) data,
+ length))
+ {
+ return false;
+ }
+
+ return true;
+}
--- /dev/null
+/******************************************************************
+ *
+ * Copyright 2015 Intel Corporation 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.
+ *
+ ******************************************************************/
+
+#ifndef CA_BLE_LINUX_SERVER_H
+#define CA_BLE_LINUX_SERVER_H
+
+#include "bluez-glue.h"
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+/**
+ * Information needed to complete a GATT server response send.
+ */
+typedef struct _CAGattResponseInfo
+{
+ /**
+ * The BlueZ @c org.bluez.GattCharacteristic1 skeleton object
+ * through which data will be sent.
+ */
+ GattCharacteristic1 * const characteristic;
+
+} CAGattResponseInfo;
+
+/**
+ * Send response notification to the GATT client.
+ *
+ * @param[in] method_info Pointer to @c GattResponseInfo object that
+ * contains information necessary to complete
+ * send of response.
+ * @param[in] data Octet array of response data to be sent.
+ * @param[in] length Length of the @a data octet array.
+ *
+ * @see @c CAGattSendMethod() for further details.
+ */
+bool CAGattServerSendResponse(void const * method_info,
+ void const * data,
+ size_t length);
+
+
+#endif /* CA_BLE_LINUX_SERVER_H */
--- /dev/null
+/* ****************************************************************
+ *
+ * Copyright 2015 Intel Corporation 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.
+ *
+ ******************************************************************/
+
+#include "service.h"
+#include "gatt_dbus.h"
+#include "utils.h"
+#include "bluez.h"
+
+#include "cagattservice.h"
+#include "logger.h"
+
+#include <assert.h>
+
+
+// Logging tag.
+static char const TAG[] = "BLE_SERVICE";
+
+static GVariant * CAGattServiceGetProperties(GattService1 * service)
+{
+ /*
+ Create a variant containing the @c GattService1 properties, of
+ the form @c a{sa{sv}}.
+ */
+
+ /**
+ * Populate the property table, and create the variant to be
+ * embedded in the results of the
+ * @c org.freedesktop.Dbus.ObjectManager.GetManagedObjects()
+ * method call.
+ *
+ * The @c "Device" property is only available on the client side
+ * so we don't bother returning it here.
+ *
+ * @todo Do we care about the @c "Includes" property?
+ * @c "Includes" isn't implemented by BlueZ as of version
+ * 5.30, so we can leave it out.
+ */
+ CADBusSkeletonProperty const properties[] = {
+ { "UUID",
+ g_variant_new_string(gatt_service1_get_uuid(service)) },
+ { "Primary",
+ g_variant_new_boolean(gatt_service1_get_primary(service)) },
+ { "Characteristics",
+ g_variant_new_objv(
+ gatt_service1_get_characteristics(service),
+ -1) }
+ };
+
+ return
+ CAMakePropertyDictionary(
+ BLUEZ_GATT_SERVICE_INTERFACE,
+ properties,
+ sizeof(properties) / sizeof(properties[0]));
+}
+
+/**
+ * Implementation of the
+ * @c org.freedesktop.DBus.ObjectManager.GetManagedObjects() method
+ * for the @c org.bluez.GattService1 interface.
+ */
+static gboolean CAGattServiceHandleGetManagedObjects(
+ ObjectManager * object,
+ GDBusMethodInvocation * invocation,
+ gpointer user_data)
+{
+ /**
+ * @note Ideally we shouldn't need this implementation, and should
+ * be able to simply rely GDBusObjectManagerServer instead.
+ * Unfortunately, BlueZ expects the @c
+ * org.bluez.GattService1 object to implement the @c
+ * ObjectManager interface, and both interfaces must rooted
+ * at the same object path, as well. That requirement
+ * prevents us from using @c GDBusObjectManagerServer since
+ * it won't allow us to export more than interface on a
+ * given object path.
+ */
+
+ /*
+ Build the object array containing the IoTivity
+ org.bluez.GattService1 hierarchy.
+
+ a{oa{sa{sv}}}
+ */
+ CAGattService * const service = user_data;
+
+ GVariantBuilder builder;
+ g_variant_builder_init(&builder, G_VARIANT_TYPE("a{oa{sa{sv}}}"));
+
+ // Start out with the service itself.
+ g_variant_builder_add(&builder,
+ "{o@a{sa{sv}}}",
+ service->object_path,
+ CAGattServiceGetProperties(service->service));
+
+ /*
+ Add the request characteristic and user description
+ descriptor.
+ */
+ CAGattCharacteristic * const request_chrc =
+ &service->request_characteristic;
+
+ g_variant_builder_add(&builder,
+ "{o@a{sa{sv}}}",
+ request_chrc->object_path,
+ CAGattCharacteristicGetProperties(
+ request_chrc->characteristic));
+
+ CAGattDescriptor * const request_desc = &request_chrc->descriptor;
+
+ g_variant_builder_add(&builder,
+ "{o@a{sa{sv}}}",
+ request_desc->object_path,
+ CAGattDescriptorGetProperties(
+ request_desc->descriptor));
+
+ /*
+ Add the response characteristic and user description
+ descriptor.
+ */
+ CAGattCharacteristic * const response_chrc =
+ &service->response_characteristic;
+
+ g_variant_builder_add(&builder,
+ "{o@a{sa{sv}}}",
+ response_chrc->object_path,
+ CAGattCharacteristicGetProperties(
+ response_chrc->characteristic));
+
+ CAGattDescriptor * const response_desc = &response_chrc->descriptor;
+
+ g_variant_builder_add(&builder,
+ "{o@a{sa{sv}}}",
+ response_desc->object_path,
+ CAGattDescriptorGetProperties(
+ response_desc->descriptor));
+
+ GVariant * const objects = g_variant_builder_end(&builder);
+
+ object_manager_complete_get_managed_objects(object,
+ invocation,
+ objects);
+
+ return TRUE;
+}
+
+bool CAGattServiceInitialize(CAGattService * s,
+ CALEContext * context,
+ char const * hci_name)
+{
+ assert(s != NULL);
+ assert(context != NULL);
+ assert(hci_name != NULL);
+
+ // Path of the form /org/iotivity/gatt/hci0/service0.
+ s->object_path =
+ g_strdup_printf("%s/%s/%s",
+ CA_GATT_SERVICE_ROOT_PATH,
+ hci_name,
+ CA_GATT_SERVICE_PATH);
+
+ assert(g_variant_is_object_path(s->object_path));
+
+ s->object_manager = object_manager_skeleton_new();
+ s->service = gatt_service1_skeleton_new();
+
+ gatt_service1_set_uuid(s->service, CA_GATT_SERVICE_UUID);
+ gatt_service1_set_primary(s->service, TRUE);
+
+ if (!CAGattRequestCharacteristicInitialize(s, context)
+ || !CAGattResponseCharacteristicInitialize(s, context))
+ {
+ CAGattServiceDestroy(s);
+ return false;
+ }
+
+ /*
+ The characteristic object paths are not fixed at compile-time.
+ Retrieve the object paths that were set at run-time.
+ */
+ char const * characteristic_paths[] = {
+ s->request_characteristic.object_path,
+ s->response_characteristic.object_path,
+ NULL
+ };
+
+ gatt_service1_set_characteristics(s->service, characteristic_paths);
+
+ /*
+ Set the org.freedesktop.DBus.ObjectManager.GetManagedObjects()
+ handler for our BlueZ GATT service.
+ */
+ g_signal_connect(
+ s->object_manager,
+ "handle-get-managed-objects",
+ G_CALLBACK(CAGattServiceHandleGetManagedObjects),
+ s);
+
+ /*
+ BlueZ expects both the org.freedesktop.DBus.ObjectManager and
+ org.bluez.GattService1 interfaces to be rooted at the same
+ object path. Export the service and object manager interface
+ skeletons with the same object path.
+ */
+ GError * error = NULL;
+ if (!g_dbus_interface_skeleton_export(
+ G_DBUS_INTERFACE_SKELETON(s->object_manager),
+ context->connection,
+ s->object_path,
+ &error)
+ || !g_dbus_interface_skeleton_export(
+ G_DBUS_INTERFACE_SKELETON(s->service),
+ context->connection,
+ s->object_path,
+ &error))
+ {
+ OIC_LOG_V(ERROR,
+ TAG,
+ "Unable to export GATT service interfaces: %s",
+ error->message);
+
+ return false;
+ }
+
+ return true;
+}
+
+void CAGattServiceDestroy(CAGattService * s)
+{
+ /**
+ * @todo If necessary, emit the
+ * @c org.freedesktop.DBus.ObjectManager.InterfacesRemoved
+ * signal via @c object_manager_emit_interfaces_removed() if
+ * the CA GATT service objects were removed from the
+ * @c ObjectManager.
+ */
+
+ assert(s != NULL); // As designed, s is always non-NULL.
+
+ g_clear_object(&s->gatt_manager);
+
+ CAGattCharacteristicDestroy(&s->response_characteristic);
+ CAGattCharacteristicDestroy(&s->request_characteristic);
+
+ g_clear_object(&s->service);
+ g_clear_object(&s->object_manager);
+
+ g_free(s->object_path);
+ s->object_path = NULL;
+}
--- /dev/null
+/******************************************************************
+ *
+ * Copyright 2015 Intel Corporation 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.
+ *
+ ******************************************************************/
+
+#ifndef CA_BLE_LINUX_SERVICE_H
+#define CA_BLE_LINUX_SERVICE_H
+
+#include "bluez-glue.h"
+#include "object_manager-glue.h"
+#include "characteristic.h"
+
+
+/**
+ * GATT Service Information
+ */
+typedef struct CAGattService
+{
+ /// D-Bus object path for the GattService1 object.
+ char * object_path;
+
+ /**
+ * OIC GATT service object_manager D-Bus interface skeleton
+ * object.
+ */
+ ObjectManager * object_manager;
+
+ /// OIC GATT service D-Bus interface skeleton object.
+ GattService1 * service;
+
+ /// OIC GATT request characteristic information.
+ CAGattCharacteristic request_characteristic;
+
+ /// OIC GATT response characteristic information.
+ CAGattCharacteristic response_characteristic;
+
+ /**
+ * org.bluez.GattManager1 object with which the service is
+ * registered.
+ */
+ GDBusProxy * gatt_manager;
+
+} CAGattService;
+
+/**
+ * Initialize GATT service fields.
+ *
+ * This function initializes the @c CAGattService object fields.
+ *
+ * @param[out] service GATT service information to be initialized.
+ * @param[in] context Object containing the D-Bus connection to the
+ * bus on which the service will be exported.
+ * @param[in] hci_name Name of the bluetooth adapter installed on the
+ * system, e.g. @c "hci0".
+ *
+ * @return @c true on success, @c false otherwise.
+ *
+ * @note This function does not allocate the @a service object
+ * itself. The caller is responsible for allocating that
+ * memory.
+ */
+bool CAGattServiceInitialize(CAGattService * service,
+ CALEContext * context,
+ char const * hci_name);
+
+/**
+ * Destroy GATT service fields.
+ *
+ * This function finalizes the @c CAGattService object fields.
+ *
+ * @param[in] service GATT service information to be finalized.
+ *
+ * @note This function does not deallocate the @a service object
+ * itself. The caller is responsible for deallocating that
+ * memory.
+ */
+void CAGattServiceDestroy(CAGattService * service);
+
+
+#endif // CA_BLE_LINUX_SERVICE_H
--- /dev/null
+/******************************************************************
+ *
+ * Copyright 2015 Intel Corporation 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.
+ *
+ ******************************************************************/
+
+#include "utils.h"
+#include "bluez.h"
+
+#include "logger.h"
+
+#include <assert.h>
+
+
+// Logging tag.
+static char const TAG[] = "BLE_UTILS";
+
+bool CAGetBlueZManagedObjectProxies(GList ** proxies,
+ char const * interface,
+ CALEContext * context,
+ CALEProxyFilter filter)
+{
+ assert(interface != NULL);
+ assert(proxies != NULL);
+ assert(context != NULL);
+
+ /*
+ Failure is only reported if an error occurred. An empty
+ returned list, for example, is not an error since it could
+ be updated at a later time when handling D-Bus signals related
+ to the given interface.
+ */
+ bool success = true;
+
+ ca_mutex_lock(context->lock);
+
+ if (context->objects == NULL)
+ {
+ ca_mutex_unlock(context->lock);
+ return success;
+ }
+
+ /*
+ Iterate over the objects to find those that implement the given
+ BlueZ interface and them to the given list.
+ */
+ for (GList * l = context->objects; l != NULL; l = l->next)
+ {
+ GDBusProxy * const proxy =
+ G_DBUS_PROXY(g_dbus_object_get_interface(
+ G_DBUS_OBJECT(l->data),
+ interface));
+
+ if (proxy != NULL)
+ {
+ if (filter == NULL || filter(proxy))
+ {
+ /*
+ Add the object information to the list.
+
+ Note that we prepend instead of append in this case
+ since it is more efficient to do so for linked lists
+ like the one used here.
+ */
+ *proxies = g_list_prepend(*proxies, proxy);
+ }
+ else
+ {
+ // Rejected by filter.
+ g_object_unref(proxy);
+ }
+ }
+ }
+
+ ca_mutex_unlock(context->lock);
+
+ return success;
+}
+
+GDBusProxy * CAGetBlueZInterfaceProxy(GVariant * tuple,
+ char const * interface,
+ GDBusObjectManager * object_manager)
+{
+ /*
+ The tuple is of the form "(oa{sv})".
+
+ The object path is first tuple element, and the properties the
+ second. Check if the given interface exists in the properties.
+ Return the object path (the dictionary item key) of the found
+ property (the dictionary item value).
+ */
+ GVariant * const props = g_variant_get_child_value(tuple, 1);
+
+ GVariant * const value =
+ g_variant_lookup_value(props, interface, NULL);
+
+ GDBusProxy * proxy = NULL;
+
+ if (value != NULL)
+ {
+ /*
+ A set of properties corresponding to the given D-Bus
+ interface was found. Create a proxy to the object that
+ implements that interface. Store that proxy, the D-Bus
+ object path and properties in the given list.
+ */
+
+ gchar const * path = NULL;
+
+ // The object path is the first tuple element.
+ g_variant_get_child(tuple, 0, "&o", &path);
+
+ /*
+ Create a proxy to the object that implements the given
+ interface.
+ */
+ proxy =
+ G_DBUS_PROXY(
+ g_dbus_object_manager_get_interface(object_manager,
+ path,
+ interface));
+
+ g_variant_unref(value);
+ }
+
+ g_variant_unref(props);
+
+ return proxy;
+}
+
+bool CASetBlueZObjectProperty(GDBusProxy * proxy,
+ char const * interface,
+ char const * property,
+ GVariant * value)
+{
+ /*
+ Only make the D-Bus call to set the property if we need to
+ change it.
+ */
+ GVariant * const cached_value =
+ g_dbus_proxy_get_cached_property(proxy, property);
+
+ if (cached_value != NULL)
+ {
+ bool const already_set = g_variant_equal(cached_value, value);
+
+ g_variant_unref(cached_value);
+
+ if (already_set)
+ {
+ /*
+ Ownership of the value GVariant was transferred when
+ this function was called. It won't be used since the
+ property with the same value is already set, so release
+ our reference to it.
+ */
+ g_variant_unref(value);
+
+ return true;
+ }
+ }
+
+ /*
+ Either the property wasn't previously set or it is being
+ changed. Set property on the given interface.
+ */
+ GError * error = NULL;
+
+ GVariant * const ret =
+ g_dbus_proxy_call_sync(proxy,
+ "org.freedesktop.DBus.Properties.Set",
+ g_variant_new("(ssv)",
+ interface,
+ property,
+ value),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, // timeout (default == -1),
+ NULL, // cancellable
+ &error);
+
+ if (ret == NULL)
+ {
+ OIC_LOG_V(ERROR,
+ TAG,
+ "Attempt to set \"%s\" property for "
+ "\"%s\" interface failed.: %s",
+ error->message);
+
+ g_error_free(error);
+
+ return false;
+ }
+
+ g_variant_unref(ret);
+
+ return true;
+}
+
+GVariant * CAMakePropertyDictionary(
+ char const * interface_name,
+ CADBusSkeletonProperty const * properties,
+ size_t count)
+{
+ /*
+ Create a variant containing the proxy properties, of the form
+ a{sa{sv}}.
+ */
+
+ GVariantBuilder builder;
+
+ // Create the inner (property) dictionary.
+ g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
+
+ CADBusSkeletonProperty const * const end =
+ properties + count;
+
+ for (CADBusSkeletonProperty const * prop = properties;
+ prop != end;
+ ++prop)
+ {
+ g_variant_builder_add(&builder, "{sv}", prop->name, prop->value);
+ }
+
+ GVariant * const props = g_variant_builder_end(&builder);
+
+ // Now create the dictionary with the above property dictionary
+ // embedded.
+ g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sa{sv}}"));
+
+ g_variant_builder_add(&builder, "{s@a{sv}}", interface_name, props);
+
+ return g_variant_builder_end(&builder);
+}
--- /dev/null
+/******************************************************************
+ *
+ * Copyright 2015 Intel Corporation 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.
+ *
+ ******************************************************************/
+
+#ifndef CA_BLE_LINUX_UTILS_H
+#define CA_BLE_LINUX_UTILS_H
+
+#include "context.h"
+
+
+/**
+ * Proxy retrieval filter function type.
+ *
+ * A function that implements this interface may be passed to
+ * @c CAGetBlueZManagedObjectProxies() to filter proxies to be added
+ * to the returned list.
+ *
+ * @return @c true if the proxy should be added to the proxy list,
+ * @c false otherwise.
+ */
+typedef bool(*CALEProxyFilter)(GDBusProxy * proxy);
+
+/**
+ * Get information for all BlueZ managed objects with a given
+ * interface.
+ *
+ * @param[out] proxies List containing @a proxies (@c GDBusProxy)
+ * for all found BlueZ objects that match the
+ * given D-Bus @a interface.
+ * @param[in] interface D-Bus interface of BlueZ object for which a
+ * proxy will be created.
+ * @param[in] context BLE Linux adapter context.
+ * @param[in] filter Filter function used to determine whether or
+ * not a proxy retrieved from the BlueZ
+ * @c ObjectManager should be added to the
+ * returned @a proxies list.
+ *
+ * @return @c true if objects were found.
+ */
+bool CAGetBlueZManagedObjectProxies(GList ** proxies,
+ char const * interface,
+ CALEContext * context,
+ CALEProxyFilter filter);
+
+
+/**
+ * Get the proxy to a object that implements the given D-Bus
+ * @a interface.
+ *
+ * @param[in] tuple A D-Bus tuple that contains the D-Bus object
+ * path and corresponding dictionary of
+ * properties, i.e. with the format specifier
+ * @c "(oa{sv})".
+ * @param[in] interface The D-Bus interface in which we're
+ * interested. A @c GDBusProxy object will be
+ * create using the properties found in the
+ * dictionary item whose key matches this
+ * interface name.
+ * @param[in] manager The D-Bus object manager that contains the
+ * information about the object in question.
+ */
+GDBusProxy * CAGetBlueZInterfaceProxy(GVariant * tuple,
+ char const * interface,
+ GDBusObjectManager * object_manager);
+
+/**
+ * Set @a property on BlueZ object to given @a value.
+ *
+ * Using the @c org.freedesktop.DBus.Properties.Set() method
+ * implemented by BlueZ objects, set the desired @a property on the
+ * object pointed to by @a proxy to the given @a value.
+ *
+ * @param[in] proxy D-Bus proxy to the BlueZ object.
+ * @param[in] interface Interface name of the object on which the
+ * property is being set.
+ * @param[in] property Property name.
+ * @param[in] value Property value. Ownership is transferred from
+ * the caller.
+ */
+bool CASetBlueZObjectProperty(GDBusProxy * proxy,
+ char const * interface,
+ char const * property,
+ GVariant * value);
+
+/**
+ * D-Bus skeleton propery name/value pair.
+ *
+ * This name value pair will be used when constructing the property
+ * dictionary embedded in
+ * @c org.freedesktop.DBus.ObjectManager.GetManagedObjects() results.
+ */
+typedef struct _CADBusSkeletonProperty
+{
+ /// Property name.
+ char const * const name;
+
+ /**
+ * Property value.
+ *
+ * @c Ownership is transferred to the function this variant passed
+ * to.
+ */
+ GVariant * const value;
+} CADBusSkeletonProperty;
+
+/**
+ * Construct property dictionary suitable for place in ObjectManager
+ * results.
+ *
+ * @param[in] interface_name The name of the interface to which the
+ * properties correspond.
+ * @param[in] properties Array of property name/value pairs.
+ * Ownership of the value variant will be
+ * transferred to this function.
+ * @param[in] count Number of elements in the @a properties
+ * array.
+ *
+ * @return A variant of the form a{sa{sv}}, suitable for use in the
+ * results of
+ * @c org.freedesktop.DBus.ObjectManager.GetManagedObjects()
+ * or @c org.freedesktop.DBus.Properties.GetAll() method
+ * implementations.
+ *
+ * @note Ownership of the returned @c GVariant is transferred to the
+ * caller.
+ */
+GVariant * CAMakePropertyDictionary(
+ char const * interface_name,
+ CADBusSkeletonProperty const * properties,
+ size_t count);
+
+
+#endif // CA_BLE_LINUX_UTILS_H
#include "caqueueingthread.h"
#include "caadapterutils.h"
#include "cafragmentation.h"
+#include "cagattservice.h"
#include "oic_string.h"
#include "oic_malloc.h"
#define TZ_BLE_CLIENT_TAG "TZ_BLE_GATT_CLIENT"
/**
- * Its the constant value for characteristic descriptor from spec.
- */
-#define BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG "2902"
-
-/**
- * This contains the list of OIC services a client connect tot.
+ * @var g_bLEServiceList
+ * @brief This contains the list of OIC services a client connect to.
*/
static BLEServiceList *g_bLEServiceList = NULL;
* Maintains the callback to be notified on receival of network packets
* from other BLE devices
*/
-static CABLEClientDataReceivedCallback g_bleClientDataReceivedCallback = NULL;
+static CABLEDataReceivedCallback g_bleClientDataReceivedCallback = NULL;
/**
* callback to update the error to le adapter
ca_mutex_lock(g_bleServerBDAddressMutex);
uint32_t sentLength = 0;
- g_bleClientDataReceivedCallback(g_remoteAddress, OIC_BLE_SERVICE_ID,
- value, valueLen, &sentLength);
+ g_bleClientDataReceivedCallback(g_remoteAddress, value, valueLen,
+ &sentLength);
OIC_LOG_V(DEBUG, TZ_BLE_CLIENT_TAG, "Sent data Length is %d", sentLength);
ca_mutex_unlock(g_bleServerBDAddressMutex);
OIC_LOG_V(DEBUG, TZ_BLE_CLIENT_TAG, "New Characteristics[%s] of uuid[%s] is obtained",
(char *)characteristic, uuid);
- if(0 == strcasecmp(uuid, CA_BLE_READ_CHAR_UUID)) // Server will read on this characterisctics
+ if(0 == strcasecmp(uuid, CA_GATT_RESPONSE_CHRC_UUID)) // Server will read on this characterisctics
{
OIC_LOG(DEBUG, TZ_BLE_CLIENT_TAG , "Read characteristics is obtained");
OICFree(uuid);
}
ca_mutex_unlock(g_bleClientThreadPoolMutex);
}
- else if (0 == strcasecmp(uuid, CA_BLE_WRITE_CHAR_UUID)) // Server will write on this characteristics.
+ else if (0 == strcasecmp(uuid, CA_GATT_REQUEST_CHRC_UUID)) // Server will write on this characteristics.
{
OICFree(uuid);
OIC_LOG(DEBUG, TZ_BLE_CLIENT_TAG , "Write characteristics is obtained");
OIC_LOG(DEBUG, TZ_BLE_CLIENT_TAG, "OUT");
}
-void CASetLEReqRespClientCallback(CABLEClientDataReceivedCallback callback)
+void CASetLEReqRespClientCallback(CABLEDataReceivedCallback callback)
{
OIC_LOG(DEBUG, TZ_BLE_CLIENT_TAG, "IN");
OIC_LOG_V(DEBUG, TZ_BLE_CLIENT_TAG, "desc x3 [%x]", stGattCharDescInfo->desc[3]);
- OIC_LOG_V(DEBUG, TZ_BLE_CLIENT_TAG, "BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG strUUID is [%s]",
+ OIC_LOG_V(DEBUG, TZ_BLE_CLIENT_TAG, "CA_GATT_CONFIGURATION_DESC_UUID strUUID is [%s]",
strUUID);
- //if (!strncmp(strUUID, BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG, 2))
+ //if (!strncmp(strUUID, CA_GATT_CONFIGURATION_DESC_UUID, 2))
{
OIC_LOG(DEBUG, TZ_BLE_CLIENT_TAG, "setting notification/indication for descriptor");
}
CAResult_t CAUpdateCharacteristicsToGattServer(const char *remoteAddress,
- const char *data, const uint32_t dataLen,
- CALETransferType_t type, const int32_t position)
+ const char *data, uint32_t dataLen,
+ CALETransferType_t type, int32_t position)
{
OIC_LOG(DEBUG, TZ_BLE_CLIENT_TAG, "IN");
return CA_STATUS_OK;
}
+CAResult_t CAStartLEAdapter()
+{
+ // Nothing to do.
+
+ return CA_STATUS_OK;
+}
+
CAResult_t CAGetLEAdapterState()
{
OIC_LOG(DEBUG, TZ_LE_NWK_MONITOR_TAG, "IN");
#include "caqueueingthread.h"
#include "caadapterutils.h"
#include "cafragmentation.h"
+#include "cagattservice.h"
#include "cableutil.h"
#include "oic_string.h"
#include "oic_malloc.h"
* @brief Maintains the callback to be notified on receival of network packets from other
* BLE devices
*/
-static CABLEServerDataReceivedCallback g_bleServerDataReceivedCallback = NULL;
+static CABLEDataReceivedCallback g_bleServerDataReceivedCallback = NULL;
/**
* @var g_serverErrorCallback
sleep(5); // Sleep is must because of the platform issue.
- char *serviceUUID = OIC_BLE_SERVICE_ID;
+ char *serviceUUID = CA_GATT_SERVICE_UUID;
ret = CAAddNewBleServiceInGattServer(serviceUUID);
if (CA_STATUS_OK != ret )
return;
}
- char *charReadUUID = CA_BLE_READ_CHAR_UUID;
+ char *charReadUUID = CA_GATT_RESPONSE_CHRC_UUID;
char charReadValue[] = {33, 44, 55, 66}; // These are initial random values
ret = CAAddNewCharacteristicsToGattServer(g_gattSvcPath, charReadUUID, charReadValue,
return;
}
- char *charWriteUUID = CA_BLE_WRITE_CHAR_UUID;
+ char *charWriteUUID = CA_GATT_REQUEST_CHRC_UUID;
char charWriteValue[] = {33, 44, 55, 66}; // These are initial random values
OIC_LOG(DEBUG, TZ_BLE_SERVER_TAG, "Sending data up !");
uint32_t sentLength = 0;
- g_bleServerDataReceivedCallback(remoteAddress, OIC_BLE_SERVICE_ID,
- data, charValueLen, &sentLength);
+ g_bleServerDataReceivedCallback(remoteAddress, data, charValueLen,
+ &sentLength);
ca_mutex_unlock(g_bleReqRespCbMutex);
return CA_STATUS_OK;
}
-CAResult_t CAUpdateCharacteristicsToGattClient(const char* address, const char *charValue,
- const uint32_t charValueLen)
+CAResult_t CAUpdateCharacteristicsToGattClient(const char *address,
+ const char *charValue,
+ uint32_t charValueLen)
{
OIC_LOG(DEBUG, TZ_BLE_SERVER_TAG, "IN");
return CA_STATUS_OK;
}
-void CASetLEReqRespServerCallback(CABLEServerDataReceivedCallback callback)
+void CASetLEReqRespServerCallback(CABLEDataReceivedCallback callback)
{
OIC_LOG(DEBUG, TZ_BLE_SERVER_TAG, "IN");
#include "caadapterutils.h"
+#include "cagattservice.h"
#include "oic_string.h"
#include "oic_malloc.h"
VERIFY_NON_NULL(serviceUUID, TZ_BLE_CLIENT_UTIL_TAG, "Param serviceHandle is NULL");
- if (strcasecmp(serviceUUID, OIC_BLE_SERVICE_ID) != 0)
+ if (strcasecmp(serviceUUID, CA_GATT_SERVICE_UUID) != 0)
{
OIC_LOG(ERROR, TZ_BLE_CLIENT_UTIL_TAG, "It is not OIC service!");
return CA_STATUS_FAILED;
return CA_STATUS_FAILED;
}
- if (strcasecmp(uuid, OIC_BLE_SERVICE_ID) != 0)
+ if (strcasecmp(uuid, CA_GATT_SERVICE_UUID) != 0)
{
OIC_LOG(ERROR, TZ_BLE_CLIENT_UTIL_TAG, "It is not OIC service!");
OICFree(uuid);
int total; /**< The total number of descriptor in a characteristic */
} stGattCharDescriptor_t;
-#define OIC_BLE_SERVICE_ID "ADE3D529-C784-4F63-A987-EB69F70EE816"
-///TODO: OIC_BLE_SERVICE_ID will be generated by invoking API in future.
-
-/**
- * @def CA_BLE_READ_CHAR_UUID
- * @brief UUID of read characteristic. This UUID is common across all platform for LE transport.
- */
-#define CA_BLE_READ_CHAR_UUID "E9241982-4580-42C4-8831-95048216B256"
-
-/**
- * @def CA_BLE_WRITE_CHAR_UUID
- * @brief UUID of write characteristic. This UUID is common across all platform for LE transport.
- */
-#define CA_BLE_WRITE_CHAR_UUID "AD7B334F-4637-4B86-90B6-9D787F03D218"
-
/**
* @brief Used to increment the registered service count.
* @return NONE
# Valgrind run.
valgrind_environment = ''
+ # GLib uses a custom memory allocation scheme that can
+ # sometimes confuse Valgrind. Configure GLib to be Valgrind
+ # friendly.
+ valgrind_environment += 'G_DEBUG=gc-friendly G_SLICE=always-malloc'
+
# Valgrind suppressions file.
suppression_file = env.File('#tools/valgrind/iotivity.supp').srcnode().path
# limitations under the License.
# ------------------------------------------------------------------------
+# ************************************************************************
# This file contains Valgrind suppressions. It is meant to make
# Valgrind ignore memory violations that are out of the control of
# IoTivity developers. Do NOT abuse this file by adding suppressions
# for memory violations that can be addressed in IoTivity itself.
+# ************************************************************************
+
+# ************************************************************************
+# GLib related suppressions
+#
+# These suppressions hide "possibly lost" leak diagnostics from
+# Valgrind that occur from running simple code like the following:
+# GError * error = NULL;
+# GDBusConnection * const connection =
+# g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
+# g_object_unref(connection);
+# ************************************************************************
+
+# gdbus-codegen generated code
+{
+ gdbus-codegen/*-skeleton-new
+ Memcheck:Leak
+ ...
+ fun:*_skeleton_new
+}
+# GLib >= 2.44.1
+{
+ glib/gobject-init-ctor
+ Memcheck:Leak
+ ...
+ fun:gobject_init_ctor
+}
+{
+ glib/g-dbus-auth-run-client
+ Memcheck:Leak
+ ...
+ fun:_g_dbus_auth_run_client
+}
+{
+ glib/g-main-context-iterate-
+ Memcheck:Leak
+ ...
+ fun:g_main_context_iterate.*
+}
+{
+ glib/g-main-context-dispatch
+ Memcheck:Leak
+ ...
+ fun:g_main_context_dispatch
+}
+{
+ glib/g-bus-get-sync
+ Memcheck:Leak
+ ...
+ fun:g_bus_get_sync
+}
+{
+ glib/get-uninitialized-connection
+ Memcheck:Leak
+ ...
+ fun:get_uninitialized_connection
+}
+{
+ glib/g-dbus-address-get-stream-sync
+ Memcheck:Leak
+ ...
+ fun:g_dbus_address_get_stream_sync
+}
+{
+ glib/g-type-register-static-simple
+ Memcheck:Leak
+ ...
+ fun:g_type_register_static_simple
+}
+{
+ glib/g-type-add-interface-static
+ Memcheck:Leak
+ ...
+ fun:g_type_add_interface_static
+}
+{
+ glib/complete-in-idle-cb
+ Memcheck:Leak
+ ...
+ fun:complete_in_idle_cb
+}
+{
+ glib/g-dbus-address-try-connect-one
+ Memcheck:Leak
+ ...
+ fun:g_dbus_address_try_connect_one
+}
+{
+ glib/g-initable-new
+ Memcheck:Leak
+ ...
+ fun:g_initable_new
+}
+# GLib < 2.36
+{
+ glib/g-type-register-fundamental
+ Memcheck:Leak
+ ...
+ fun:g_type_register_fundamental
+}
+{
+ glib/g-type-register-static
+ Memcheck:Leak
+ ...
+ fun:g_type_register_static
+}
+{
+ glib/g-malloc0
+ Memcheck:Leak
+ ...
+ fun:g_malloc0
+ obj:/usr/lib/*/libgobject-2.0*.so.*
+}
+# ************************************************************************