1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "device/bluetooth/bluetooth_advertisement_winrt.h"
6
7 #include <windows.foundation.collections.h>
8 #include <windows.storage.streams.h>
9
10 #include <utility>
11 #include <vector>
12
13 #include "base/bind.h"
14 #include "base/callback_helpers.h"
15 #include "base/logging.h"
16 #include "base/strings/string_piece.h"
17 #include "base/threading/thread_task_runner_handle.h"
18 #include "base/win/core_winrt_util.h"
19 #include "base/win/scoped_hstring.h"
20 #include "base/win/winrt_storage_util.h"
21 #include "components/device_event_log/device_event_log.h"
22 #include "device/bluetooth/event_utils_winrt.h"
23
24 namespace device {
25
26 namespace {
27
28 using ABI::Windows::Devices::Bluetooth::BluetoothError;
29 using ABI::Windows::Devices::Bluetooth::BluetoothError_NotSupported;
30 using ABI::Windows::Devices::Bluetooth::BluetoothError_RadioNotAvailable;
31 using ABI::Windows::Devices::Bluetooth::Advertisement::
32 BluetoothLEAdvertisementPublisherStatus;
33 using ABI::Windows::Devices::Bluetooth::Advertisement::
34 BluetoothLEAdvertisementPublisherStatus_Aborted;
35 using ABI::Windows::Devices::Bluetooth::Advertisement::
36 BluetoothLEAdvertisementPublisherStatus_Started;
37 using ABI::Windows::Devices::Bluetooth::Advertisement::
38 BluetoothLEAdvertisementPublisherStatus_Stopped;
39 using ABI::Windows::Devices::Bluetooth::Advertisement::
40 BluetoothLEManufacturerData;
41 using ABI::Windows::Devices::Bluetooth::Advertisement::
42 IBluetoothLEAdvertisement;
43 using ABI::Windows::Devices::Bluetooth::Advertisement::
44 IBluetoothLEAdvertisementPublisher;
45 using ABI::Windows::Devices::Bluetooth::Advertisement::
46 IBluetoothLEAdvertisementPublisherFactory;
47 using ABI::Windows::Devices::Bluetooth::Advertisement::
48 IBluetoothLEAdvertisementPublisherStatusChangedEventArgs;
49 using ABI::Windows::Devices::Bluetooth::Advertisement::
50 IBluetoothLEManufacturerData;
51 using ABI::Windows::Devices::Bluetooth::Advertisement::
52 IBluetoothLEManufacturerDataFactory;
53 using ABI::Windows::Foundation::Collections::IVector;
54 using ABI::Windows::Storage::Streams::IBuffer;
55 using Microsoft::WRL::ComPtr;
56
RemoveStatusChangedHandler(IBluetoothLEAdvertisementPublisher * publisher,EventRegistrationToken token)57 void RemoveStatusChangedHandler(IBluetoothLEAdvertisementPublisher* publisher,
58 EventRegistrationToken token) {
59 HRESULT hr = publisher->remove_StatusChanged(token);
60 if (FAILED(hr)) {
61 BLUETOOTH_LOG(ERROR) << "Removing StatusChanged Handler failed: "
62 << logging::SystemErrorCodeToString(hr);
63 }
64 }
65
66 } // namespace
67
BluetoothAdvertisementWinrt()68 BluetoothAdvertisementWinrt::BluetoothAdvertisementWinrt() {}
69
Initialize(std::unique_ptr<BluetoothAdvertisement::Data> advertisement_data)70 bool BluetoothAdvertisementWinrt::Initialize(
71 std::unique_ptr<BluetoothAdvertisement::Data> advertisement_data) {
72 if (advertisement_data->service_uuids()) {
73 BLUETOOTH_LOG(ERROR)
74 << "Windows does not support advertising Service UUIDs.";
75 return false;
76 }
77
78 if (advertisement_data->solicit_uuids()) {
79 BLUETOOTH_LOG(ERROR)
80 << "Windows does not support advertising Solicit UUIDs.";
81 return false;
82 }
83
84 if (advertisement_data->service_data()) {
85 BLUETOOTH_LOG(ERROR)
86 << "Windows does not support advertising Service Data.";
87 return false;
88 }
89
90 auto manufacturer_data = advertisement_data->manufacturer_data();
91 if (!manufacturer_data) {
92 BLUETOOTH_LOG(ERROR) << "No Manufacturer Data present.";
93 return false;
94 }
95
96 ComPtr<IBluetoothLEAdvertisement> advertisement;
97 HRESULT hr = ActivateBluetoothLEAdvertisementInstance(&advertisement);
98 if (FAILED(hr)) {
99 BLUETOOTH_LOG(ERROR) << "ActivateBluetoothLEAdvertisementInstance failed: "
100 << logging::SystemErrorCodeToString(hr);
101 return false;
102 }
103
104 ComPtr<IVector<BluetoothLEManufacturerData*>> manufacturer_data_list;
105 hr = advertisement->get_ManufacturerData(&manufacturer_data_list);
106 if (FAILED(hr)) {
107 BLUETOOTH_LOG(ERROR) << "Getting ManufacturerData failed: "
108 << logging::SystemErrorCodeToString(hr);
109 return false;
110 }
111
112 ComPtr<IBluetoothLEManufacturerDataFactory> manufacturer_data_factory;
113 hr = GetBluetoothLEManufacturerDataFactory(&manufacturer_data_factory);
114 if (FAILED(hr)) {
115 BLUETOOTH_LOG(ERROR) << "GetBluetoothLEManufacturerDataFactory failed: "
116 << logging::SystemErrorCodeToString(hr);
117 return false;
118 }
119
120 for (const auto& pair : *manufacturer_data) {
121 uint16_t manufacturer = pair.first;
122 const std::vector<uint8_t>& data = pair.second;
123
124 ComPtr<IBuffer> buffer;
125 hr = base::win::CreateIBufferFromData(data.data(), data.size(), &buffer);
126 if (FAILED(hr)) {
127 BLUETOOTH_LOG(ERROR) << "CreateIBufferFromData() failed: "
128 << logging::SystemErrorCodeToString(hr);
129 return false;
130 }
131
132 ComPtr<IBluetoothLEManufacturerData> manufacturer_data_entry;
133 hr = manufacturer_data_factory->Create(manufacturer, buffer.Get(),
134 &manufacturer_data_entry);
135 if (FAILED(hr)) {
136 BLUETOOTH_LOG(ERROR) << "Creating BluetoothLEManufacturerData failed: "
137 << logging::SystemErrorCodeToString(hr);
138 return false;
139 }
140
141 hr = manufacturer_data_list->Append(manufacturer_data_entry.Get());
142 if (FAILED(hr)) {
143 BLUETOOTH_LOG(ERROR) << "Appending BluetoothLEManufacturerData failed: "
144 << logging::SystemErrorCodeToString(hr);
145 return false;
146 }
147 }
148
149 ComPtr<IBluetoothLEAdvertisementPublisherFactory> publisher_factory;
150 hr =
151 GetBluetoothLEAdvertisementPublisherActivationFactory(&publisher_factory);
152 if (FAILED(hr)) {
153 BLUETOOTH_LOG(ERROR)
154 << "GetBluetoothLEAdvertisementPublisherActivationFactory "
155 "failed:"
156 << logging::SystemErrorCodeToString(hr);
157 return false;
158 }
159
160 hr = publisher_factory->Create(advertisement.Get(), &publisher_);
161 if (FAILED(hr)) {
162 BLUETOOTH_LOG(ERROR)
163 << "Creating IBluetoothLEAdvertisementPublisher failed: "
164 << logging::SystemErrorCodeToString(hr);
165 return false;
166 }
167
168 return true;
169 }
170
Register(SuccessCallback callback,ErrorCallback error_callback)171 void BluetoothAdvertisementWinrt::Register(SuccessCallback callback,
172 ErrorCallback error_callback) {
173 // Register should only be called once during initialization.
174 DCHECK(!status_changed_token_);
175 DCHECK(!pending_register_callbacks_);
176 DCHECK(!pending_unregister_callbacks_);
177
178 // Register should only be called after successful initialization.
179 DCHECK(publisher_);
180
181 status_changed_token_ = AddTypedEventHandler(
182 publisher_.Get(), &IBluetoothLEAdvertisementPublisher::add_StatusChanged,
183 base::BindRepeating(&BluetoothAdvertisementWinrt::OnStatusChanged,
184 weak_ptr_factory_.GetWeakPtr()));
185 if (!status_changed_token_) {
186 base::ThreadTaskRunnerHandle::Get()->PostTask(
187 FROM_HERE, base::BindOnce(std::move(error_callback),
188 ERROR_STARTING_ADVERTISEMENT));
189 return;
190 }
191
192 HRESULT hr = publisher_->Start();
193 if (FAILED(hr)) {
194 BLUETOOTH_LOG(ERROR)
195 << "Starting IBluetoothLEAdvertisementPublisher failed: "
196 << logging::SystemErrorCodeToString(hr);
197 base::ThreadTaskRunnerHandle::Get()->PostTask(
198 FROM_HERE, base::BindOnce(std::move(error_callback),
199 ERROR_STARTING_ADVERTISEMENT));
200 RemoveStatusChangedHandler(publisher_.Get(), *status_changed_token_);
201 status_changed_token_.reset();
202 return;
203 }
204
205 pending_register_callbacks_ = std::make_unique<PendingCallbacks>(
206 std::move(callback), std::move(error_callback));
207 }
208
Unregister(SuccessCallback success_callback,ErrorCallback error_callback)209 void BluetoothAdvertisementWinrt::Unregister(SuccessCallback success_callback,
210 ErrorCallback error_callback) {
211 // Unregister() should only be called when an advertisement is registered
212 // already, or during destruction. In both of these cases there should be no
213 // pending register callbacks and the publisher should be present.
214 DCHECK(!pending_register_callbacks_);
215 DCHECK(publisher_);
216
217 if (pending_unregister_callbacks_) {
218 BLUETOOTH_LOG(ERROR) << "An Unregister Operation is already in progress.";
219 base::ThreadTaskRunnerHandle::Get()->PostTask(
220 FROM_HERE,
221 base::BindOnce(std::move(error_callback), ERROR_RESET_ADVERTISING));
222 return;
223 }
224
225 BluetoothLEAdvertisementPublisherStatus status;
226 HRESULT hr = publisher_->get_Status(&status);
227 if (FAILED(hr)) {
228 BLUETOOTH_LOG(ERROR) << "Getting the Publisher Status failed: "
229 << logging::SystemErrorCodeToString(hr);
230 base::ThreadTaskRunnerHandle::Get()->PostTask(
231 FROM_HERE,
232 base::BindOnce(std::move(error_callback), ERROR_RESET_ADVERTISING));
233 return;
234 }
235
236 if (status == BluetoothLEAdvertisementPublisherStatus_Aborted) {
237 // Report an error if the publisher is in the aborted state.
238 base::ThreadTaskRunnerHandle::Get()->PostTask(
239 FROM_HERE,
240 base::BindOnce(std::move(error_callback), ERROR_RESET_ADVERTISING));
241 return;
242 }
243
244 if (status == BluetoothLEAdvertisementPublisherStatus_Stopped) {
245 // Report success if the publisher is already stopped.
246 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
247 std::move(success_callback));
248 return;
249 }
250
251 hr = publisher_->Stop();
252 if (FAILED(hr)) {
253 BLUETOOTH_LOG(ERROR)
254 << "IBluetoothLEAdvertisementPublisher::Stop() failed: "
255 << logging::SystemErrorCodeToString(hr);
256 base::ThreadTaskRunnerHandle::Get()->PostTask(
257 FROM_HERE,
258 base::BindOnce(std::move(error_callback), ERROR_RESET_ADVERTISING));
259 return;
260 }
261
262 pending_unregister_callbacks_ = std::make_unique<PendingCallbacks>(
263 std::move(success_callback), std::move(error_callback));
264 }
265
266 IBluetoothLEAdvertisementPublisher*
GetPublisherForTesting()267 BluetoothAdvertisementWinrt::GetPublisherForTesting() {
268 return publisher_.Get();
269 }
270
~BluetoothAdvertisementWinrt()271 BluetoothAdvertisementWinrt::~BluetoothAdvertisementWinrt() {
272 if (status_changed_token_) {
273 DCHECK(publisher_);
274 RemoveStatusChangedHandler(publisher_.Get(), *status_changed_token_);
275 }
276
277 // Stop any pending register operation.
278 if (pending_register_callbacks_) {
279 auto callbacks = std::move(pending_register_callbacks_);
280 std::move(callbacks->error_callback).Run(ERROR_STARTING_ADVERTISEMENT);
281 }
282
283 // Unregister the advertisement on a best effort basis if it's not already in
284 // process of doing so.
285 if (!pending_unregister_callbacks_ && publisher_)
286 Unregister(base::DoNothing(), base::DoNothing());
287 }
288
289 HRESULT
290 BluetoothAdvertisementWinrt::
GetBluetoothLEAdvertisementPublisherActivationFactory(IBluetoothLEAdvertisementPublisherFactory ** factory) const291 GetBluetoothLEAdvertisementPublisherActivationFactory(
292 IBluetoothLEAdvertisementPublisherFactory** factory) const {
293 return base::win::GetActivationFactory<
294 IBluetoothLEAdvertisementPublisherFactory,
295 RuntimeClass_Windows_Devices_Bluetooth_Advertisement_BluetoothLEAdvertisementPublisher>(
296 factory);
297 }
298
299 HRESULT
ActivateBluetoothLEAdvertisementInstance(IBluetoothLEAdvertisement ** instance) const300 BluetoothAdvertisementWinrt::ActivateBluetoothLEAdvertisementInstance(
301 IBluetoothLEAdvertisement** instance) const {
302 auto advertisement_hstring = base::win::ScopedHString::Create(
303 RuntimeClass_Windows_Devices_Bluetooth_Advertisement_BluetoothLEAdvertisement);
304 if (!advertisement_hstring.is_valid())
305 return E_FAIL;
306
307 ComPtr<IInspectable> inspectable;
308 HRESULT hr =
309 base::win::RoActivateInstance(advertisement_hstring.get(), &inspectable);
310 if (FAILED(hr)) {
311 BLUETOOTH_LOG(ERROR) << "RoActivateInstance failed: "
312 << logging::SystemErrorCodeToString(hr);
313 return hr;
314 }
315
316 ComPtr<IBluetoothLEAdvertisement> advertisement;
317 hr = inspectable.As(&advertisement);
318 if (FAILED(hr)) {
319 BLUETOOTH_LOG(ERROR) << "As IBluetoothLEAdvertisementWatcher failed: "
320 << logging::SystemErrorCodeToString(hr);
321 return hr;
322 }
323
324 return advertisement.CopyTo(instance);
325 }
326
327 HRESULT
GetBluetoothLEManufacturerDataFactory(IBluetoothLEManufacturerDataFactory ** factory) const328 BluetoothAdvertisementWinrt::GetBluetoothLEManufacturerDataFactory(
329 IBluetoothLEManufacturerDataFactory** factory) const {
330 return base::win::GetActivationFactory<
331 IBluetoothLEManufacturerDataFactory,
332 RuntimeClass_Windows_Devices_Bluetooth_Advertisement_BluetoothLEManufacturerData>(
333 factory);
334 }
335
PendingCallbacks(SuccessCallback callback,ErrorCallback error_callback)336 BluetoothAdvertisementWinrt::PendingCallbacks::PendingCallbacks(
337 SuccessCallback callback,
338 ErrorCallback error_callback)
339 : callback(std::move(callback)),
340 error_callback(std::move(error_callback)) {}
341
342 BluetoothAdvertisementWinrt::PendingCallbacks::~PendingCallbacks() = default;
343
OnStatusChanged(IBluetoothLEAdvertisementPublisher * publisher,IBluetoothLEAdvertisementPublisherStatusChangedEventArgs * changed)344 void BluetoothAdvertisementWinrt::OnStatusChanged(
345 IBluetoothLEAdvertisementPublisher* publisher,
346 IBluetoothLEAdvertisementPublisherStatusChangedEventArgs* changed) {
347 BluetoothLEAdvertisementPublisherStatus status;
348 HRESULT hr = changed->get_Status(&status);
349 if (FAILED(hr)) {
350 BLUETOOTH_LOG(ERROR) << "Getting the Publisher Status failed: "
351 << logging::SystemErrorCodeToString(hr);
352 return;
353 }
354
355 BLUETOOTH_LOG(EVENT) << "Publisher Status: " << static_cast<int>(status);
356 if (status == BluetoothLEAdvertisementPublisherStatus_Stopped) {
357 // Notify Observers.
358 for (auto& observer : observers_)
359 observer.AdvertisementReleased(this);
360 }
361
362 // Return early if there is no pending action.
363 if (!pending_register_callbacks_ && !pending_unregister_callbacks_)
364 return;
365
366 // Register and Unregister should never be pending at the same time.
367 DCHECK(!pending_register_callbacks_ || !pending_unregister_callbacks_);
368
369 const bool is_starting = pending_register_callbacks_ != nullptr;
370 ErrorCode error_code =
371 is_starting ? ERROR_STARTING_ADVERTISEMENT : ERROR_RESET_ADVERTISING;
372
373 // Clears out pending callbacks by moving them into a local variable and runs
374 // the appropriate error callback with |error_code|.
375 auto run_error_cb = [&](ErrorCode error_code) {
376 auto callbacks = std::move(is_starting ? pending_register_callbacks_
377 : pending_unregister_callbacks_);
378 std::move(callbacks->error_callback).Run(error_code);
379 };
380
381 if (status == BluetoothLEAdvertisementPublisherStatus_Aborted) {
382 BluetoothError bluetooth_error;
383 hr = changed->get_Error(&bluetooth_error);
384 if (FAILED(hr)) {
385 BLUETOOTH_LOG(ERROR) << "Getting the Publisher Error failed: "
386 << logging::SystemErrorCodeToString(hr);
387 run_error_cb(error_code);
388 return;
389 }
390
391 BLUETOOTH_LOG(EVENT) << "Publisher aborted: "
392 << static_cast<int>(bluetooth_error);
393 switch (bluetooth_error) {
394 case BluetoothError_RadioNotAvailable:
395 error_code = ERROR_ADAPTER_POWERED_OFF;
396 break;
397 case BluetoothError_NotSupported:
398 error_code = ERROR_UNSUPPORTED_PLATFORM;
399 break;
400 default:
401 break;
402 }
403
404 run_error_cb(error_code);
405 return;
406 }
407
408 if (is_starting &&
409 status == BluetoothLEAdvertisementPublisherStatus_Started) {
410 BLUETOOTH_LOG(EVENT) << "Starting the Publisher was successful.";
411 auto callbacks = std::move(pending_register_callbacks_);
412 std::move(callbacks->callback).Run();
413 return;
414 }
415
416 if (!is_starting &&
417 status == BluetoothLEAdvertisementPublisherStatus_Stopped) {
418 BLUETOOTH_LOG(EVENT) << "Stopping the Publisher was successful.";
419 auto callbacks = std::move(pending_unregister_callbacks_);
420 std::move(callbacks->callback).Run();
421 return;
422 }
423
424 // The other states are temporary and we expect a future StatusChanged
425 // event.
426 }
427
428 } // namespace device
429