Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / platform / Linux / bluez / ChipDeviceScanner.cpp
1 /*
2  *
3  *    Copyright (c) 2021 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 #include "ChipDeviceScanner.h"
19
20 #if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
21
22 #include "BluezObjectList.h"
23 #include "MainLoop.h"
24 #include "Types.h"
25
26 #include <errno.h>
27 #include <pthread.h>
28 #include <support/logging/CHIPLogging.h>
29
30 namespace chip {
31 namespace DeviceLayer {
32 namespace Internal {
33 namespace {
34
35 struct GObjectUnref
36 {
37     template <typename T>
38     void operator()(T * value)
39     {
40         g_object_unref(value);
41     }
42 };
43
44 using GCancellableUniquePtr       = std::unique_ptr<GCancellable, GObjectUnref>;
45 using GDBusObjectManagerUniquePtr = std::unique_ptr<GDBusObjectManager, GObjectUnref>;
46
47 /// Retrieve CHIP device identification info from the device advertising data
48 bool BluezGetChipDeviceInfo(BluezDevice1 & aDevice, chip::Ble::ChipBLEDeviceIdentificationInfo & aDeviceInfo)
49 {
50     GVariant * serviceData = bluez_device1_get_service_data(&aDevice);
51     VerifyOrReturnError(serviceData != nullptr, false);
52
53     GVariant * dataValue = g_variant_lookup_value(serviceData, CHIP_BLE_UUID_SERVICE_STRING, nullptr);
54     VerifyOrReturnError(dataValue != nullptr, false);
55
56     size_t dataLen         = 0;
57     const void * dataBytes = g_variant_get_fixed_array(dataValue, &dataLen, sizeof(uint8_t));
58     VerifyOrReturnError(dataBytes != nullptr && dataLen >= sizeof(aDeviceInfo), false);
59
60     memcpy(&aDeviceInfo, dataBytes, sizeof(aDeviceInfo));
61     return true;
62 }
63
64 } // namespace
65
66 ChipDeviceScanner::ChipDeviceScanner(GDBusObjectManager * manager, BluezAdapter1 * adapter, GCancellable * cancellable,
67                                      ChipDeviceScannerDelegate * delegate) :
68     mManager(manager),
69     mAdapter(adapter), mCancellable(cancellable), mDelegate(delegate)
70 {
71     g_object_ref(mAdapter);
72     g_object_ref(mCancellable);
73     g_object_ref(mManager);
74 }
75
76 ChipDeviceScanner::~ChipDeviceScanner()
77 {
78     StopScan();
79
80     // In case the timeout timer is still active
81     chip::DeviceLayer::SystemLayer.CancelTimer(TimerExpiredCallback, this);
82
83     g_object_unref(mManager);
84     g_object_unref(mCancellable);
85     g_object_unref(mAdapter);
86
87     mManager     = nullptr;
88     mAdapter     = nullptr;
89     mCancellable = nullptr;
90     mDelegate    = nullptr;
91 }
92
93 std::unique_ptr<ChipDeviceScanner> ChipDeviceScanner::Create(BluezAdapter1 * adapter, ChipDeviceScannerDelegate * delegate)
94 {
95     GError * error = nullptr;
96
97     GCancellableUniquePtr cancellable(g_cancellable_new(), GObjectUnref());
98
99     if (!cancellable)
100     {
101         return std::unique_ptr<ChipDeviceScanner>();
102     }
103
104     GDBusObjectManagerUniquePtr manager(
105         g_dbus_object_manager_client_new_for_bus_sync(G_BUS_TYPE_SYSTEM, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, BLUEZ_INTERFACE,
106                                                       "/", bluez_object_manager_client_get_proxy_type,
107                                                       nullptr /* unused user data in the Proxy Type Func */,
108                                                       nullptr /*destroy notify */, cancellable.get(), &error),
109         GObjectUnref());
110     if (!manager)
111     {
112         ChipLogError(Ble, "Failed to get DBUS object manager for device scanning: %s", error->message);
113         g_error_free(error);
114         return std::unique_ptr<ChipDeviceScanner>();
115     }
116
117     return std::make_unique<ChipDeviceScanner>(manager.get(), adapter, cancellable.get(), delegate);
118 }
119
120 CHIP_ERROR ChipDeviceScanner::StartScan(unsigned timeoutMs)
121 {
122     ReturnErrorCodeIf(mIsScanning, CHIP_ERROR_INCORRECT_STATE);
123
124     ReturnErrorOnFailure(MainLoop::Instance().EnsureStarted());
125
126     mIsScanning = true; // optimistic, to allow all callbacks to check this
127     if (!MainLoop::Instance().Schedule(MainLoopStartScan, this))
128     {
129         ChipLogError(Ble, "Failed to schedule BLE scan start.");
130         mIsScanning = false;
131         return CHIP_ERROR_INTERNAL;
132     }
133
134     CHIP_ERROR err = chip::DeviceLayer::SystemLayer.StartTimer(timeoutMs, TimerExpiredCallback, static_cast<void *>(this));
135
136     if (err != CHIP_NO_ERROR)
137     {
138         ChipLogError(Ble, "Failed to schedule scan timeout.");
139         StopScan();
140         return err;
141     }
142
143     return CHIP_NO_ERROR;
144 }
145
146 void ChipDeviceScanner::TimerExpiredCallback(chip::System::Layer * layer, void * appState, chip::System::Error error)
147 {
148     static_cast<ChipDeviceScanner *>(appState)->StopScan();
149 }
150
151 CHIP_ERROR ChipDeviceScanner::StopScan()
152 {
153     ReturnErrorCodeIf(!mIsScanning, CHIP_NO_ERROR);
154     ReturnErrorCodeIf(mIsStopping, CHIP_NO_ERROR);
155     mIsStopping = true;
156     g_cancellable_cancel(mCancellable); // in case we are currently running a scan
157
158     if (mObjectAddedSignal)
159     {
160         g_signal_handler_disconnect(mManager, mObjectAddedSignal);
161         mObjectAddedSignal = 0;
162     }
163
164     if (mInterfaceChangedSignal)
165     {
166         g_signal_handler_disconnect(mManager, mInterfaceChangedSignal);
167         mInterfaceChangedSignal = 0;
168     }
169
170     if (!MainLoop::Instance().ScheduleAndWait(MainLoopStopScan, this))
171     {
172         ChipLogError(Ble, "Failed to schedule BLE scan stop.");
173         return CHIP_ERROR_INTERNAL;
174     }
175
176     return CHIP_NO_ERROR;
177 }
178
179 int ChipDeviceScanner::MainLoopStopScan(ChipDeviceScanner * self)
180 {
181     GError * error = nullptr;
182
183     if (!bluez_adapter1_call_stop_discovery_sync(self->mAdapter, nullptr /* not cancellable */, &error))
184     {
185         ChipLogError(Ble, "Failed to stop discovery %s", error->message);
186         g_error_free(error);
187     }
188     ChipDeviceScannerDelegate * delegate = self->mDelegate;
189     self->mIsScanning                    = false;
190
191     // callback is explicitly allowed to delete the scanner (hence no more
192     // references to 'self' here)
193     delegate->OnScanComplete();
194
195     return 0;
196 }
197
198 void ChipDeviceScanner::SignalObjectAdded(GDBusObjectManager * manager, GDBusObject * object, ChipDeviceScanner * self)
199 {
200     self->ReportDevice(bluez_object_get_device1(BLUEZ_OBJECT(object)));
201 }
202
203 void ChipDeviceScanner::SignalInterfaceChanged(GDBusObjectManagerClient * manager, GDBusObjectProxy * object,
204                                                GDBusProxy * aInterface, GVariant * aChangedProperties,
205                                                const gchar * const * aInvalidatedProps, ChipDeviceScanner * self)
206 {
207     self->ReportDevice(bluez_object_get_device1(BLUEZ_OBJECT(object)));
208 }
209
210 void ChipDeviceScanner::ReportDevice(BluezDevice1 * device)
211 {
212     if (device == nullptr)
213     {
214         return;
215     }
216
217     if (strcmp(bluez_device1_get_adapter(device), g_dbus_proxy_get_object_path(G_DBUS_PROXY(mAdapter))) != 0)
218     {
219         return;
220     }
221
222     chip::Ble::ChipBLEDeviceIdentificationInfo deviceInfo;
223
224     if (!BluezGetChipDeviceInfo(*device, deviceInfo))
225     {
226         ChipLogDetail(Ble, "Device %s does not look like a CHIP device.", bluez_device1_get_address(device));
227         return;
228     }
229
230     mDelegate->OnDeviceScanned(device, deviceInfo);
231 }
232
233 int ChipDeviceScanner::MainLoopStartScan(ChipDeviceScanner * self)
234 {
235     GError * error = nullptr;
236
237     self->mObjectAddedSignal = g_signal_connect(self->mManager, "object-added", G_CALLBACK(SignalObjectAdded), self);
238     self->mInterfaceChangedSignal =
239         g_signal_connect(self->mManager, "interface-proxy-properties-changed", G_CALLBACK(SignalInterfaceChanged), self);
240
241     ChipLogProgress(Ble, "BLE scanning through known devices.");
242     for (BluezObject & object : BluezObjectList(self->mManager))
243     {
244         self->ReportDevice(bluez_object_get_device1(&object));
245     }
246
247     ChipLogProgress(Ble, "BLE initiating scan.");
248     if (!bluez_adapter1_call_start_discovery_sync(self->mAdapter, self->mCancellable, &error))
249     {
250         ChipLogError(Ble, "Failed to start discovery: %s", error->message);
251         g_error_free(error);
252
253         self->mIsScanning = false;
254         self->mDelegate->OnScanComplete();
255     }
256
257     return 0;
258 }
259
260 } // namespace Internal
261 } // namespace DeviceLayer
262 } // namespace chip
263
264 #endif // CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE