137 explicit TinyCTSBlueZPeripheral(
const std::string& adapter) : _adapterName(adapter) {}
138 ~TinyCTSBlueZPeripheral()
override {
stop(); }
140 bool start(
const std::string& name,
bool settable,
bool encrypted,
144 _settable = settable;
145 _encrypted = encrypted;
149 if (conn ==
nullptr) {
154 _nodeInfo = g_dbus_node_info_new_for_xml(xml(),
nullptr);
158 if (!exportObject(App,
"org.freedesktop.DBus.ObjectManager") ||
159 !exportObject(Service,
"org.bluez.GattService1") ||
160 !exportObject(_chars[0].path.c_str(),
"org.bluez.GattCharacteristic1") ||
161 !exportObject(_chars[1].path.c_str(),
"org.bluez.GattCharacteristic1") ||
162 !exportObject(_chars[2].path.c_str(),
"org.bluez.GattCharacteristic1") ||
163 !exportObject(Advertisement,
"org.bluez.LEAdvertisement1")) {
172 g_dbus_connection_call(conn,
"org.bluez", _adapter.c_str(),
"org.bluez.GattManager1",
173 "RegisterApplication", g_variant_new_parsed(
"(%o, @a{sv} {})", App),
174 nullptr, G_DBUS_CALL_FLAGS_NONE, 20000,
nullptr, onRegistered,
this);
175 g_dbus_connection_call(conn,
"org.bluez", _adapter.c_str(),
176 "org.bluez.LEAdvertisingManager1",
"RegisterAdvertisement",
177 g_variant_new_parsed(
"(%o, @a{sv} {})", Advertisement),
nullptr,
178 G_DBUS_CALL_FLAGS_NONE, 20000,
nullptr, onRegistered,
this);
179 for (
int i = 0; i < 4000 && _pending > 0; i++) {
183 if (_pending > 0 && _error.empty()) _error =
"BlueZ does not answer";
184 if (!_error.empty()) {
194 while (g_main_context_iteration(
nullptr, FALSE)) {
198 void notify(uint16_t uuid,
const std::vector<uint8_t>& value)
override {
199 if (!_notifying)
return;
200 for (
const Chr& c : _chars) {
201 if (c.uuid == uuid) emitValue(c, value);
209 g_variant_new(
"(o)", App), ignored, 2000);
210 TinyCTSBlueZ::call(_adapter,
"org.bluez.LEAdvertisingManager1",
"UnregisterAdvertisement",
211 g_variant_new(
"(o)", Advertisement), ignored, 2000);
215 for (guint
id : _ids) {
216 if (conn !=
nullptr) g_dbus_connection_unregister_object(conn,
id);
219 if (_nodeInfo !=
nullptr) g_dbus_node_info_unref(_nodeInfo);
226 static constexpr const char* App =
"/org/tinycts";
227 static constexpr const char* Service =
"/org/tinycts/service0";
228 static constexpr const char* Advertisement =
"/org/tinycts/adv0";
234 bool current =
false;
237 static const char* xml() {
239 " <interface name='org.freedesktop.DBus.ObjectManager'>"
240 " <method name='GetManagedObjects'>"
241 " <arg type='a{oa{sa{sv}}}' name='objects' direction='out'/>"
244 " <interface name='org.bluez.GattService1'>"
245 " <property name='UUID' type='s' access='read'/>"
246 " <property name='Primary' type='b' access='read'/>"
248 " <interface name='org.bluez.GattCharacteristic1'>"
249 " <method name='ReadValue'>"
250 " <arg type='a{sv}' name='options' direction='in'/>"
251 " <arg type='ay' name='value' direction='out'/>"
253 " <method name='WriteValue'>"
254 " <arg type='ay' name='value' direction='in'/>"
255 " <arg type='a{sv}' name='options' direction='in'/>"
257 " <method name='StartNotify'/>"
258 " <method name='StopNotify'/>"
259 " <property name='UUID' type='s' access='read'/>"
260 " <property name='Service' type='o' access='read'/>"
261 " <property name='Flags' type='as' access='read'/>"
262 " <property name='Notifying' type='b' access='read'/>"
264 " <interface name='org.bluez.LEAdvertisement1'>"
265 " <method name='Release'/>"
266 " <property name='Type' type='s' access='read'/>"
267 " <property name='ServiceUUIDs' type='as' access='read'/>"
268 " <property name='LocalName' type='s' access='read'/>"
273 bool exportObject(
const char* path,
const char* iface) {
274 static const GDBusInterfaceVTable vtable = {onCall, onGet,
nullptr, {}};
275 GError* err =
nullptr;
277 GDBusInterfaceInfo* info = g_dbus_node_info_lookup_interface(_nodeInfo, iface);
278 guint
id = conn ==
nullptr ? 0
279 : g_dbus_connection_register_object(conn, path, info, &vtable,
280 this,
nullptr, &err);
282 if (err !=
nullptr) {
283 _error = std::string(
"cannot export ") + path +
": " + err->message;
292 static void onRegistered(GObject* source, GAsyncResult* res, gpointer self) {
293 TinyCTSBlueZPeripheral* s =
static_cast<TinyCTSBlueZPeripheral*
>(self);
294 GError* err =
nullptr;
295 GVariant* r = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), res, &err);
298 g_dbus_error_strip_remote_error(err);
299 s->_error = std::string(
"BlueZ refused the registration: ") + err->message;
306 const Chr* find(
const char* path)
const {
307 for (
const Chr& c : _chars) {
308 if (c.path == path)
return &c;
313 static GVariant* bytes(
const std::vector<uint8_t>& v) {
314 return g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, v.data(), v.size(), 1);
317 GVariant* flags(
const Chr& c)
const {
319 g_variant_builder_init(&b, G_VARIANT_TYPE(
"as"));
320 g_variant_builder_add(&b,
"s",
"read");
321 if (c.current) g_variant_builder_add(&b,
"s",
"notify");
322 if (_settable && c.uuid != CTS_REFERENCE_TIME_INFO_UUID) {
323 g_variant_builder_add(&b,
"s", _encrypted ?
"encrypt-write" :
"write");
325 return g_variant_builder_end(&b);
328 GVariant* managed() {
329 GVariantBuilder objects;
330 g_variant_builder_init(&objects, G_VARIANT_TYPE(
"a{oa{sa{sv}}}"));
331 GVariant* service = g_variant_new_parsed(
332 "{'org.bluez.GattService1': {'UUID': <%s>, 'Primary': <true>}}",
334 g_variant_builder_add(&objects,
"{o@a{sa{sv}}}", Service, service);
335 for (
const Chr& c : _chars) {
336 GVariantBuilder props;
337 g_variant_builder_init(&props, G_VARIANT_TYPE(
"a{sv}"));
338 g_variant_builder_add(&props,
"{sv}",
"UUID",
340 g_variant_builder_add(&props,
"{sv}",
"Service", g_variant_new_object_path(Service));
341 g_variant_builder_add(&props,
"{sv}",
"Flags", flags(c));
342 GVariantBuilder ifaces;
343 g_variant_builder_init(&ifaces, G_VARIANT_TYPE(
"a{sa{sv}}"));
344 g_variant_builder_add(&ifaces,
"{sa{sv}}",
"org.bluez.GattCharacteristic1", &props);
345 g_variant_builder_add(&objects,
"{oa{sa{sv}}}", c.path.c_str(), &ifaces);
347 return g_variant_new(
"(a{oa{sa{sv}}})", &objects);
350 void emitValue(
const Chr& c,
const std::vector<uint8_t>& value) {
353 if (conn ==
nullptr)
return;
354 GVariantBuilder changed;
355 g_variant_builder_init(&changed, G_VARIANT_TYPE(
"a{sv}"));
356 g_variant_builder_add(&changed,
"{sv}",
"Value", bytes(value));
357 g_dbus_connection_emit_signal(
358 conn,
nullptr, c.path.c_str(),
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
359 g_variant_new(
"(sa{sv}as)",
"org.bluez.GattCharacteristic1", &changed,
nullptr),
363 static void onCall(GDBusConnection*,
const gchar*,
const gchar* path,
const gchar*,
364 const gchar* method, GVariant* params, GDBusMethodInvocation* inv,
366 TinyCTSBlueZPeripheral* s =
static_cast<TinyCTSBlueZPeripheral*
>(self);
367 if (!strcmp(method,
"GetManagedObjects")) {
368 g_dbus_method_invocation_return_value(inv, s->managed());
369 }
else if (!strcmp(method,
"ReadValue")) {
370 const Chr* c = s->find(path);
371 std::vector<uint8_t> value;
372 if (c ==
nullptr || s->_handler ==
nullptr || !s->_handler->onRead(c->uuid, value)) {
373 g_dbus_method_invocation_return_dbus_error(inv,
"org.bluez.Error.Failed",
"unknown");
376 g_dbus_method_invocation_return_value(inv, g_variant_new(
"(@ay)", bytes(value)));
377 }
else if (!strcmp(method,
"WriteValue")) {
378 const Chr* c = s->find(path);
381 g_variant_get(params,
"(@ay@a{sv})", &data, &options);
383 const uint8_t* d =
static_cast<const uint8_t*
>(g_variant_get_fixed_array(data, &n, 1));
384 bool ok = c !=
nullptr && s->_handler !=
nullptr && s->_handler->onWrite(c->uuid, d, n);
385 g_variant_unref(data);
386 g_variant_unref(options);
388 g_dbus_method_invocation_return_value(inv,
nullptr);
390 g_dbus_method_invocation_return_dbus_error(inv,
"org.bluez.Error.NotPermitted",
391 "the time cannot be set");
393 }
else if (!strcmp(method,
"StartNotify")) {
394 s->_notifying =
true;
395 g_dbus_method_invocation_return_value(inv,
nullptr);
396 }
else if (!strcmp(method,
"StopNotify")) {
397 s->_notifying =
false;
398 g_dbus_method_invocation_return_value(inv,
nullptr);
400 g_dbus_method_invocation_return_value(inv,
nullptr);
404 static GVariant* onGet(GDBusConnection*,
const gchar*,
const gchar* path,
const gchar* iface,
405 const gchar* prop, GError**, gpointer self) {
406 TinyCTSBlueZPeripheral* s =
static_cast<TinyCTSBlueZPeripheral*
>(self);
407 if (!strcmp(iface,
"org.bluez.GattService1")) {
408 if (!strcmp(prop,
"UUID")) {
411 return g_variant_new_boolean(TRUE);
413 if (!strcmp(iface,
"org.bluez.LEAdvertisement1")) {
414 if (!strcmp(prop,
"Type"))
return g_variant_new_string(
"peripheral");
415 if (!strcmp(prop,
"LocalName"))
return g_variant_new_string(s->_name.c_str());
417 const gchar* uuids[] = {u.c_str(),
nullptr};
418 return g_variant_new_strv(uuids, 1);
420 const Chr* c = s->find(path);
421 if (c ==
nullptr)
return nullptr;
423 if (!strcmp(prop,
"Service"))
return g_variant_new_object_path(Service);
424 if (!strcmp(prop,
"Notifying"))
return g_variant_new_boolean(s->_notifying);
428 std::string _adapterName, _adapter, _name, _error;
429 TinyCTSPeripheralHandler* _handler =
nullptr;
430 GDBusNodeInfo* _nodeInfo =
nullptr;
431 std::vector<guint> _ids;
434 bool _settable =
false, _encrypted =
true;
435 bool _started =
false, _registered =
false, _notifying =
false;
A device that was found by TinyCTSClient::scan().
Definition TinyCTSDesktop.h:18