blob: 8a40bd530e088c245cd5bb36941932bdc4f4eb71 [file] [log] [blame]
/*
*
* Copyright (c) 2017 Nest Labs, Inc.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* This file provides Weave over Bluez Peripheral implementation, which uses Bluez DBUS APIs.
*
*/
#include "BluezHelperCode.h"
#include "WoBluez.h"
#if CONFIG_BLE_PLATFORM_BLUEZ
namespace nl {
namespace Ble {
namespace Platform {
namespace BlueZ {
static void WeaveRegisterSetup(DBusMessageIter * iter, void * bluezData);
static void WeaveRegisterReply(DBusMessage * message, void * bluezData);
static void WeaveUnregisterSetup(DBusMessageIter *iter, void *bluezData);
static void WeaveUnregisterReply(DBusMessage *message, void *bluezData);
static gboolean WeaveAdvertisingGetType(const GDBusPropertyTable * property, DBusMessageIter * iter, void * bluezData);
static gboolean GetWeaveUUIDs(const GDBusPropertyTable * property, DBusMessageIter * iter, void * bluezData);
static gboolean WeaveServiceDataCheck(const GDBusPropertyTable * property, void * bluezData);
static gboolean AppendArrayVariant(DBusMessageIter * iter, int type, void * val, int nElements);
static gboolean DictAppendBasicArray(DBusMessageIter * dict, int keyType, const void * key, int type, void * val, int nElements);
static gboolean GetWeaveServiceData(const GDBusPropertyTable * property, DBusMessageIter * iter, void * bluezData);
static gboolean WeaveNameCheck(const GDBusPropertyTable * property, void * bluezData);
static gboolean WeaveGetName(const GDBusPropertyTable * property, DBusMessageIter * iter, void * bluezData);
static DBusMessage * WeaveDestroyAdvertising(DBusConnection * dbusConn, DBusMessage * dbusMsg, void * bluezData);
static DBusMessage * WeaveDestroyProfile(DBusConnection * dbusConn, DBusMessage * dbusMsg, void * bluezData);
static void RegisterWeaveAppSetup(DBusMessageIter * iter, void * bluezData);
static void RegisterWeaveAppReply(DBusMessage * message, void * bluezData);
static void WeaveCharacteristicDestroy(void * bluezData);
static gboolean WeaveServiceGetUUID(const GDBusPropertyTable * property, DBusMessageIter * iter, void * bluezData);
static gboolean WeaveServiceGetPrimary(const GDBusPropertyTable * property, DBusMessageIter * iter, void * bluezData);
static void ServiceDestroy(void * bluezData);
static gboolean CharacteristicGetUUID(const GDBusPropertyTable * property, DBusMessageIter * iter, void * bluezData);
static gboolean CharacteristicGetService(const GDBusPropertyTable * property, DBusMessageIter * iter, void * bluezData);
static gboolean CharacteristicGetValue(const GDBusPropertyTable * property, DBusMessageIter * iter, void * bluezData);
static gboolean CharacteristicGetNotifying(const GDBusPropertyTable * property, DBusMessageIter * iter, void * bluezData);
static gboolean CharacteristicGetFlags(const GDBusPropertyTable * property, DBusMessageIter * iter, void * bluezData);
static DBusMessage * CharacteristicRead(DBusConnection * dbusConn, DBusMessage * dbusMsg, void * bluezData);
#if BLE_CONFIG_BLUEZ_MTU_FEATURE
static bool WritePipeIORead(struct io * io, void * bluezData);
static bool PipeIODestroy(struct io * io, void * bluezData);
static DBusMessage * CharacteristicCreatePipe(Characteristic * characteristic, DBusMessage * dbusMsg);
static DBusMessage * CharacteristicAcquireWrite(DBusConnection * dbusConn, DBusMessage * dbusMsg, void * bluezData);
static DBusMessage * CharacteristicAcquireNotify(DBusConnection * dbusConn, DBusMessage * dbusMsg, void * bluezData);
static gboolean CharacteristicPipeAcquired(const GDBusPropertyTable * property, DBusMessageIter * iter, void * bluezData);
#endif
static DBusMessage * CharacteristicWrite(DBusConnection * dbusConn, DBusMessage * dbusMsg, void * bluezData);
static DBusMessage * CharacteristicStartNotify(DBusConnection * dbusConn, DBusMessage * dbusMsg, void * bluezData);
static DBusMessage * CharacteristicStopNotify(DBusConnection * dbusConn, DBusMessage * dbusMsg, void * bluezData);
static DBusMessage * CharacteristicIndicationConf(DBusConnection * dbusConn, DBusMessage * dbusMsg, void * bluezData);
static void WeaveClientConnectHandler(DBusConnection * connection, void * bluezData);
static void WeaveClientDisconnectHandler(DBusConnection * connection, void * bluezData);
static gboolean CheckDeviceIsChild(GDBusProxy * childProxy, GDBusProxy * parentProxy);
static void WeaveAdapterAdded(GDBusProxy * proxy);
static void WeaveProfileAdded(GDBusProxy * proxy);
static void WeaveAdvertisingAdded(GDBusProxy * proxy);
static void WeaveDeviceAdded(GDBusProxy *proxy);
static void WeaveProxyAdded(GDBusProxy * proxy, void * bluezData);
static void WeaveProxyDeleted(GDBusProxy * proxy, void * bluezData);
static void WeaveDisconnReply(DBusMessage * dbusMsg, void * bluezData);
static void WeaveDeviceDisconnect(GDBusProxy * proxy);
static void WeavePropertyChange(GDBusProxy *proxy, const char *name, DBusMessageIter *iter, void *bluezData);
static void PowerCb(const DBusError * error, void * bluezData);
static void WeaveClientReady(GDBusClient * weaveClient, void * bluezData);
BluezServerEndpoint * gBluezServerEndpoint = NULL;
BluezBlePlatformDelegate * gBluezBlePlatformDelegate = NULL;
BluezBleApplicationDelegate * gBluezBleApplicationDelegate = NULL;
static GMainLoop * gBluezMainLoop;
static DBusConnection * gBluezDbusConn;
static Adapter * gDefaultAdapter;
static const GDBusMethodTable weaveAdvertisingMethods[] = {
{ "Release", WeaveDestroyAdvertising, (GDBusMethodFlags) 0, 0, NULL, NULL }, { }
};
static const GDBusPropertyTable weaveAdvertisingProperties[] = { { "Type", "s", WeaveAdvertisingGetType },
{ "ServiceUUIDs", "as", GetWeaveUUIDs, NULL, NULL },
{ "LocalName", "s", WeaveGetName, NULL, WeaveNameCheck },
{ "ServiceData", "a{sv}", GetWeaveServiceData, NULL,
WeaveServiceDataCheck },
{ } };
static const GDBusMethodTable weaveAppMethods[] = { { "Release", WeaveDestroyProfile, GDBusMethodFlags(0), 0, NULL, NULL }, { } };
static const GDBusPropertyTable weaveAppProperties[] = { { "UUIDs", "as", GetWeaveUUIDs }, { } };
static const GDBusPropertyTable serviceProperties[] = { { "UUID", "s", WeaveServiceGetUUID },
{ "Primary", "b", WeaveServiceGetPrimary },
{ } };
static const GDBusPropertyTable WeaveCharacteristicProperties[] = {
{ "UUID", "s", CharacteristicGetUUID, NULL, NULL },
{ "Service", "o", CharacteristicGetService, NULL, NULL },
{ "Value", "ay", CharacteristicGetValue, NULL, NULL },
{ "Notifying", "b", CharacteristicGetNotifying, NULL, NULL },
{ "Flags", "as", CharacteristicGetFlags, NULL, NULL },
#if BLE_CONFIG_BLUEZ_MTU_FEATURE
{ "WriteAcquired", "b", CharacteristicPipeAcquired, NULL, NULL },
{ "NotifyAcquired", "b", CharacteristicPipeAcquired, NULL, NULL },
#endif // BLE_CONFIG_BLUEZ_MTU_FEATURE
{ }
};
static const GDBusMethodTable weaveCharacteristicMethods[] = {
{ "ReadValue", CharacteristicRead, G_DBUS_METHOD_FLAG_ASYNC, 0, GDBUS_ARGS( { "options", "a{sv}" }),
GDBUS_ARGS( { "value", "ay" }) },
#if BLE_CONFIG_BLUEZ_MTU_FEATURE
{ "AcquireWrite", CharacteristicAcquireWrite, G_DBUS_METHOD_FLAG_ASYNC, 0, GDBUS_ARGS( { "options", "a{sv}" }), NULL },
{ "AcquireNotify", CharacteristicAcquireNotify, G_DBUS_METHOD_FLAG_ASYNC, 0, GDBUS_ARGS( { "options", "a{sv}" }), NULL },
#endif // BLE_CONFIG_BLUEZ_MTU_FEATURE
{ "WriteValue", CharacteristicWrite, G_DBUS_METHOD_FLAG_ASYNC, 0, GDBUS_ARGS( { "value", "ay" }, { "options", "a{sv}" }), NULL },
{ "StartNotify", CharacteristicStartNotify, G_DBUS_METHOD_FLAG_ASYNC, 0, NULL, NULL },
{ "StopNotify", CharacteristicStopNotify, G_DBUS_METHOD_FLAG_ASYNC, 0, NULL, NULL },
{ "Confirm", CharacteristicIndicationConf, G_DBUS_METHOD_FLAG_ASYNC, 0, NULL, NULL },
{ }
};
static void WeaveRegisterSetup(DBusMessageIter * iter, void * bluezData)
{
DBusMessageIter dict;
const char * path = ADVERTISING_PATH;
gboolean success = FALSE;
const char * msg = NULL;
success = dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
VerifyOrExit(success == TRUE, msg = "Fail to append basic in WeaveRegisterSetup");
success = dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING
DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
&dict);
VerifyOrExit(success == TRUE, msg = "Fail to open container in WeaveRegisterSetup");
success = dbus_message_iter_close_container(iter, &dict);
VerifyOrExit(success == TRUE, msg = "Fail to close container in WeaveRegisterSetup");
exit:
if ((success != TRUE) && (msg != NULL))
{
WeaveLogError(Ble, msg);
}
}
static void WeaveRegisterReply(DBusMessage * message, void * bluezData)
{
DBusConnection * dbusConn = static_cast<DBusConnection *>(bluezData);
DBusError error;
dbus_error_init(&error);
if (TRUE == dbus_set_error_from_message(&error, message))
{
WeaveLogError(Ble, "Fail to register weave advertisement in WeaveRegisterReply: %s", error.name);
dbus_error_free(&error);
if (FALSE == g_dbus_unregister_interface(dbusConn, ADVERTISING_PATH, ADVERTISING_INTERFACE))
{
WeaveLogError(Ble, "Fail to unregister weave advertisement in WeaveRegisterReply");
}
}
else
{
WeaveLogProgress(Ble, "Weave advertisement object registered");
}
}
static void WeaveUnregisterSetup(DBusMessageIter *iter, void *bluezData)
{
const char *path = ADVERTISING_PATH;
gboolean success = FALSE;
const char *msg = NULL;
success = dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
VerifyOrExit(success == TRUE, msg = "Fail to append basic in WeaveUnregisterSetup");
exit:
if ((success != TRUE) && (msg != NULL))
{
WeaveLogError(Ble, msg);
}
}
static void WeaveUnregisterReply(DBusMessage *message, void *bluezData)
{
DBusError error;
dbus_error_init(&error);
if (FALSE == dbus_set_error_from_message(&error, message))
{
WeaveLogProgress(Ble, "Weave advertisement unregistered");
if (gBluezDbusConn)
{
if (FALSE == g_dbus_unregister_interface(gBluezDbusConn, ADVERTISING_PATH, ADVERTISING_INTERFACE))
{
WeaveLogError(Ble, "Fail to unregister weave advertisement object in WeaveUnregisterReply");
}
}
}
else
{
WeaveLogError(Ble, "Fail to unregister weave advertisement in WeaveUnregisterReply: %s", error.name);
dbus_error_free(&error);
}
}
static gboolean WeaveAdvertisingGetType(const GDBusPropertyTable * property, DBusMessageIter * iter, void * bluezData)
{
gboolean success = dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &(gBluezServerEndpoint->advertisingType));
if (FALSE == success)
{
WeaveLogError(Ble, "Fail to get advertising type in WeaveAdvertisingGetType");
}
return success;
}
static gboolean GetWeaveUUIDs(const GDBusPropertyTable * property, DBusMessageIter * iter, void * bluezData)
{
const char * msg = NULL;
gboolean success = FALSE;
DBusMessageIter dbusArray;
success = dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "as", &dbusArray);
VerifyOrExit(success == TRUE, msg = "Fail to open container in GetWeaveUUIDs");
success = dbus_message_iter_append_basic(&dbusArray, DBUS_TYPE_STRING, &(gBluezServerEndpoint->advertisingUUID));
VerifyOrExit(success == TRUE, msg = "Fail to append basic in GetWeaveUUIDs");
success = dbus_message_iter_close_container(iter, &dbusArray);
VerifyOrExit(success == TRUE, msg = "Fail to close container in GetWeaveUUIDs");
exit:
if ((success != TRUE) && (msg != NULL))
{
WeaveLogError(Ble, msg);
}
return success;
}
static gboolean WeaveServiceDataCheck(const GDBusPropertyTable * property, void * bluezData)
{
gboolean success = FALSE;
if (NULL != gBluezServerEndpoint->weaveServiceData)
{
success = TRUE;
}
return success;
}
static gboolean AppendArrayVariant(DBusMessageIter * iter, int type, void * val, int nElements)
{
const char * msg = NULL;
gboolean success = FALSE;
DBusMessageIter variant, array;
const char *** strArray = (const char ***) val;
int i;
char typeSig[2] = { (char) type, '\0' };
char arraySig[3] = { DBUS_TYPE_ARRAY, (char) type, '\0' };
success = dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, arraySig, &variant);
VerifyOrExit(success == TRUE, msg = "Fail to open DBUS_TYPE_VARIANT container in AppendArrayVariant");
success = dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, typeSig, &array);
VerifyOrExit(success == TRUE, msg = "Fail to open DBUS_TYPE_ARRAY container in AppendArrayVariant");
if (dbus_type_is_fixed(type) == TRUE)
{
success = dbus_message_iter_append_fixed_array(&array, type, val, nElements);
VerifyOrExit(success == TRUE, msg = "Fail to append fixed array in AppendArrayVariant");
}
else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH)
{
for (i = 0; i < nElements; i++)
{
success = dbus_message_iter_append_basic(&array, type, &((*strArray)[i]));
VerifyOrExit(success == TRUE, msg = "Fail to append basic in AppendArrayVariant");
}
}
success = dbus_message_iter_close_container(&variant, &array);
VerifyOrExit(success == TRUE, msg = "Fail to close DBUS_TYPE_ARRAY container in AppendArrayVariant");
success = dbus_message_iter_close_container(iter, &variant);
VerifyOrExit(success == TRUE, msg = "Fail to close DBUS_TYPE_VARIANT container in AppendArrayVariant");
exit:
if ((success != TRUE) && (msg != NULL))
{
WeaveLogError(Ble, msg);
}
return success;
}
static gboolean DictAppendBasicArray(DBusMessageIter * dict, int keyType, const void * key, int type, void * val, int nElements)
{
const char * msg = NULL;
gboolean success = FALSE;
DBusMessageIter entry;
success = dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
VerifyOrExit(success == TRUE, msg = "Fail to open DBUS_TYPE_DICT_ENTRY container in DictAppendBasicArray");
success = dbus_message_iter_append_basic(&entry, keyType, key);
VerifyOrExit(success == TRUE, msg = "Fail to append key in DictAppendBasicArray");
success = AppendArrayVariant(&entry, type, val, nElements);
VerifyOrExit(success == TRUE, msg = "Fail to append array variant in DictAppendBasicArray");
success = dbus_message_iter_close_container(dict, &entry);
VerifyOrExit(success == TRUE, msg = "Fail to close DBUS_TYPE_DICT_ENTRY container in DictAppendBasicArray");
exit:
if ((success != TRUE) && (msg != NULL))
{
WeaveLogError(Ble, msg);
}
return success;
}
static gboolean GetWeaveServiceData(const GDBusPropertyTable * property, DBusMessageIter * iter, void * bluezData)
{
const char * msg = NULL;
gboolean success = FALSE;
DBusMessageIter dict;
success = dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
VerifyOrExit(success == TRUE, msg = "Fail to open DBUS_TYPE_ARRAY container in GetWeaveServiceData");
success = DictAppendBasicArray(&dict, DBUS_TYPE_STRING, &gBluezServerEndpoint->advertisingUUID, DBUS_TYPE_BYTE,
&gBluezServerEndpoint->weaveServiceData, sizeof(WeaveServiceData));
VerifyOrExit(success == TRUE, msg = "Fail to append dictionary in GetWeaveServiceData");
success = dbus_message_iter_close_container(iter, &dict);
VerifyOrExit(success == TRUE, msg = "Fail to close DBUS_TYPE_ARRAY container in GetWeaveServiceData");
exit:
if ((success != TRUE) && (msg != NULL))
{
WeaveLogError(Ble, msg);
}
return success;
}
static gboolean WeaveNameCheck(const GDBusPropertyTable * property, void * bluezData)
{
gboolean success = FALSE;
if (NULL != gBluezServerEndpoint->adapterName)
{
success = TRUE;
}
return success;
}
static gboolean WeaveGetName(const GDBusPropertyTable * property, DBusMessageIter * iter, void * bluezData)
{
gboolean success = dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &(gBluezServerEndpoint->adapterName));
if (FALSE == success)
{
WeaveLogError(Ble, "Fail to get Weave Local name in WeaveGetName");
}
return success;
}
static DBusMessage * WeaveDestroyAdvertising(DBusConnection * dbusConn, DBusMessage * dbusMsg, void * bluezData)
{
if (FALSE == g_dbus_unregister_interface(dbusConn, ADVERTISING_PATH, ADVERTISING_INTERFACE))
{
WeaveLogError(Ble, "Fail to destroy advertising object in WeaveDestroyAdvertising");
}
return dbus_message_new_method_return(dbusMsg);
}
gboolean SetAlias(void)
{
gboolean success = g_dbus_proxy_set_property_basic(gDefaultAdapter->adapterProxy, "Alias", DBUS_TYPE_STRING, &(gBluezServerEndpoint->adapterName), NULL, NULL, NULL);
if (FALSE == success)
{
WeaveLogError(Ble, "Fail to set controller alias for adapter %p(%s)", gDefaultAdapter->adapterProxy, gBluezServerEndpoint->adapterName);
}
return success;
}
gboolean EnableDiscoverable(void)
{
gboolean success = FALSE;
dbus_bool_t discoverable = TRUE;
success = g_dbus_proxy_set_property_basic(gDefaultAdapter->adapterProxy, "Discoverable", DBUS_TYPE_BOOLEAN, &discoverable, NULL, NULL, NULL);
if (FALSE == success)
{
WeaveLogError(Ble, "Fail to set Discoverable property for adapter %p", gDefaultAdapter->adapterProxy);
}
return success;
}
gboolean AdvertisingRegister(DBusConnection * dbusConn, GDBusProxy * proxy)
{
gboolean success = FALSE;
const char * msg = NULL;
success = g_dbus_register_interface(dbusConn, ADVERTISING_PATH, ADVERTISING_INTERFACE, weaveAdvertisingMethods, NULL,
weaveAdvertisingProperties, NULL, NULL);
VerifyOrExit(success == TRUE, msg = "Failed to register advertising object in AdvertisingRegister");
success = g_dbus_proxy_method_call(proxy, "RegisterAdvertisement", WeaveRegisterSetup, WeaveRegisterReply, dbusConn, NULL);
VerifyOrExit(success == TRUE, msg = "Failed to call RegisterAdvertisement in AdvertisingRegister");
exit:
if ((success != TRUE) && (msg != NULL))
{
WeaveLogError(Ble, msg);
}
return success;
}
static DBusMessage * WeaveDestroyProfile(DBusConnection * dbusConn, DBusMessage * dbusMsg, void * bluezData)
{
if (FALSE == g_dbus_unregister_interface(dbusConn, WEAVE_PATH, PROFILE_INTERFACE))
{
WeaveLogError(Ble, "Failed to destroy advertising object in WeaveDestroyProfile");
}
return dbus_message_new_method_return(dbusMsg);
}
static void RegisterWeaveAppSetup(DBusMessageIter * iter, void * bluezData)
{
DBusMessageIter dict;
const char * path = "/";
gboolean success = FALSE;
const char * msg = NULL;
success = dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
VerifyOrExit(success == TRUE, msg = "Fail to append basic in RegisterWeaveAppSetup");
success = dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING
DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
&dict);
VerifyOrExit(success == TRUE, msg = "Fail to open container in RegisterWeaveAppSetup");
success = dbus_message_iter_close_container(iter, &dict);
VerifyOrExit(success == TRUE, msg = "Fail to close container in RegisterWeaveAppSetup");
exit:
if ((success != TRUE) && (msg != NULL))
{
WeaveLogError(Ble, msg);
}
}
static void RegisterWeaveAppReply(DBusMessage * message, void * bluezData)
{
DBusError error;
dbus_error_init(&error);
if (TRUE == dbus_set_error_from_message(&error, message))
{
WeaveLogError(Ble, "Failed to setup weave application in RegisterWeaveAppReply: %s", error.name);
dbus_error_free(&error);
}
}
gboolean SetupWeaveApp(DBusConnection * dbusConn, GDBusProxy * proxy)
{
gboolean success = FALSE;
const char * msg = NULL;
success =
g_dbus_register_interface(dbusConn, WEAVE_PATH, PROFILE_INTERFACE, weaveAppMethods, NULL, weaveAppProperties, NULL, NULL);
VerifyOrExit(success == TRUE, msg = "Fail in register interface in SetupWeaveApp");
success = g_dbus_proxy_method_call(proxy, "RegisterApplication", RegisterWeaveAppSetup, RegisterWeaveAppReply, NULL, NULL);
if (FALSE == success)
{
msg = "Fail to call RegisterApplication in SetupWeaveApp";
g_dbus_unregister_interface(dbusConn, WEAVE_PATH, PROFILE_INTERFACE);
ExitNow();
}
exit:
if ((success != TRUE) && (msg != NULL))
{
WeaveLogError(Ble, msg);
}
return success;
}
static void WeaveCharacteristicDestroy(void * bluezData)
{
Characteristic * WeaveCharacteristic = static_cast<Characteristic *>(bluezData);
if (NULL != WeaveCharacteristic)
{
g_free(WeaveCharacteristic->path);
g_free(WeaveCharacteristic->servicePath);
g_free(WeaveCharacteristic->uuid);
g_strfreev(WeaveCharacteristic->flags);
g_free(WeaveCharacteristic->value);
g_free(WeaveCharacteristic);
}
}
static gboolean WeaveServiceGetUUID(const GDBusPropertyTable * property, DBusMessageIter * iter, void * bluezData)
{
Service * weaveService = static_cast<Service *>(bluezData);
gboolean success = dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &weaveService->uuid);
if (FALSE == success)
{
WeaveLogError(Ble, "Failed to get weave service uuid property in WeaveServiceGetUUID");
}
return success;
}
static gboolean WeaveServiceGetPrimary(const GDBusPropertyTable * property, DBusMessageIter * iter, void * bluezData)
{
Service * weaveService = static_cast<Service *>(bluezData);
dbus_bool_t servicePrimary;
gboolean success = FALSE;
const char * msg = NULL;
VerifyOrExit(weaveService != NULL, msg = "weaveService is NULL in WeaveServiceGetPrimary");
servicePrimary = weaveService->isPrimary ? TRUE : FALSE;
success = dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &servicePrimary);
VerifyOrExit(success == TRUE, msg = "Failed to get weave service primary property in WeaveServiceGetPrimary");
exit:
if ((success != TRUE) && (msg != NULL))
{
WeaveLogError(Ble, msg);
}
return success;
}
static void ServiceDestroy(void * bluezData)
{
Service * weaveService = static_cast<Service *>(bluezData);
if (NULL != weaveService)
{
g_free(weaveService->path);
g_free(weaveService->uuid);
g_free(weaveService);
}
}
gboolean RegisterWeaveService(DBusConnection * dbusConn)
{
gboolean success = FALSE;
const char * msg = NULL;
Service * weaveService;
weaveService = g_new0(Service, 1);
VerifyOrExit(weaveService != NULL, msg = "weaveService is NULL in RegisterWeaveService");
weaveService->dbusConn = dbusConn;
weaveService->path = g_strdup_printf("%s/WeaveService%p", WEAVE_PATH, weaveService);
weaveService->isPrimary = true;
weaveService->uuid = g_strdup(UUID_WEAVE);
success = g_dbus_register_interface(dbusConn, weaveService->path, SERVICE_INTERFACE, NULL, NULL, serviceProperties,
weaveService, ServiceDestroy);
if (FALSE == success)
{
msg = "Failed to register weave service";
ServiceDestroy(weaveService);
weaveService = NULL;
}
gBluezServerEndpoint->weaveService = weaveService;
exit:
if ((success != TRUE) && (msg != NULL))
{
WeaveLogError(Ble, msg);
}
return success;
}
static gboolean CharacteristicGetUUID(const GDBusPropertyTable * property, DBusMessageIter * iter, void * bluezData)
{
Characteristic * characteristic = static_cast<Characteristic *>(bluezData);
const char * msg = NULL;
gboolean success = FALSE;
VerifyOrExit(characteristic != NULL, msg = "characteristic is NULL in CharacteristicGetUUID");
success = dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &characteristic->uuid);
VerifyOrExit(success == TRUE, msg = "Fail to append basic in CharacteristicGetUUID");
exit:
if ((success != TRUE) && (msg != NULL))
{
WeaveLogError(Ble, msg);
}
return success;
}
static gboolean CharacteristicGetService(const GDBusPropertyTable * property, DBusMessageIter * iter, void * bluezData)
{
Characteristic * characteristic = static_cast<Characteristic *>(bluezData);
const char * msg = NULL;
gboolean success = FALSE;
VerifyOrExit(characteristic != NULL, msg = "characteristic is NULL in CharacteristicGetService");
success = dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &characteristic->servicePath);
VerifyOrExit(success == TRUE, msg = "Fail to append basic in CharacteristicGetService");
exit:
if ((success != TRUE) && (msg != NULL))
{
WeaveLogError(Ble, msg);
}
return success;
}
static gboolean CharacteristicGetValue(const GDBusPropertyTable * property, DBusMessageIter * iter, void * bluezData)
{
Characteristic * characteristic = static_cast<Characteristic *>(bluezData);
DBusMessageIter array;
const char * msg = NULL;
gboolean success = FALSE;
VerifyOrExit(characteristic != NULL, msg = "characteristic is NULL in CharacteristicGetValue");
success = dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
VerifyOrExit(success == TRUE, msg = "Fail to open container in CharacteristicGetValue");
success = dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &characteristic->value, characteristic->valueLen);
VerifyOrExit(success == TRUE, msg = "Fail to append array in CharacteristicGetValue");
success = dbus_message_iter_close_container(iter, &array);
VerifyOrExit(success == TRUE, msg = "Fail to close container in CharacteristicGetValue");
exit:
if ((success != TRUE) && (msg != NULL))
{
WeaveLogError(Ble, msg);
}
return success;
}
static gboolean CharacteristicGetNotifying(const GDBusPropertyTable * property, DBusMessageIter * iter, void * bluezData)
{
Characteristic * characteristic = static_cast<Characteristic *>(bluezData);
dbus_bool_t characteristicNotify;
const char * msg = NULL;
gboolean success = FALSE;
VerifyOrExit(characteristic != NULL, msg = "characteristic is NULL in CharacteristicGetNotifying");
characteristicNotify = characteristic->isNotifying ? TRUE : FALSE;
success = dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &characteristicNotify);
VerifyOrExit(success == TRUE, msg = "Fail to append basic in CharacteristicGetNotifying");
exit:
if ((success != TRUE) && (msg != NULL))
{
WeaveLogError(Ble, msg);
}
return success;
}
static gboolean CharacteristicGetFlags(const GDBusPropertyTable * property, DBusMessageIter * iter, void * bluezData)
{
Characteristic * characteristic = static_cast<Characteristic *>(bluezData);
int flagIndex = 0;
DBusMessageIter array;
const char * msg = NULL;
gboolean success = FALSE;
VerifyOrExit(characteristic != NULL, msg = "characteristic is NULL in CharacteristicGetFlags");
success = dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &array);
VerifyOrExit(success == TRUE, msg = "Fail to open container in CharacteristicGetFlags");
while (characteristic->flags[flagIndex])
{
success = dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &characteristic->flags[flagIndex]);
VerifyOrExit(success == TRUE, msg = "Fail to append array in CharacteristicGetFlags");
flagIndex++;
}
success = dbus_message_iter_close_container(iter, &array);
VerifyOrExit(success == TRUE, msg = "Fail to close container in CharacteristicGetFlags");
exit:
if ((success != TRUE) && (msg != NULL))
{
WeaveLogError(Ble, msg);
}
return success;
}
static DBusMessage * CharacteristicRead(DBusConnection * dbusConn, DBusMessage * dbusMsg, void * bluezData)
{
Characteristic * characteristic = static_cast<Characteristic *>(bluezData);
DBusMessage * readReply = NULL;
DBusMessageIter iter, array;
const char * msg = NULL;
gboolean success = FALSE;
readReply = g_dbus_create_reply(dbusMsg, DBUS_TYPE_INVALID);
dbus_message_iter_init_append(readReply, &iter);
VerifyOrExit(characteristic != NULL, msg = "characteristic is NULL in CharacteristicRead");
success = dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "y", &array);
VerifyOrExit(success == TRUE, msg = "Fail to open container in CharacteristicRead");
success = dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &(characteristic->value), characteristic->valueLen);
VerifyOrExit(success == TRUE, msg = "Fail to append fixed array in CharacteristicRead");
success = dbus_message_iter_close_container(&iter, &array);
VerifyOrExit(success == TRUE, msg = "Fail to close container in CharacteristicRead");
exit:
if ((success != TRUE) && (NULL != msg))
{
WeaveLogError(Ble, msg);
}
return readReply;
}
#if BLE_CONFIG_BLUEZ_MTU_FEATURE
static bool WritePipeIORead(struct io * io, void * bluezData)
{
Characteristic * characteristic = static_cast<Characteristic *>(bluezData);
int fd;
const char * msg = NULL;
uint8_t writerData[BUFF_SIZE];
ssize_t writerDataLength;
bool success = false;
VerifyOrExit(bluezData != NULL, msg = "characteristic is NULL in WritePipeIORead");
if (io == characteristic->writePipeIO)
{
fd = io_get_fd(io);
VerifyOrExit(fd >= 0, msg = "expect file descriptor with non-negatvie value in WritePipeIORead");
}
else
{
msg = "expect writePipeIO in WritePipeIORead";
ExitNow();
}
writerDataLength = read(fd, writerData, sizeof(writerData));
VerifyOrExit(writerDataLength >= 0, msg = "writerDataLength should be larger than or equal to 0");
g_free(characteristic->value);
characteristic->value = static_cast<uint8_t *>(g_memdup(writerData, writerDataLength));
characteristic->valueLen = writerDataLength;
if (strcmp(characteristic->uuid, UUID_WEAVE_C1) == 0)
{
WoBLEz_WriteReceived(gBluezServerEndpoint, characteristic->value, characteristic->valueLen);
success = true;
}
else
{
msg = "current uuid is not UUID_WEAVE_C1";
}
exit:
if (NULL != msg)
{
WeaveLogDetail(Ble, msg);
}
return success;
}
static bool PipeIODestroy(struct io * io, void * bluezData)
{
const char * msg = NULL;
bool success = false;
Characteristic * characteristic = static_cast<Characteristic *>(bluezData);
VerifyOrExit(characteristic != NULL, msg = "characteristic is NULL in PipeIODestroy");
if (io != NULL)
{
if (io == characteristic->indicatePipeIO)
{
io_destroy(characteristic->indicatePipeIO);
characteristic->indicatePipeIO = NULL;
}
else if (io == characteristic->writePipeIO)
{
io_destroy(characteristic->writePipeIO);
characteristic->writePipeIO = NULL;
}
else
{
msg = "unknow io in PipeIODestroy";
ExitNow();
}
}
success = true;
exit:
if (NULL != msg)
{
WeaveLogError(Ble, msg);
}
return success;
}
static DBusMessage * CharacteristicCreatePipe(Characteristic * characteristic, DBusMessage * dbusMsg)
{
int characteristicPipefd[2];
int fdToClose, fdToUse, ioSelection;
struct io * io;
const char * msg = NULL;
DBusMessage * CharacteristicCreatePipeReply = NULL;
VerifyOrExit(characteristic != NULL, msg = "characteristic is NULL in CharacteristicAcquireWrite");
if (pipe2(characteristicPipefd, O_DIRECT | O_NONBLOCK | O_CLOEXEC) < 0)
{
msg = strerror(errno);
CharacteristicCreatePipeReply = g_dbus_create_error(dbusMsg, "org.bluez.Error.Failed", "%s", strerror(errno));
ExitNow();
}
if (TRUE == dbus_message_has_member(dbusMsg, "AcquireWrite"))
{
fdToClose = characteristicPipefd[1];
fdToUse = characteristicPipefd[0];
ioSelection = 1;
}
else if (TRUE == dbus_message_has_member(dbusMsg, "AcquireNotify"))
{
fdToClose = characteristicPipefd[0];
fdToUse = characteristicPipefd[1];
ioSelection = 0;
}
else
{
msg = "dbus message expects member, AcquireWrite or AcquireNotify";
ExitNow();
}
io = io_new(fdToUse);
if (io == NULL)
{
close(fdToClose);
close(fdToUse);
msg = strerror(errno);
CharacteristicCreatePipeReply = g_dbus_create_error(dbusMsg, "org.bluez.Error.Failed", "%s", strerror(errno));
ExitNow();
}
io_set_close_on_destroy(io, true);
io_set_read_handler(io, WritePipeIORead, characteristic, NULL);
io_set_disconnect_handler(io, PipeIODestroy, characteristic, NULL);
CharacteristicCreatePipeReply = g_dbus_create_reply(dbusMsg, DBUS_TYPE_UNIX_FD, &fdToClose, DBUS_TYPE_UINT16,
&gBluezServerEndpoint->mtu, DBUS_TYPE_INVALID);
close(fdToClose);
if (ioSelection == 1)
characteristic->writePipeIO = io;
else
characteristic->indicatePipeIO = io;
exit:
if (NULL != msg)
{
WeaveLogError(Ble, msg);
}
return CharacteristicCreatePipeReply;
}
static DBusMessage * CharacteristicAcquireWrite(DBusConnection * dbusConn, DBusMessage * dbusMsg, void * bluezData)
{
Characteristic * characteristic = static_cast<Characteristic *>(bluezData);
const char * msg = NULL;
const char * key;
DBusMessageIter iter, dict, value, entry;
DBusMessage * acquireWriteReply = NULL;
bool acquireMTU = false;
dbus_bool_t iterCheck;
VerifyOrExit(characteristic != NULL, msg = "characteristic is NULL in CharacteristicAcquireWrite");
if (characteristic->writePipeIO != NULL)
{
msg = "there exists writePipeIO, error";
acquireWriteReply = g_dbus_create_error(dbusMsg, "org.bluez.Error.NotPermitted", NULL);
ExitNow();
}
dbus_message_iter_init(dbusMsg, &iter);
VerifyOrExit(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY,
msg = "dbus iterator is not array in CharacteristicAcquireWrite");
dbus_message_iter_recurse(&iter, &dict);
while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY)
{
dbus_message_iter_recurse(&dict, &entry);
dbus_message_iter_get_basic(&entry, &key);
iterCheck = dbus_message_iter_next(&entry);
VerifyOrExit(iterCheck == TRUE, msg = "Reach the end of iterator");
dbus_message_iter_recurse(&entry, &value);
if (strcasecmp(key, "MTU") == 0)
{
dbus_message_iter_get_basic(&value, &gBluezServerEndpoint->mtu);
acquireMTU = true;
break;
}
dbus_message_iter_next(&dict);
}
if (!acquireMTU)
{
msg = "AcquireWite cannot get MTU from bluez";
acquireWriteReply = g_dbus_create_error(dbusMsg, "org.bluez.Error.InvalidArguments", NULL);
ExitNow();
}
acquireWriteReply = CharacteristicCreatePipe(characteristic, dbusMsg);
if (characteristic->writePipeIO != NULL)
{
if (strcmp(characteristic->uuid, UUID_WEAVE_C1) == 0)
{
g_dbus_emit_property_changed(dbusConn, characteristic->path, CHARACTERISTIC_INTERFACE, "WriteAcquired");
}
else
{
msg = "uuid expects UUID_WEAVE_C1";
ExitNow();
}
}
exit:
if (NULL != msg)
{
WeaveLogDetail(Ble, msg);
}
return acquireWriteReply;
}
static DBusMessage * CharacteristicAcquireNotify(DBusConnection * dbusConn, DBusMessage * dbusMsg, void * bluezData)
{
Characteristic * characteristic = static_cast<Characteristic *>(bluezData);
const char * msg = NULL;
DBusMessage * acquireNotifyReply = NULL;
VerifyOrExit(characteristic != NULL, msg = "characteristic is NULL in CharacteristicAcquireNotify");
if (characteristic->isNotifying)
{
msg = "Notifying has been enabled in CharacteristicAcquireNotify";
acquireNotifyReply = g_dbus_create_error(dbusMsg, "org.bluez.Error.NotPermitted", NULL);
ExitNow();
}
if (characteristic->indicatePipeIO != NULL)
{
msg = "there exists indicatePipeIO, error";
acquireNotifyReply = g_dbus_create_error(dbusMsg, "org.bluez.Error.NotPermitted", NULL);
ExitNow();
}
acquireNotifyReply = CharacteristicCreatePipe(characteristic, dbusMsg);
if (characteristic->indicatePipeIO != NULL)
{
characteristic->isNotifying = true;
WeaveLogProgress(Ble, "Characteristic path %s notification enabled", characteristic->path);
if (strcmp(characteristic->uuid, UUID_WEAVE_C2) == 0)
{
WoBLEz_SubscriptionChange(gBluezServerEndpoint);
}
else
{
msg = "uuid expects UUID_WEAVE_C2";
ExitNow();
}
g_dbus_emit_property_changed(dbusConn, characteristic->path, CHARACTERISTIC_INTERFACE, "Notifying");
g_dbus_emit_property_changed(dbusConn, characteristic->path, CHARACTERISTIC_INTERFACE, "NotifyAcquired");
}
exit:
if (NULL != msg)
{
WeaveLogError(Ble, msg);
}
return acquireNotifyReply;
}
#endif // BLE_CONFIG_BLUEZ_MTU_FEATURE
static DBusMessage * CharacteristicWrite(DBusConnection * dbusConn, DBusMessage * dbusMsg, void * bluezData)
{
Characteristic * characteristic = static_cast<Characteristic *>(bluezData);
DBusMessageIter iter;
dbus_message_iter_init(dbusMsg, &iter);
const char * msg = NULL;
DBusMessageIter array;
DBusMessage * writeReply = NULL;
uint8_t * writerData = NULL;
int writerDataLength;
VerifyOrExit(characteristic != NULL, msg = "characteristic is NULL in CharacteristicWrite");
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
{
msg = "Fail to get arg type in CharacteristicWrite";
writeReply = g_dbus_create_error(dbusMsg, "org.bluez.Error.InvalidArguments", NULL);
ExitNow();
}
dbus_message_iter_recurse(&iter, &array);
dbus_message_iter_get_fixed_array(&array, &(writerData), &(writerDataLength));
characteristic->value = static_cast<uint8_t *>(g_memdup(writerData, writerDataLength));
characteristic->valueLen = writerDataLength;
g_dbus_emit_property_changed(dbusConn, characteristic->path, CHARACTERISTIC_INTERFACE, "Value");
if (strcmp(characteristic->uuid, UUID_WEAVE_C1) == 0)
{
WoBLEz_WriteReceived(gBluezServerEndpoint, characteristic->value, characteristic->valueLen);
}
writeReply = g_dbus_create_reply(dbusMsg, DBUS_TYPE_INVALID);
exit:
if (NULL != msg)
{
WeaveLogError(Ble, msg);
}
return writeReply;
}
static DBusMessage * CharacteristicStartNotify(DBusConnection * dbusConn, DBusMessage * dbusMsg, void * bluezData)
{
Characteristic * characteristic = static_cast<Characteristic *>(bluezData);
const char * msg = NULL;
DBusMessage * notifyReply = NULL;
VerifyOrExit(characteristic != NULL, msg = "characteristic is NULL in CharacteristicStartNotify");
VerifyOrExit(!characteristic->isNotifying, msg = "Notifying has been enabled in CharacteristicStartNotify");
characteristic->isNotifying = true;
g_dbus_emit_property_changed(dbusConn, characteristic->path, CHARACTERISTIC_INTERFACE, "Notifying");
WeaveLogDetail(Ble, "Characteristic path %s notification enabled", characteristic->path);
if (strcmp(characteristic->uuid, UUID_WEAVE_C2) == 0)
{
WoBLEz_SubscriptionChange(gBluezServerEndpoint);
}
notifyReply = g_dbus_create_reply(dbusMsg, DBUS_TYPE_INVALID);
exit:
if (NULL != msg)
{
WeaveLogError(Ble, msg);
}
return notifyReply;
}
static DBusMessage * CharacteristicStopNotify(DBusConnection * dbusConn, DBusMessage * dbusMsg, void * bluezData)
{
Characteristic * characteristic = static_cast<Characteristic *>(bluezData);
const char * msg = NULL;
DBusMessage * notifyReply = NULL;
VerifyOrExit(characteristic != NULL, msg = "characteristic is NULL in CharacteristicStopNotify");
VerifyOrExit(characteristic->isNotifying, msg = "Notifying has been disabled in CharacteristicStopNotify");
characteristic->isNotifying = false;
g_dbus_emit_property_changed(dbusConn, characteristic->path, CHARACTERISTIC_INTERFACE, "Notifying");
WeaveLogProgress(Ble, "Characteristic path %s notification disabled", characteristic->path);
if (strcmp(characteristic->uuid, UUID_WEAVE_C2) == 0)
{
WoBLEz_SubscriptionChange(gBluezServerEndpoint);
}
notifyReply = g_dbus_create_reply(dbusMsg, DBUS_TYPE_INVALID);
exit:
if (NULL != msg)
{
WeaveLogError(Ble, msg);
}
return notifyReply;
}
static DBusMessage * CharacteristicIndicationConf(DBusConnection * dbusConn, DBusMessage * dbusMsg, void * bluezData)
{
const char * msg = NULL;
Characteristic * characteristic = static_cast<Characteristic *>(bluezData);
VerifyOrExit(characteristic != NULL, msg = "characteristic is NULL in CharacteristicIndicationConf");
WeaveLogDetail(Ble, "Indication confirmation received at %s", characteristic->path);
WoBLEz_IndicationConfirmation(gBluezServerEndpoint);
exit:
if (NULL != msg)
{
WeaveLogDetail(Ble, msg);
}
return g_dbus_create_reply(dbusMsg, DBUS_TYPE_INVALID);
}
#if BLE_CONFIG_BLUEZ_MTU_FEATURE
static gboolean CharacteristicPipeAcquired(const GDBusPropertyTable * property, DBusMessageIter * iter, void * bluezData)
{
gboolean success = FALSE;
dbus_bool_t value = FALSE;
const char * msg = NULL;
Characteristic * characteristic = static_cast<Characteristic *>(bluezData);
VerifyOrExit(characteristic != NULL, msg = "characteristic is NULL in CharacteristicPipeAcquired");
if (strcmp(characteristic->uuid, UUID_WEAVE_C1) == 0)
{
value = (characteristic->writePipeIO != NULL) ? TRUE : FALSE;
}
else if (strcmp(characteristic->uuid, UUID_WEAVE_C2) == 0)
{
value = (characteristic->indicatePipeIO != NULL) ? TRUE : FALSE;
}
else
{
VerifyOrExit(value == TRUE, msg = "writePipeIO or indicatePipeIO is not set in C1 and C2");
}
success = dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
exit:
if (NULL != msg)
{
WeaveLogDetail(Ble, msg);
}
return success;
}
#endif // BLE_CONFIG_BLUEZ_MTU_FEATURE
Characteristic * RegisterWeaveCharacteristic(DBusConnection * dbusConn, const char * uuid, const char * flags)
{
Characteristic * weaveCharacteristic = NULL;
gboolean success = FALSE;
const char * msg = NULL;
weaveCharacteristic = g_new0(Characteristic, 1);
VerifyOrExit(weaveCharacteristic != NULL, msg = "no memory allocated for characteristic in RegisterWeaveCharacteristic");
weaveCharacteristic->dbusConn = dbusConn;
weaveCharacteristic->uuid = g_strdup(uuid);
weaveCharacteristic->value = NULL;
weaveCharacteristic->path =
g_strdup_printf("%s/weaveCharacteristic%p", gBluezServerEndpoint->weaveService->path, weaveCharacteristic);
weaveCharacteristic->servicePath = g_strdup_printf("%s", gBluezServerEndpoint->weaveService->path);
weaveCharacteristic->flags = g_strsplit(flags, ",", -1);
#if BLE_CONFIG_BLUEZ_MTU_FEATURE
weaveCharacteristic->writePipeIO = NULL;
weaveCharacteristic->indicatePipeIO = NULL;
#endif // BLE_CONFIG_BLUEZ_MTU_FEATURE
success = g_dbus_register_interface(dbusConn, weaveCharacteristic->path, CHARACTERISTIC_INTERFACE, weaveCharacteristicMethods,
NULL, WeaveCharacteristicProperties, weaveCharacteristic, WeaveCharacteristicDestroy);
if (FALSE == success)
{
msg = "Failed to register weaveCharacteristic object in RegisterWeaveCharacteristic";
WeaveCharacteristicDestroy(weaveCharacteristic);
weaveCharacteristic = NULL;
}
exit:
if ((success != TRUE) && (msg != NULL))
{
WeaveLogError(Ble, msg);
}
return weaveCharacteristic;
}
static void WeaveClientConnectHandler(DBusConnection * connection, void * bluezData)
{
WeaveLogProgress(Ble, "Weave client connected to bluez daemon via dbus");
}
static void WeaveClientDisconnectHandler(DBusConnection * connection, void * bluezData)
{
WeaveLogError(Ble, "Weave client disconnected from bluez daemon(Daemon crash?)");
// Recovery from bluez daemon crash not implemented. So, exiting from mainloop.
ExitBluezIOThread();
}
static gboolean CheckDeviceIsChild(GDBusProxy * childProxy, GDBusProxy * parentProxy)
{
DBusMessageIter iter;
const char * adapterPath1 = NULL;
const char * adapterPath2 = NULL;
const char * msg = NULL;
bool success = false;
VerifyOrExit(NULL != parentProxy, msg = "parentProxy is NULL");
VerifyOrExit(NULL != childProxy, msg = "childProxy is NULL");
if (g_dbus_proxy_get_property(childProxy, "Adapter", &iter))
{
dbus_message_iter_get_basic(&iter, &adapterPath1);
adapterPath2 = g_dbus_proxy_get_path(parentProxy);
if (strcmp(adapterPath1, adapterPath2) == 0)
{
success = true;
}
}
exit:
if (msg != NULL)
{
WeaveLogDetail(Ble, msg);
}
return success;
}
static void WeaveAdapterAdded(GDBusProxy * proxy)
{
DBusMessageIter iter;
const char * addr = NULL;
bool proxyAdded = false;
dbus_bool_t powered = TRUE;
if (g_dbus_proxy_get_property(proxy, "Address", &iter))
{
dbus_message_iter_get_basic(&iter, &addr);
if (!strcasecmp(addr, gBluezServerEndpoint->adapterAddr))
{
if (gDefaultAdapter)
{
gDefaultAdapter->adapterProxy = proxy;
gDefaultAdapter->advertisingProxy = NULL;
gDefaultAdapter->profileProxy = NULL;
gDefaultAdapter->deviceProxies.clear();
proxyAdded = true;
WeaveLogProgress(Ble, "%p(%s) added as default adapter proxy", proxy, addr);
if (FALSE == g_dbus_proxy_set_property_basic(proxy, "Powered", DBUS_TYPE_BOOLEAN, &powered, PowerCb, NULL, NULL))
{
WeaveLogError(Ble, "Fail to set Powered property for adapter %p(%s)", proxy, addr);
}
}
}
}
if (!proxyAdded)
{
WeaveLogDetail(Ble, "Adaptor proxy %p(%s) ignored", proxy, addr);
}
}
static void WeaveProfileAdded(GDBusProxy * proxy)
{
if (gDefaultAdapter && gDefaultAdapter->adapterProxy)
{
if (strcmp(g_dbus_proxy_get_path(proxy), g_dbus_proxy_get_path(gDefaultAdapter->adapterProxy)) == 0)
{
WeaveLogProgress(Ble, "%p added as default profile(Gatt manager) proxy", proxy);
gDefaultAdapter->profileProxy = proxy;
}
}
}
static void WeaveAdvertisingAdded(GDBusProxy * proxy)
{
if (gDefaultAdapter && gDefaultAdapter->adapterProxy)
{
if (strcmp(g_dbus_proxy_get_path(proxy), g_dbus_proxy_get_path(gDefaultAdapter->adapterProxy)) == 0)
{
WeaveLogProgress(Ble, "%p added as default advertising manager proxy", proxy);
gDefaultAdapter->advertisingProxy = proxy;
}
}
}
static void WeaveDeviceAdded(GDBusProxy *proxy)
{
const char * devAddr = NULL;
DBusMessageIter iter;
if (gDefaultAdapter && CheckDeviceIsChild(proxy, gDefaultAdapter->adapterProxy))
{
if (g_dbus_proxy_get_property(proxy, "Address", &iter))
{
dbus_message_iter_get_basic(&iter, &devAddr);
WeaveLogProgress(Ble, "%p(%s) added to device proxy list", proxy, devAddr);
}
gDefaultAdapter->deviceProxies.push_back(proxy);
}
}
static void WeaveProxyAdded(GDBusProxy * proxy, void * bluezData)
{
const char *interface = NULL;
interface = g_dbus_proxy_get_interface(proxy);
if (!strcmp(interface, ADAPTER_INTERFACE))
{
WeaveAdapterAdded(proxy);
}
else if (!strcmp(interface, PROFILE_INTERFACE))
{
WeaveProfileAdded(proxy);
}
else if (!strcmp(interface, ADVERTISING_MANAGER_INTERFACE))
{
WeaveAdvertisingAdded(proxy);
}
else if (!strcmp(interface, DEVICE_INTERFACE))
{
WeaveDeviceAdded(proxy);
}
}
static void WeaveProxyDeleted(GDBusProxy * proxy, void * bluezData)
{
const char *interface = NULL;
interface = g_dbus_proxy_get_interface(proxy);
if (!strcmp(interface, ADAPTER_INTERFACE))
{
WeaveLogProgress(Ble, "Got notification about %p adaptor proxy removal", proxy);
if (gDefaultAdapter && gDefaultAdapter->adapterProxy == proxy)
{
gDefaultAdapter->adapterProxy = NULL;
}
}
else if (!strcmp(interface, PROFILE_INTERFACE))
{
WeaveLogProgress(Ble, "Got notification about %p profile(gatt manager) proxy removal", proxy);
if (gDefaultAdapter && gDefaultAdapter->profileProxy == proxy)
{
gDefaultAdapter->profileProxy = NULL;
}
}
else if (!strcmp(interface, ADVERTISING_MANAGER_INTERFACE))
{
WeaveLogProgress(Ble, "Got notification about %p advertising manager proxy removal", proxy);
if (gDefaultAdapter && gDefaultAdapter->advertisingProxy == proxy)
{
gDefaultAdapter->advertisingProxy = NULL;
}
}
else if (!strcmp(interface, DEVICE_INTERFACE))
{
WeaveLogProgress(Ble, "Got notification about %p device proxy removal", proxy);
if (gDefaultAdapter && !(gDefaultAdapter->deviceProxies.empty()))
{
gDefaultAdapter->deviceProxies.remove(proxy);
}
}
}
static void WeaveDisconnReply(DBusMessage * dbusMsg, void * bluezData)
{
DBusError error;
dbus_error_init(&error);
if (TRUE == dbus_set_error_from_message(&error, dbusMsg))
{
WeaveLogError(Ble, "failed to disconnect with error: %s", error.name);
dbus_error_free(&error);
}
}
static void WeaveDeviceDisconnect(GDBusProxy * proxy)
{
dbus_bool_t connected;
const char * devAddr = NULL;
DBusMessageIter iter;
if (g_dbus_proxy_get_property(proxy, "Connected", &iter))
{
dbus_message_iter_get_basic(&iter, &connected);
if (connected)
{
if (g_dbus_proxy_get_property(proxy, "Address", &iter))
{
dbus_message_iter_get_basic(&iter, &devAddr);
WeaveLogRetain(Ble, "Issuing disconnect to device:%s", devAddr);
}
g_dbus_proxy_method_call(proxy, "Disconnect", NULL, WeaveDisconnReply, proxy, NULL);
}
}
}
static void WeavePropertyChange(GDBusProxy *proxy, const char *name, DBusMessageIter *iter, void *bluezData)
{
const char *interface = NULL;
dbus_bool_t connected;
const char * devAddr = NULL;
DBusMessageIter addrIter;
interface = g_dbus_proxy_get_interface(proxy);
if (!strcmp(interface, DEVICE_INTERFACE)) {
if (CheckDeviceIsChild(proxy, gDefaultAdapter->adapterProxy)) {
if (strcmp(name, "Connected") == 0)
{
dbus_message_iter_get_basic(iter, &connected);
if (g_dbus_proxy_get_property(proxy, "Address", &addrIter))
{
dbus_message_iter_get_basic(&addrIter, &devAddr);
WeaveLogRetain(Ble, "%s device %p(%s)", connected?"Connected to":"Disconnected with", proxy, devAddr);
}
if (connected)
{
gBluezBleApplicationDelegate->NotifyBleActivity(kBleConnect);
WoBLEz_NewConnection(gBluezServerEndpoint);
}
else
{
WoBLEz_ConnectionClosed(gBluezServerEndpoint);
CloseBleconnection();
gBluezBleApplicationDelegate->NotifyBleActivity(kBleDisconnect);
}
}
}
}
}
static void PowerCb(const DBusError * error, void * bluezData)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
gboolean success = FALSE;
VerifyOrExit(!dbus_error_is_set(error), err = WEAVE_ERROR_INCORRECT_STATE);
success = RegisterWeaveService(gBluezDbusConn);
VerifyOrExit(success == TRUE, err = WEAVE_ERROR_INCORRECT_STATE);
gBluezServerEndpoint->weaveC1 = RegisterWeaveCharacteristic(gBluezDbusConn, UUID_WEAVE_C1, FLAGS_WEAVE_C1);
VerifyOrExit(gBluezServerEndpoint->weaveC1 != NULL, err = WEAVE_ERROR_NO_MEMORY);
WeaveLogDetail(Ble, "weave C1 uuid: %s, path: %s", gBluezServerEndpoint->weaveC1->uuid, gBluezServerEndpoint->weaveC1->path);
gBluezServerEndpoint->weaveC2 = RegisterWeaveCharacteristic(gBluezDbusConn, UUID_WEAVE_C2, FLAGS_WEAVE_C2);
VerifyOrExit(gBluezServerEndpoint->weaveC2 != NULL, err = WEAVE_ERROR_NO_MEMORY);
WeaveLogDetail(Ble, "weave C2 uuid: %s, path: %s", gBluezServerEndpoint->weaveC2->uuid, gBluezServerEndpoint->weaveC2->path);
success = SetupWeaveApp(gBluezDbusConn, gDefaultAdapter->profileProxy);
VerifyOrExit(success == TRUE, err = WEAVE_ERROR_INCORRECT_STATE);
success = EnableDiscoverable();
VerifyOrExit(success == TRUE, err = WEAVE_ERROR_INCORRECT_STATE);
success = SetAlias();
VerifyOrExit(success == TRUE, err = WEAVE_ERROR_INCORRECT_STATE);
success = AdvertisingRegister(gBluezDbusConn, gDefaultAdapter->advertisingProxy);
VerifyOrExit(success == TRUE, err = WEAVE_ERROR_INCORRECT_STATE);
exit:
if (err != WEAVE_NO_ERROR)
{
WeaveLogError(Ble, "PowerCb failed: %d", err);
}
}
static void WeaveClientReady(GDBusClient * weaveClient, void * bluezData)
{
WeaveLogProgress(Ble, "Weave client is ready");
return;
}
uint16_t GetMTUWeaveCb(BLE_CONNECTION_OBJECT connObj)
{
uint16_t mtu = gBluezServerEndpoint->mtu;
WeaveLogDetail(Ble, "GetMTU: %d", mtu);
return mtu;
}
void CloseBleconnection()
{
if (gDefaultAdapter && !(gDefaultAdapter->deviceProxies.empty()))
{
// Check for connected device & close the connection
for (auto iter = gDefaultAdapter->deviceProxies.begin();
iter != gDefaultAdapter->deviceProxies.end(); iter++)
{
WeaveDeviceDisconnect(*iter);
}
}
gBluezServerEndpoint->weaveC2->isNotifying = false;
g_dbus_emit_property_changed(gBluezServerEndpoint->weaveC2->dbusConn, gBluezServerEndpoint->weaveC2->path,
CHARACTERISTIC_INTERFACE, "Notifying");
#if BLE_CONFIG_BLUEZ_MTU_FEATURE
PipeIODestroy(gBluezServerEndpoint->weaveC2->indicatePipeIO, gBluezServerEndpoint->weaveC2);
PipeIODestroy(gBluezServerEndpoint->weaveC1->writePipeIO, gBluezServerEndpoint->weaveC1);
#endif //BLE_CONFIG_BLUEZ_MTU_FEATURE
}
void ExitBluezIOThread(void)
{
g_main_loop_quit(gBluezMainLoop);
}
bool RunOnBluezIOThread(int (*aCallback)(void *), void * aClosure)
{
GMainContext * context = NULL;
const char * msg = NULL;
VerifyOrExit(gBluezMainLoop != NULL, msg = "RunOnBluezIOThread: BlueZ mainloop is NULL");
VerifyOrExit(g_main_loop_is_running(gBluezMainLoop), msg = "RunOnBluezIOThread: mainloop is not running");
context = g_main_loop_get_context(gBluezMainLoop);
VerifyOrExit(context != NULL, msg = "RunOnBluezIOThread: main context is NULL");
g_main_context_invoke(context, aCallback, aClosure);
exit:
if (msg != NULL)
{
WeaveLogError(Ble, msg);
}
return msg == NULL;
}
bool RunBluezIOThread(BluezPeripheralArgs * arg)
{
GDBusClient * weaveClient = NULL;
const char * msg = NULL;
gboolean success = FALSE;
WEAVE_ERROR err = WEAVE_NO_ERROR;
const char * advertisingType = "peripheral";
VerifyOrExit(arg != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
VerifyOrExit(arg->bluezBleApplicationDelegate != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
gBluezBleApplicationDelegate = arg->bluezBleApplicationDelegate;
VerifyOrExit(arg->bluezBlePlatformDelegate != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
gBluezBlePlatformDelegate = arg->bluezBlePlatformDelegate;
gBluezBlePlatformDelegate->SetSendIndicationCallback(WoBLEz_ScheduleSendIndication);
gBluezBlePlatformDelegate->SetGetMTUCallback(GetMTUWeaveCb);
gDefaultAdapter = new Adapter();
VerifyOrExit(gDefaultAdapter != NULL, err = WEAVE_ERROR_NO_MEMORY);
gBluezServerEndpoint = (BluezServerEndpoint *) g_new0(BluezServerEndpoint, 1);
VerifyOrExit(gBluezServerEndpoint != NULL, err = WEAVE_ERROR_NO_MEMORY);
gBluezServerEndpoint->adapterName = g_strdup(arg->bleName);
gBluezServerEndpoint->adapterAddr = g_strdup(arg->bleAddress);
gBluezServerEndpoint->advertisingUUID = g_strdup(UUID_WEAVE_SHORT);
gBluezServerEndpoint->advertisingType = g_strdup(advertisingType);
gBluezServerEndpoint->weaveServiceData = g_new0(WeaveServiceData, 1);
VerifyOrExit(gBluezServerEndpoint->weaveServiceData != NULL, err = WEAVE_ERROR_NO_MEMORY);
/**
* Data arranged in "Length Type Value" pairs inside Weave service data.
* Length should include size of value + size of Type field, which is 1 byte
*/
gBluezServerEndpoint->weaveServiceData->dataBlock0Len = sizeof(WeaveIdInfo) + 1;
gBluezServerEndpoint->weaveServiceData->dataBlock0Type = WEAVE_SRV_DATA_BLOCK_TYPE_WEAVE_ID_INFO;
gBluezServerEndpoint->weaveServiceData->weaveIdInfo.major = WEAVE_ID_INFO_MAJ_VER;
gBluezServerEndpoint->weaveServiceData->weaveIdInfo.minor = WEAVE_ID_INFO_MIN_VER;
gBluezServerEndpoint->weaveServiceData->weaveIdInfo.vendorId = arg->vendorId;
gBluezServerEndpoint->weaveServiceData->weaveIdInfo.productId = arg->productId;
gBluezServerEndpoint->weaveServiceData->weaveIdInfo.deviceId = arg->deviceId;
gBluezServerEndpoint->weaveServiceData->weaveIdInfo.pairingStatus = arg->pairingStatus;
gBluezServerEndpoint->mtu = HCI_MAX_MTU;
gBluezMainLoop = g_main_loop_new(NULL, FALSE);
gBluezDbusConn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
VerifyOrExit(gBluezDbusConn != NULL, err = WEAVE_ERROR_NO_MEMORY);
success = g_dbus_attach_object_manager(gBluezDbusConn);
VerifyOrExit(success == TRUE, msg = "Fail to attach object manager in RunBluezIOThread");
weaveClient = g_dbus_client_new(gBluezDbusConn, BLUEZ_INTERFACE, BLUEZ_PATH);
VerifyOrExit(weaveClient != NULL, err = WEAVE_ERROR_NO_MEMORY);
success = g_dbus_client_set_proxy_handlers(weaveClient, WeaveProxyAdded, WeaveProxyDeleted, WeavePropertyChange, NULL);
VerifyOrExit(success == TRUE, msg = "Fail to set weave proxy handler in RunBluezIOThread");
success = g_dbus_client_set_ready_watch(weaveClient, WeaveClientReady, NULL);
VerifyOrExit(success == TRUE, msg = "Fail to set ready watch for weave client in RunBluezIOThread");
success = g_dbus_client_set_connect_watch(weaveClient, WeaveClientConnectHandler, NULL);
VerifyOrExit(success == TRUE, msg = "Fail to set connect watch for weave client in RunBluezIOThread");
success = g_dbus_client_set_disconnect_watch(weaveClient, WeaveClientDisconnectHandler, NULL);
VerifyOrExit(success == TRUE, msg = "Fail to set disconnect watch for weave client in RunBluezIOThread");
g_main_loop_run(gBluezMainLoop);
WeaveLogProgress(Ble, "Exited from Bluez main loop");
exit:
if (err != WEAVE_NO_ERROR)
{
success = FALSE;
WeaveLogError(Ble, "RunBluezIOThread failed: %d", err);
}
if ((success != TRUE) && (msg != NULL))
{
WeaveLogError(Ble, msg);
}
if (gDefaultAdapter && gDefaultAdapter->advertisingProxy)
{
WeaveLogProgress(Ble, "Unregistering weave advertisement");
if (FALSE == g_dbus_proxy_method_call(gDefaultAdapter->advertisingProxy,
"UnregisterAdvertisement",
WeaveUnregisterSetup, WeaveUnregisterReply,
gBluezDbusConn, NULL))
{
WeaveLogError(Ble, "Fail to call UnregisterAdvertisement method");
}
if (FALSE == g_dbus_unregister_interface(gBluezDbusConn, ADVERTISING_PATH,
ADVERTISING_INTERFACE))
{
WeaveLogError(Ble, "Fail to unregister weave advertisement object");
}
}
if (NULL != gBluezServerEndpoint)
{
if (NULL != gBluezServerEndpoint->weaveService)
{
ServiceDestroy(gBluezServerEndpoint->weaveService);
gBluezServerEndpoint->weaveService = NULL;
}
if (NULL != gBluezServerEndpoint->weaveC1)
{
WeaveCharacteristicDestroy(gBluezServerEndpoint->weaveC1);
gBluezServerEndpoint->weaveC1 = NULL;
}
if (NULL != gBluezServerEndpoint->weaveC2)
{
WeaveCharacteristicDestroy(gBluezServerEndpoint->weaveC2);
gBluezServerEndpoint->weaveC2 = NULL;
}
g_free(gBluezServerEndpoint->adapterName);
gBluezServerEndpoint->adapterName = NULL;
g_free(gBluezServerEndpoint->adapterAddr);
gBluezServerEndpoint->adapterAddr = NULL;
g_free(gBluezServerEndpoint->advertisingUUID);
gBluezServerEndpoint->advertisingUUID = NULL;
g_free(gBluezServerEndpoint->advertisingType);
gBluezServerEndpoint->advertisingType = NULL;
g_free(gBluezServerEndpoint->weaveServiceData);
gBluezServerEndpoint->weaveServiceData = NULL;
g_free(gBluezServerEndpoint);
gBluezServerEndpoint = NULL;
}
if (NULL != gDefaultAdapter)
{
delete gDefaultAdapter;
gDefaultAdapter = NULL;
}
if (NULL != weaveClient)
{
g_dbus_client_unref(weaveClient);
}
if (NULL != gBluezDbusConn)
{
dbus_connection_unref(gBluezDbusConn);
gBluezDbusConn = NULL;
}
if (NULL != gBluezMainLoop)
{
g_main_loop_unref(gBluezMainLoop);
gBluezMainLoop = NULL;
}
gBluezBlePlatformDelegate = NULL;
gBluezBleApplicationDelegate = NULL;
return success;
}
} // namespace BlueZ
} /* namespace Platform */
} /* namespace Ble */
} /* namespace nl */
#endif /* CONFIG_BLE_PLATFORM_BLUEZ */