Coverage Report

Created: 2024-12-17 06:44

/src/connectedhomeip/src/platform/Linux/bluez/BluezEndpoint.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 *
3
 *    Copyright (c) 2020-2022 Project CHIP Authors
4
 *
5
 *    Licensed under the Apache License, Version 2.0 (the "License");
6
 *    you may not use this file except in compliance with the License.
7
 *    You may obtain a copy of the License at
8
 *
9
 *        http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 *    Unless required by applicable law or agreed to in writing, software
12
 *    distributed under the License is distributed on an "AS IS" BASIS,
13
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 *    See the License for the specific language governing permissions and
15
 *    limitations under the License.
16
 */
17
18
/*
19
 *  Copyright (c) 2016-2019, The OpenThread Authors.
20
 *  All rights reserved.
21
 *
22
 *  Redistribution and use in source and binary forms, with or without
23
 *  modification, are permitted provided that the following conditions are met:
24
 *  1. Redistributions of source code must retain the above copyright
25
 *     notice, this list of conditions and the following disclaimer.
26
 *  2. Redistributions in binary form must reproduce the above copyright
27
 *     notice, this list of conditions and the following disclaimer in the
28
 *     documentation and/or other materials provided with the distribution.
29
 *  3. Neither the name of the copyright holder nor the
30
 *     names of its contributors may be used to endorse or promote products
31
 *     derived from this software without specific prior written permission.
32
 *
33
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
34
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
35
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
37
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
38
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
39
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
40
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
41
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
43
 *  POSSIBILITY OF SUCH DAMAGE.
44
 */
45
46
#include "BluezEndpoint.h"
47
48
#include <cstring>
49
#include <errno.h>
50
#include <memory>
51
#include <sys/socket.h>
52
#include <unistd.h>
53
#include <utility>
54
55
#include <gio/gio.h>
56
#include <gio/gunixfdlist.h>
57
#include <glib-object.h>
58
#include <glib.h>
59
60
#include <ble/Ble.h>
61
#include <lib/support/BitFlags.h>
62
#include <lib/support/CHIPMem.h>
63
#include <lib/support/CodeUtils.h>
64
#include <lib/support/logging/CHIPLogging.h>
65
#include <platform/ConfigurationManager.h>
66
#include <platform/ConnectivityManager.h>
67
#include <platform/DeviceInstanceInfoProvider.h>
68
#include <platform/GLibTypeDeleter.h>
69
#include <platform/Linux/dbus/bluez/DbusBluez.h>
70
#include <platform/PlatformManager.h>
71
#include <platform/internal/BLEManager.h>
72
#include <setup_payload/AdditionalDataPayloadGenerator.h>
73
#include <system/SystemPacketBuffer.h>
74
75
#include "BluezConnection.h"
76
#include "Types.h"
77
78
#if !GLIB_CHECK_VERSION(2, 68, 0)
79
#define g_memdup2(mem, size) g_memdup(mem, static_cast<unsigned int>(size))
80
#endif
81
82
namespace chip {
83
namespace DeviceLayer {
84
namespace Internal {
85
86
constexpr uint16_t kMaxConnectRetries = 4;
87
88
gboolean BluezEndpoint::BluezCharacteristicReadValue(BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInvocation,
89
                                                     GVariant * aOptions)
90
0
{
91
0
    ChipLogDetail(DeviceLayer, "Received %s", __func__);
92
0
    GVariant * val = bluez_gatt_characteristic1_get_value(aChar);
93
0
    bluez_gatt_characteristic1_complete_read_value(aChar, aInvocation, val);
94
0
    return TRUE;
95
0
}
96
97
gboolean BluezEndpoint::BluezCharacteristicAcquireWrite(BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInvocation,
98
                                                        GUnixFDList * aFDList, GVariant * aOptions)
99
0
{
100
0
    int fds[2] = { -1, -1 };
101
0
#if CHIP_ERROR_LOGGING
102
0
    char * errStr;
103
0
#endif // CHIP_ERROR_LOGGING
104
0
    BluezConnection * conn = nullptr;
105
0
    GAutoPtr<GVariant> option_mtu;
106
0
    uint16_t mtu;
107
108
0
    conn = GetBluezConnectionViaDevice();
109
0
    VerifyOrReturnValue(
110
0
        conn != nullptr, FALSE,
111
0
        g_dbus_method_invocation_return_dbus_error(aInvocation, "org.bluez.Error.Failed", "No CHIPoBLE connection"));
112
113
0
    ChipLogDetail(DeviceLayer, "BluezCharacteristicAcquireWrite is called, conn: %p", conn);
114
115
0
    VerifyOrReturnValue(
116
0
        g_variant_lookup(aOptions, "mtu", "q", &mtu), FALSE, ChipLogError(DeviceLayer, "FAIL: No MTU in options in %s", __func__);
117
0
        g_dbus_method_invocation_return_dbus_error(aInvocation, "org.bluez.Error.InvalidArguments", "MTU negotiation failed"));
118
0
    conn->SetMTU(mtu);
119
120
0
    if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, fds) < 0)
121
0
    {
122
0
#if CHIP_ERROR_LOGGING
123
0
        errStr = strerror(errno);
124
0
#endif // CHIP_ERROR_LOGGING
125
0
        ChipLogError(DeviceLayer, "FAIL: socketpair: %s in %s", StringOrNullMarker(errStr), __func__);
126
0
        g_dbus_method_invocation_return_dbus_error(aInvocation, "org.bluez.Error.Failed", "FD creation failed");
127
0
        return FALSE;
128
0
    }
129
130
0
    conn->SetupWriteHandler(fds[0]);
131
0
    bluez_gatt_characteristic1_set_write_acquired(aChar, TRUE);
132
133
0
    GUnixFDList * fdList = g_unix_fd_list_new_from_array(&fds[1], 1);
134
0
    bluez_gatt_characteristic1_complete_acquire_write(aChar, aInvocation, fdList, g_variant_new_handle(0), conn->GetMTU());
135
0
    g_object_unref(fdList);
136
137
0
    return TRUE;
138
0
}
139
140
static gboolean BluezCharacteristicAcquireWriteError(BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInvocation,
141
                                                     GUnixFDList * aFDList, GVariant * aOptions)
142
0
{
143
0
    ChipLogDetail(DeviceLayer, "Received %s", __func__);
144
0
    g_dbus_method_invocation_return_dbus_error(aInvocation, "org.bluez.Error.NotSupported",
145
0
                                               "AcquireWrite for characteristic is unsupported");
146
0
    return TRUE;
147
0
}
148
149
gboolean BluezEndpoint::BluezCharacteristicAcquireNotify(BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInvocation,
150
                                                         GUnixFDList * aFDList, GVariant * aOptions)
151
0
{
152
0
    int fds[2] = { -1, -1 };
153
0
#if CHIP_ERROR_LOGGING
154
0
    char * errStr;
155
0
#endif // CHIP_ERROR_LOGGING
156
0
    BluezConnection * conn       = nullptr;
157
0
    bool isAdditionalAdvertising = false;
158
0
    GAutoPtr<GVariant> option_mtu;
159
0
    uint16_t mtu;
160
161
#if CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING
162
    isAdditionalAdvertising = (aChar == mC3.get());
163
#endif
164
165
0
    if (bluez_gatt_characteristic1_get_notifying(aChar))
166
0
    {
167
0
        g_dbus_method_invocation_return_dbus_error(aInvocation, "org.bluez.Error.NotPermitted", "Already notifying");
168
0
        return FALSE;
169
0
    }
170
171
0
    conn = GetBluezConnectionViaDevice();
172
0
    VerifyOrReturnValue(
173
0
        conn != nullptr, FALSE,
174
0
        g_dbus_method_invocation_return_dbus_error(aInvocation, "org.bluez.Error.Failed", "No CHIPoBLE connection"));
175
176
0
    VerifyOrReturnValue(
177
0
        g_variant_lookup(aOptions, "mtu", "q", &mtu), FALSE, ChipLogError(DeviceLayer, "FAIL: No MTU in options in %s", __func__);
178
0
        g_dbus_method_invocation_return_dbus_error(aInvocation, "org.bluez.Error.InvalidArguments", "MTU negotiation failed"););
179
0
    conn->SetMTU(mtu);
180
181
0
    if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, fds) < 0)
182
0
    {
183
0
#if CHIP_ERROR_LOGGING
184
0
        errStr = strerror(errno);
185
0
#endif // CHIP_ERROR_LOGGING
186
0
        ChipLogError(DeviceLayer, "FAIL: socketpair: %s in %s", StringOrNullMarker(errStr), __func__);
187
0
        g_dbus_method_invocation_return_dbus_error(aInvocation, "org.bluez.Error.Failed", "FD creation failed");
188
0
        return FALSE;
189
0
    }
190
191
0
    conn->SetupNotifyHandler(fds[0], isAdditionalAdvertising);
192
0
    bluez_gatt_characteristic1_set_notify_acquired(aChar, TRUE);
193
0
    conn->SetNotifyAcquired(true);
194
195
0
    GUnixFDList * fdList = g_unix_fd_list_new_from_array(&fds[1], 1);
196
0
    bluez_gatt_characteristic1_complete_acquire_notify(aChar, aInvocation, fdList, g_variant_new_handle(0), conn->GetMTU());
197
0
    g_object_unref(fdList);
198
199
0
    BLEManagerImpl::HandleTXCharCCCDWrite(conn);
200
201
0
    return TRUE;
202
0
}
203
204
static gboolean BluezCharacteristicAcquireNotifyError(BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInvocation,
205
                                                      GVariant * aOptions)
206
0
{
207
0
    ChipLogDetail(DeviceLayer, "Received %s", __func__);
208
0
    g_dbus_method_invocation_return_dbus_error(aInvocation, "org.bluez.Error.NotSupported",
209
0
                                               "AcquireNotify for characteristic is unsupported");
210
0
    return TRUE;
211
0
}
212
213
gboolean BluezEndpoint::BluezCharacteristicConfirm(BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInvocation)
214
0
{
215
0
    BluezConnection * conn = GetBluezConnectionViaDevice();
216
0
    ChipLogDetail(Ble, "Indication confirmation, %p", conn);
217
0
    bluez_gatt_characteristic1_complete_confirm(aChar, aInvocation);
218
0
    BLEManagerImpl::HandleTXComplete(conn);
219
0
    return TRUE;
220
0
}
221
222
static gboolean BluezCharacteristicConfirmError(BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInvocation)
223
0
{
224
0
    g_dbus_method_invocation_return_dbus_error(aInvocation, "org.bluez.Error.Failed", "Confirm from characteristic is unsupported");
225
0
    return TRUE;
226
0
}
227
228
BluezGattCharacteristic1 * BluezEndpoint::CreateGattCharacteristic(BluezGattService1 * aService, const char * aCharName,
229
                                                                   const char * aUUID, const char * const * aFlags)
230
0
{
231
0
    const char * servicePath = g_dbus_object_get_object_path(g_dbus_interface_get_object(G_DBUS_INTERFACE(aService)));
232
0
    GAutoPtr<char> charPath(g_strdup_printf("%s/%s", servicePath, aCharName));
233
0
    BluezObjectSkeleton * object;
234
0
    BluezGattCharacteristic1 * characteristic;
235
236
0
    ChipLogDetail(DeviceLayer, "Create characteristic object at %s", charPath.get());
237
0
    object = bluez_object_skeleton_new(charPath.get());
238
239
0
    characteristic = bluez_gatt_characteristic1_skeleton_new();
240
0
    bluez_gatt_characteristic1_set_uuid(characteristic, aUUID);
241
0
    bluez_gatt_characteristic1_set_flags(characteristic, aFlags);
242
0
    bluez_gatt_characteristic1_set_service(characteristic, servicePath);
243
244
    // Initialize value to empty array, so it can be read without prior write from the client side.
245
0
    auto value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, nullptr, 0, sizeof(uint8_t));
246
0
    bluez_gatt_characteristic1_set_value(characteristic, value);
247
248
0
    bluez_object_skeleton_set_gatt_characteristic1(object, characteristic);
249
0
    g_dbus_object_manager_server_export(mRoot.get(), G_DBUS_OBJECT_SKELETON(object));
250
0
    g_object_unref(object);
251
252
0
    return characteristic;
