3 * Copyright (c) 2021 Project CHIP Authors
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include "ChipDeviceScanner.h"
20 #if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
22 #include "BluezObjectList.h"
28 #include <support/logging/CHIPLogging.h>
31 namespace DeviceLayer {
38 void operator()(T * value)
40 g_object_unref(value);
44 using GCancellableUniquePtr = std::unique_ptr<GCancellable, GObjectUnref>;
45 using GDBusObjectManagerUniquePtr = std::unique_ptr<GDBusObjectManager, GObjectUnref>;
47 /// Retrieve CHIP device identification info from the device advertising data
48 bool BluezGetChipDeviceInfo(BluezDevice1 & aDevice, chip::Ble::ChipBLEDeviceIdentificationInfo & aDeviceInfo)
50 GVariant * serviceData = bluez_device1_get_service_data(&aDevice);
51 VerifyOrReturnError(serviceData != nullptr, false);
53 GVariant * dataValue = g_variant_lookup_value(serviceData, CHIP_BLE_UUID_SERVICE_STRING, nullptr);
54 VerifyOrReturnError(dataValue != nullptr, false);
57 const void * dataBytes = g_variant_get_fixed_array(dataValue, &dataLen, sizeof(uint8_t));
58 VerifyOrReturnError(dataBytes != nullptr && dataLen >= sizeof(aDeviceInfo), false);
60 memcpy(&aDeviceInfo, dataBytes, sizeof(aDeviceInfo));
66 ChipDeviceScanner::ChipDeviceScanner(GDBusObjectManager * manager, BluezAdapter1 * adapter, GCancellable * cancellable,
67 ChipDeviceScannerDelegate * delegate) :
69 mAdapter(adapter), mCancellable(cancellable), mDelegate(delegate)
71 g_object_ref(mAdapter);
72 g_object_ref(mCancellable);
73 g_object_ref(mManager);
76 ChipDeviceScanner::~ChipDeviceScanner()
80 // In case the timeout timer is still active
81 chip::DeviceLayer::SystemLayer.CancelTimer(TimerExpiredCallback, this);
83 g_object_unref(mManager);
84 g_object_unref(mCancellable);
85 g_object_unref(mAdapter);
89 mCancellable = nullptr;
93 std::unique_ptr<ChipDeviceScanner> ChipDeviceScanner::Create(BluezAdapter1 * adapter, ChipDeviceScannerDelegate * delegate)
95 GError * error = nullptr;
97 GCancellableUniquePtr cancellable(g_cancellable_new(), GObjectUnref());
101 return std::unique_ptr<ChipDeviceScanner>();
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),
112 ChipLogError(Ble, "Failed to get DBUS object manager for device scanning: %s", error->message);
114 return std::unique_ptr<ChipDeviceScanner>();
117 return std::make_unique<ChipDeviceScanner>(manager.get(), adapter, cancellable.get(), delegate);
120 CHIP_ERROR ChipDeviceScanner::StartScan(unsigned timeoutMs)
122 ReturnErrorCodeIf(mIsScanning, CHIP_ERROR_INCORRECT_STATE);
124 ReturnErrorOnFailure(MainLoop::Instance().EnsureStarted());
126 mIsScanning = true; // optimistic, to allow all callbacks to check this
127 if (!MainLoop::Instance().Schedule(MainLoopStartScan, this))
129 ChipLogError(Ble, "Failed to schedule BLE scan start.");
131 return CHIP_ERROR_INTERNAL;
134 CHIP_ERROR err = chip::DeviceLayer::SystemLayer.StartTimer(timeoutMs, TimerExpiredCallback, static_cast<void *>(this));
136 if (err != CHIP_NO_ERROR)
138 ChipLogError(Ble, "Failed to schedule scan timeout.");
143 return CHIP_NO_ERROR;
146 void ChipDeviceScanner::TimerExpiredCallback(chip::System::Layer * layer, void * appState, chip::System::Error error)
148 static_cast<ChipDeviceScanner *>(appState)->StopScan();
151 CHIP_ERROR ChipDeviceScanner::StopScan()
153 ReturnErrorCodeIf(!mIsScanning, CHIP_NO_ERROR);
154 ReturnErrorCodeIf(mIsStopping, CHIP_NO_ERROR);
156 g_cancellable_cancel(mCancellable); // in case we are currently running a scan
158 if (mObjectAddedSignal)
160 g_signal_handler_disconnect(mManager, mObjectAddedSignal);
161 mObjectAddedSignal = 0;
164 if (mInterfaceChangedSignal)
166 g_signal_handler_disconnect(mManager, mInterfaceChangedSignal);
167 mInterfaceChangedSignal = 0;
170 if (!MainLoop::Instance().ScheduleAndWait(MainLoopStopScan, this))
172 ChipLogError(Ble, "Failed to schedule BLE scan stop.");
173 return CHIP_ERROR_INTERNAL;
176 return CHIP_NO_ERROR;
179 int ChipDeviceScanner::MainLoopStopScan(ChipDeviceScanner * self)
181 GError * error = nullptr;
183 if (!bluez_adapter1_call_stop_discovery_sync(self->mAdapter, nullptr /* not cancellable */, &error))
185 ChipLogError(Ble, "Failed to stop discovery %s", error->message);
188 ChipDeviceScannerDelegate * delegate = self->mDelegate;
189 self->mIsScanning = false;
191 // callback is explicitly allowed to delete the scanner (hence no more
192 // references to 'self' here)
193 delegate->OnScanComplete();
198 void ChipDeviceScanner::SignalObjectAdded(GDBusObjectManager * manager, GDBusObject * object, ChipDeviceScanner * self)
200 self->ReportDevice(bluez_object_get_device1(BLUEZ_OBJECT(object)));
203 void ChipDeviceScanner::SignalInterfaceChanged(GDBusObjectManagerClient * manager, GDBusObjectProxy * object,
204 GDBusProxy * aInterface, GVariant * aChangedProperties,
205 const gchar * const * aInvalidatedProps, ChipDeviceScanner * self)
207 self->ReportDevice(bluez_object_get_device1(BLUEZ_OBJECT(object)));
210 void ChipDeviceScanner::ReportDevice(BluezDevice1 * device)
212 if (device == nullptr)
217 if (strcmp(bluez_device1_get_adapter(device), g_dbus_proxy_get_object_path(G_DBUS_PROXY(mAdapter))) != 0)
222 chip::Ble::ChipBLEDeviceIdentificationInfo deviceInfo;
224 if (!BluezGetChipDeviceInfo(*device, deviceInfo))
226 ChipLogDetail(Ble, "Device %s does not look like a CHIP device.", bluez_device1_get_address(device));
230 mDelegate->OnDeviceScanned(device, deviceInfo);
233 int ChipDeviceScanner::MainLoopStartScan(ChipDeviceScanner * self)
235 GError * error = nullptr;
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);
241 ChipLogProgress(Ble, "BLE scanning through known devices.");
242 for (BluezObject & object : BluezObjectList(self->mManager))
244 self->ReportDevice(bluez_object_get_device1(&object));
247 ChipLogProgress(Ble, "BLE initiating scan.");
248 if (!bluez_adapter1_call_start_discovery_sync(self->mAdapter, self->mCancellable, &error))
250 ChipLogError(Ble, "Failed to start discovery: %s", error->message);
253 self->mIsScanning = false;
254 self->mDelegate->OnScanComplete();
260 } // namespace Internal
261 } // namespace DeviceLayer
264 #endif // CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE