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