253
0
}
254
255
void BluezEndpoint::RegisterGattApplicationDone(GObject * aObject, GAsyncResult * aResult)
256
0
{
257
0
    GAutoPtr<GError> error;
258
0
    if (!bluez_gatt_manager1_call_register_application_finish(reinterpret_cast<BluezGattManager1 *>(aObject), aResult,
259
0
                                                              &error.GetReceiver()))
260
0
    {
261
0
        ChipLogError(DeviceLayer, "FAIL: RegisterGattApplication: %s", error->message);
262
0
        BLEManagerImpl::NotifyBLEPeripheralRegisterAppComplete(BluezCallToChipError(error.get()));
263
0
        return;
264
0
    }
265
266
0
    ChipLogDetail(DeviceLayer, "GATT application registered successfully");
267
0
    BLEManagerImpl::NotifyBLEPeripheralRegisterAppComplete(CHIP_NO_ERROR);
268
0
}
269
270
CHIP_ERROR BluezEndpoint::RegisterGattApplicationImpl()
271
0
{
272
0
    VerifyOrReturnError(mAdapter, CHIP_ERROR_UNINITIALIZED);
273
274
    // If the adapter configured in the Init() was unplugged, the g_dbus_interface_get_object()
275
    // or bluez_object_get_gatt_manager1() might return nullptr (depending on the timing, since
276
    // the D-Bus communication is handled on a separate thread). In such case, we should not
277
    // report internal error, but adapter unavailable, so the application can handle the situation
278
    // properly.
279
280
0
    GDBusObject * adapterObject = g_dbus_interface_get_object(reinterpret_cast<GDBusInterface *>(mAdapter.get()));
281
0
    VerifyOrReturnError(adapterObject != nullptr, BLE_ERROR_ADAPTER_UNAVAILABLE);
282
0
    GAutoPtr<BluezGattManager1> gattMgr(bluez_object_get_gatt_manager1(reinterpret_cast<BluezObject *>(adapterObject)));
283
0
    VerifyOrReturnError(gattMgr, BLE_ERROR_ADAPTER_UNAVAILABLE);
284
285
0
    GVariantBuilder optionsBuilder;
286
0
    g_variant_builder_init(&optionsBuilder, G_VARIANT_TYPE("a{sv}"));
287
0
    GVariant * options = g_variant_builder_end(&optionsBuilder);
288
289
0
    bluez_gatt_manager1_call_register_application(
290
0
        gattMgr.get(), mpRootPath, options, nullptr,
291
0
        +[](GObject * aObj, GAsyncResult * aResult, void * self) {
292
0
            reinterpret_cast<BluezEndpoint *>(self)->RegisterGattApplicationDone(aObj, aResult);
293
0
        },
294
0
        this);
295
296
0
    return CHIP_NO_ERROR;
297
0
}
298
299
/// Update the table of open BLE connections whenever a new device is spotted or its attributes have changed.
300
void BluezEndpoint::UpdateConnectionTable(BluezDevice1 & aDevice)
301
0
{
302
0
    const char * objectPath = g_dbus_proxy_get_object_path(reinterpret_cast<GDBusProxy *>(&aDevice));
303
0
    BluezConnection * conn  = GetBluezConnection(objectPath);
304
305
0
    if (conn != nullptr && !bluez_device1_get_connected(&aDevice))
306
0
    {
307
0
        ChipLogDetail(DeviceLayer, "BLE connection closed: conn=%p", conn);
308
0
        BLEManagerImpl::HandleConnectionClosed(conn);
309
0
        mConnMap.erase(objectPath);
310
        // TODO: the connection object should be released after BLEManagerImpl finishes cleaning up its resources
311
        // after the disconnection. Releasing it here doesn't cause any issues, but it's error-prone.
312
0
        chip::Platform::Delete(conn);
313
0
        return;
314
0
    }
315
316
0
    if (conn == nullptr)
317
0
    {
318
0
        HandleNewDevice(aDevice);
319
0
    }
320
0
}
321
322
void BluezEndpoint::HandleNewDevice(BluezDevice1 & aDevice)
323
0
{
324
0
    VerifyOrReturn(bluez_device1_get_connected(&aDevice));
325
0
    VerifyOrReturn(!mIsCentral || bluez_device1_get_services_resolved(&aDevice));
326
0
    CHIP_ERROR err;
327
328
0
    const char * objectPath = g_dbus_proxy_get_object_path(reinterpret_cast<GDBusProxy *>(&aDevice));
329
0
    BluezConnection * conn  = GetBluezConnection(objectPath);
330
0
    VerifyOrReturn(conn == nullptr,
331
0
                   ChipLogError(DeviceLayer, "FAIL: Connection already tracked: conn=%p device=%s path=%s", conn,
332
0
                                conn->GetPeerAddress(), objectPath));
333
334
0
    conn = chip::Platform::New<BluezConnection>(aDevice);
335
0
    VerifyOrExit(conn != nullptr, err = CHIP_ERROR_NO_MEMORY);
336
0
    SuccessOrExit(err = conn->Init(*this));
337
338
0
    mpPeerDevicePath           = g_strdup(objectPath);
339
0
    mConnMap[mpPeerDevicePath] = conn;
340
341
0
    ChipLogDetail(DeviceLayer, "New BLE connection: conn=%p device=%s path=%s", conn, conn->GetPeerAddress(), objectPath);
342
343
0
    BLEManagerImpl::HandleNewConnection(conn);
344
0
    return;
345
346
0
exit:
347
0
    chip::Platform::Delete(conn);
348
0
    BLEManagerImpl::HandleConnectFailed(err);
349
0
}
350
351
void BluezEndpoint::OnDeviceAdded(BluezDevice1 & device)
352
0
{
353
0
    HandleNewDevice(device);
354
0
}
355
356
void BluezEndpoint::OnDevicePropertyChanged(BluezDevice1 & device, GVariant * changedProps, const char * const * invalidatedProps)
357
0
{
358
0
    UpdateConnectionTable(device);
359
0
}
360
361
void BluezEndpoint::OnDeviceRemoved(BluezDevice1 & device)
362
0
{
363
    // Handling device removal is not necessary because disconnection is already handled
364
    // in the OnDevicePropertyChanged() - we are checking for the "Connected" property.
365
0
}
366
367
BluezGattService1 * BluezEndpoint::CreateGattService(const char * aUUID)
368
0
{
369
0
    BluezObjectSkeleton * object;
370
0
    BluezGattService1 * service;
371
372
0
    mpServicePath = g_strdup_printf("%s/service", mpRootPath);
373
0
    ChipLogDetail(DeviceLayer, "CREATE service object at %s", mpServicePath);
374
0
    object = bluez_object_skeleton_new(mpServicePath);
375
376
0
    service = bluez_gatt_service1_skeleton_new();
377
0
    bluez_gatt_service1_set_uuid(service, aUUID);
378
    // device is only valid for remote services
379
0
    bluez_gatt_service1_set_primary(service, TRUE);
380
381
    // includes -- unclear whether required.  Might be filled in later
382
0
    bluez_object_skeleton_set_gatt_service1(object, service);
383
0
    g_dbus_object_manager_server_export(mRoot.get(), G_DBUS_OBJECT_SKELETON(object));
384
0
    g_object_unref(object);
385
386
0
    return service;
387
0
}
388
389
BluezConnection * BluezEndpoint::GetBluezConnection(const char * aPath)
390
0
{
391
0
    auto it = mConnMap.find(aPath);
392
0
    return (it != mConnMap.end()) ? it->second : nullptr;
393
0
}
394
395
BluezConnection * BluezEndpoint::GetBluezConnectionViaDevice()
396
0
{
397
0
    return GetBluezConnection(mpPeerDevicePath);
398
0
}
399
400
#if CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING
401
static void UpdateAdditionalDataCharacteristic(BluezGattCharacteristic1 * characteristic)
402
{
403
    VerifyOrReturn(characteristic != nullptr);
404
405
    // Construct the TLV for the additional data
406
    GVariant * cValue = nullptr;
407
    gpointer data;
408
    CHIP_ERROR err = CHIP_NO_ERROR;
409
    chip::System::PacketBufferHandle bufferHandle;
410
    BitFlags<AdditionalDataFields> additionalDataFields;
411
    AdditionalDataPayloadGeneratorParams additionalDataPayloadParams;
412
413
#if CHIP_ENABLE_ROTATING_DEVICE_ID && defined(CHIP_DEVICE_CONFIG_ROTATING_DEVICE_ID_UNIQUE_ID)
414
    uint8_t rotatingDeviceIdUniqueId[ConfigurationManager::kRotatingDeviceIDUniqueIDLength] = {};
415
    MutableByteSpan rotatingDeviceIdUniqueIdSpan(rotatingDeviceIdUniqueId);
416
417
    err = GetDeviceInstanceInfoProvider()->GetRotatingDeviceIdUniqueId(rotatingDeviceIdUniqueIdSpan);
418
    SuccessOrExit(err);
419
    err = ConfigurationMgr().GetLifetimeCounter(additionalDataPayloadParams.rotatingDeviceIdLifetimeCounter);
420
    SuccessOrExit(err);
421
    additionalDataPayloadParams.rotatingDeviceIdUniqueId = rotatingDeviceIdUniqueIdSpan;
422
    additionalDataFields.Set(AdditionalDataFields::RotatingDeviceId);
423
#endif /* CHIP_ENABLE_ROTATING_DEVICE_ID && defined(CHIP_DEVICE_CONFIG_ROTATING_DEVICE_ID_UNIQUE_ID) */
424
425
    err = AdditionalDataPayloadGenerator().generateAdditionalDataPayload(additionalDataPayloadParams, bufferHandle,
426
                                                                         additionalDataFields);
427
    SuccessOrExit(err);
428
429
    data = g_memdup2(bufferHandle->Start(), bufferHandle->DataLength());
430
431
    cValue = g_variant_new_from_data(G_VARIANT_TYPE("ay"), data, bufferHandle->DataLength(), TRUE, g_free, data);
432
    bluez_gatt_characteristic1_set_value(characteristic, cValue);
433
434
    return;
435
436
exit:
437
    if (err != CHIP_NO_ERROR)
438
    {
439
        ChipLogError(DeviceLayer, "Failed to generate TLV encoded Additional Data (%s)", __func__);
440
    }
441
    return;
442
}
443
#endif
444
445
void BluezEndpoint::SetupGattService()
446
0
{
447
448
0
    static const char * const c1_flags[] = { "write", nullptr };
449
0
    static const char * const c2_flags[] = { "indicate", nullptr };
450
#if CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING
451
    static const char * const c3_flags[] = { "read", nullptr };
452
#endif
453
454
0
    mService.reset(CreateGattService(Ble::CHIP_BLE_SERVICE_SHORT_UUID_STR));
455
456
    // C1 characteristic
457
0
    mC1.reset(CreateGattCharacteristic(mService.get(), "c1", Ble::CHIP_BLE_CHAR_1_UUID_STR, c1_flags));
458
0
    g_signal_connect(mC1.get(), "handle-read-value",
459
0
                     G_CALLBACK(+[](BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInv, GVariant * aOpt,
460
0
                                    BluezEndpoint * self) { return self->BluezCharacteristicReadValue(aChar, aInv, aOpt); }),
461
0
                     this);
462
0
    g_signal_connect(
463
0
        mC1.get(), "handle-acquire-write",
464
0
        G_CALLBACK(+[](BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInv, GUnixFDList * aFDList, GVariant * aOpt,
465
0
                       BluezEndpoint * self) { return self->BluezCharacteristicAcquireWrite(aChar, aInv, aFDList, aOpt); }),
466
0
        this);
467
0
    g_signal_connect(mC1.get(), "handle-acquire-notify", G_CALLBACK(BluezCharacteristicAcquireNotifyError), nullptr);
468
0
    g_signal_connect(mC1.get(), "handle-confirm", G_CALLBACK(BluezCharacteristicConfirmError), nullptr);
469
470
    // C2 characteristic
471
0
    mC2.reset(CreateGattCharacteristic(mService.get(), "c2", Ble::CHIP_BLE_CHAR_2_UUID_STR, c2_flags));
472
0
    g_signal_connect(mC2.get(), "handle-read-value",
473
0
                     G_CALLBACK(+[](BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInv, GVariant * aOpt,
474
0
                                    BluezEndpoint * self) { return self->BluezCharacteristicReadValue(aChar, aInv, aOpt); }),
475
0
                     this);
476
0
    g_signal_connect(mC2.get(), "handle-acquire-write", G_CALLBACK(BluezCharacteristicAcquireWriteError), nullptr);
477
0
    g_signal_connect(
478
0
        mC2.get(), "handle-acquire-notify",
479
0
        G_CALLBACK(+[](BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInv, GUnixFDList * aFDList, GVariant * aOpt,
480
0
                       BluezEndpoint * self) { return self->BluezCharacteristicAcquireNotify(aChar, aInv, aFDList, aOpt); }),
481
0
        this);
482
0
    g_signal_connect(mC2.get(), "handle-confirm",
483
0
                     G_CALLBACK(+[](BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInv, BluezEndpoint * self) {
484
0
                         return self->BluezCharacteristicConfirm(aChar, aInv);
485
0
                     }),
486
0
                     this);
487
488
0
    ChipLogDetail(DeviceLayer, "CHIP BTP C1 %s", bluez_gatt_characteristic1_get_service(mC1.get()));
489
0
    ChipLogDetail(DeviceLayer, "CHIP BTP C2 %s", bluez_gatt_characteristic1_get_service(mC2.get()));
490
491
#if CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING
492
    ChipLogDetail(DeviceLayer, "CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING is TRUE");
493
    // Additional data characteristics
494
    mC3.reset(CreateGattCharacteristic(mService.get(), "c3", Ble::CHIP_BLE_CHAR_3_UUID_STR, c3_flags));
495
    g_signal_connect(mC3.get(), "handle-read-value",
496
                     G_CALLBACK(+[](BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInv, GVariant * aOpt,
497
                                    BluezEndpoint * self) { return self->BluezCharacteristicReadValue(aChar, aInv, aOpt); }),
498
                     this);
499
    g_signal_connect(mC3.get(), "handle-acquire-write", G_CALLBACK(BluezCharacteristicAcquireWriteError), nullptr);
500
    g_signal_connect(
501
        mC3.get(), "handle-acquire-notify",
502
        G_CALLBACK(+[](BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInv, GUnixFDList * aFDList, GVariant * aOpt,
503
                       BluezEndpoint * self) { return self->BluezCharacteristicAcquireNotify(aChar, aInv, aFDList, aOpt); }),
504
        this);
505
    g_signal_connect(mC3.get(), "handle-confirm",
506
                     G_CALLBACK(+[](BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInv, BluezEndpoint * self) {
507
                         return self->BluezCharacteristicConfirm(aChar, aInv);
508
                     }),
509
                     this);
510
511
    // update the characteristic value
512
    UpdateAdditionalDataCharacteristic(mC3.get());
513
    ChipLogDetail(DeviceLayer, "CHIP BTP C3 %s", bluez_gatt_characteristic1_get_service(mC3.get()));
514
#else
515
0
    ChipLogDetail(DeviceLayer, "CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING is FALSE");
516
0
#endif
517
0
}
518
519
void BluezEndpoint::SetupGattServer(GDBusConnection * aConn)
520
0
{
521
0
    VerifyOrReturn(!mIsCentral);
522
523
0
    mpRootPath = g_strdup_printf("/chipoble/%04x", getpid() & 0xffff);
524
0
    mRoot.reset(g_dbus_object_manager_server_new(mpRootPath));
525
526
0
    SetupGattService();
527
528
    // Set connection after the service is set up in order to reduce the number
529
    // of signals sent on the bus.
530
0
    g_dbus_object_manager_server_set_connection(mRoot.get(), aConn);
531
0
}
532
533
CHIP_ERROR BluezEndpoint::SetupEndpointBindings()
534
0
{
535
0
    SetupGattServer(mObjectManager.GetConnection());
536
0
    return CHIP_NO_ERROR;
537
0
}
538
539
CHIP_ERROR BluezEndpoint::RegisterGattApplication()
540
0
{
541
0
    return PlatformMgrImpl().GLibMatterContextInvokeSync(
542
0
        +[](BluezEndpoint * self) { return self->RegisterGattApplicationImpl(); }, this);
543
0
}
544
545
CHIP_ERROR BluezEndpoint::Init(BluezAdapter1 * apAdapter, bool aIsCentral)
546
0
{
547
0
    VerifyOrReturnError(!mIsInitialized, CHIP_ERROR_INCORRECT_STATE);
548
0
    VerifyOrReturnError(apAdapter != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
549
550
0
    mAdapter.reset(reinterpret_cast<BluezAdapter1 *>(g_object_ref(apAdapter)));
551
0
    mIsCentral = aIsCentral;
552
553
0
    CHIP_ERROR err = mObjectManager.SubscribeDeviceNotifications(mAdapter.get(), this);
554
0
    VerifyOrReturnError(err == CHIP_NO_ERROR, err,
555
0
                        ChipLogError(DeviceLayer, "Failed to subscribe for notifications: %" CHIP_ERROR_FORMAT, err.Format()));
556
557
0
    err = PlatformMgrImpl().GLibMatterContextInvokeSync(
558
0
        +[](BluezEndpoint * self) { return self->SetupEndpointBindings(); }, this);
559
0
    VerifyOrReturnError(err == CHIP_NO_ERROR, err,
560
0
                        ChipLogError(DeviceLayer, "Failed to schedule endpoint initialization: %" CHIP_ERROR_FORMAT, err.Format()));
561
562
0
    mIsInitialized = true;
563
564
0
    return CHIP_NO_ERROR;
565
0
}
566
567
void BluezEndpoint::Shutdown()
568
1
{
569
1
    VerifyOrReturn(mIsInitialized);
570
571
    // Run endpoint cleanup on the CHIPoBluez thread. This is necessary because the
572
    // cleanup function releases the D-Bus manager client object, which handles D-Bus
573
    // signals. Otherwise, we will face race condition when the D-Bus signal is in
574
    // the middle of being processed when the cleanup function is called.
575
0
    PlatformMgrImpl().GLibMatterContextInvokeSync(
576
0
        +[](BluezEndpoint * self) {
577
0
            self->mAdapter.reset();
578
0
            self->mRoot.reset();
579
0
            self->mService.reset();
580
0
            self->mC1.reset();
581
0
            self->mC2.reset();
582
#if CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING
583
            self->mC3.reset();
584
#endif
585
0
            return CHIP_NO_ERROR;
586
0
        },
587
0
        this);
588
589
0
    g_free(mpRootPath);
590
0
    g_free(mpServicePath);
591
0
    g_free(mpPeerDevicePath);
592
593
0
    mIsInitialized = false;
594
0
}
595
596
// ConnectDevice callbacks
597
598
CHIP_ERROR BluezEndpoint::ConnectDeviceImpl(BluezDevice1 & aDevice)
599
0
{
600
    // Due to radio interferences or Wi-Fi coexistence, sometimes the BLE connection may not be
601
    // established (e.g. Connection Indication Packet is missed by BLE peripheral). In such case,
602
    // BlueZ returns "Software caused connection abort error", and we should make a connection retry.
603
    // It's important to make sure that the connection is correctly ceased, by calling `Disconnect()`
604
    // D-Bus method, or else `Connect()` returns immediately without any effect.
605
0
    for (uint16_t i = 0; i < kMaxConnectRetries; i++)
606
0
    {
607
0
        GAutoPtr<GError> error;
608
0
        if (bluez_device1_call_connect_sync(&aDevice, mConnectCancellable.get(), &error.GetReceiver()))
609
0
        {
610
0
            ChipLogDetail(DeviceLayer, "ConnectDevice complete");
611
0
            return CHIP_NO_ERROR;
612
0
        }
613
614
0
        ChipLogError(DeviceLayer, "FAIL: ConnectDevice: %s (%d)", error->message, error->code);
615
0
        if (!g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_DBUS_ERROR))
616
0
        {
617
0
            break;
618
0
        }
619
620
0
        ChipLogProgress(DeviceLayer, "ConnectDevice retry: %u out of %u", i + 1, kMaxConnectRetries);
621
0
        bluez_device1_call_disconnect_sync(&aDevice, nullptr, nullptr);
622
0
    }
623
624
0
    BLEManagerImpl::HandleConnectFailed(CHIP_ERROR_INTERNAL);
625
0
    return CHIP_ERROR_INTERNAL;
626
0
}
627
628
CHIP_ERROR BluezEndpoint::ConnectDevice(BluezDevice1 & aDevice)
629
0
{
630
0
    auto params = std::make_pair(this, &aDevice);
631
0
    mConnectCancellable.reset(g_cancellable_new());
632
0
    return PlatformMgrImpl().GLibMatterContextInvokeSync(
633
0
        +[](decltype(params) * aParams) { return aParams->first->ConnectDeviceImpl(*aParams->second); }, &params);
634
0
}
635
636
void BluezEndpoint::CancelConnect()
637
0
{
638
0
    g_cancellable_cancel(mConnectCancellable.get());
639
0
    mConnectCancellable.reset();
640
0
}
641
642
} // namespace Internal
643
} // namespace DeviceLayer
644
} // namespace chip