bluetooth_pairing_winrt.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  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. #include "device/bluetooth/bluetooth_pairing_winrt.h"
  5. #include <utility>
  6. #include "base/bind.h"
  7. #include "base/feature_list.h"
  8. #include "base/logging.h"
  9. #include "base/strings/string_number_conversions.h"
  10. #include "base/strings/string_piece.h"
  11. #include "base/task/thread_pool.h"
  12. #include "base/win/com_init_util.h"
  13. #include "base/win/post_async_results.h"
  14. #include "base/win/scoped_hstring.h"
  15. #include "device/base/features.h"
  16. #include "device/bluetooth/bluetooth_device_winrt.h"
  17. #include "device/bluetooth/event_utils_winrt.h"
  18. namespace device {
  19. namespace {
  20. using ABI::Windows::Devices::Enumeration::DevicePairingKinds;
  21. using ABI::Windows::Devices::Enumeration::DevicePairingKinds_ConfirmOnly;
  22. using ABI::Windows::Devices::Enumeration::DevicePairingKinds_ConfirmPinMatch;
  23. using ABI::Windows::Devices::Enumeration::DevicePairingKinds_DisplayPin;
  24. using ABI::Windows::Devices::Enumeration::DevicePairingKinds_ProvidePin;
  25. using ABI::Windows::Devices::Enumeration::DevicePairingResult;
  26. using ABI::Windows::Devices::Enumeration::DevicePairingResultStatus;
  27. using ABI::Windows::Devices::Enumeration::
  28. DevicePairingResultStatus_AlreadyPaired;
  29. using ABI::Windows::Devices::Enumeration::
  30. DevicePairingResultStatus_AuthenticationFailure;
  31. using ABI::Windows::Devices::Enumeration::
  32. DevicePairingResultStatus_AuthenticationTimeout;
  33. using ABI::Windows::Devices::Enumeration::
  34. DevicePairingResultStatus_ConnectionRejected;
  35. using ABI::Windows::Devices::Enumeration::DevicePairingResultStatus_Failed;
  36. using ABI::Windows::Devices::Enumeration::
  37. DevicePairingResultStatus_OperationAlreadyInProgress;
  38. using ABI::Windows::Devices::Enumeration::DevicePairingResultStatus_Paired;
  39. using ABI::Windows::Devices::Enumeration::
  40. DevicePairingResultStatus_PairingCanceled;
  41. using ABI::Windows::Devices::Enumeration::
  42. DevicePairingResultStatus_RejectedByHandler;
  43. using CompletionCallback = base::OnceCallback<void(HRESULT hr)>;
  44. using ConnectErrorCode = BluetoothDevice::ConnectErrorCode;
  45. using ABI::Windows::Devices::Enumeration::IDeviceInformationCustomPairing;
  46. using ABI::Windows::Devices::Enumeration::IDevicePairingRequestedEventArgs;
  47. using ABI::Windows::Devices::Enumeration::IDevicePairingResult;
  48. using ABI::Windows::Foundation::IAsyncOperation;
  49. using Microsoft::WRL::ComPtr;
  50. void PostTask(BluetoothPairingWinrt::ConnectCallback callback,
  51. absl::optional<BluetoothDevice::ConnectErrorCode> error_code) {
  52. base::ThreadTaskRunnerHandle::Get()->PostTask(
  53. FROM_HERE, base::BindOnce(std::move(callback), error_code));
  54. }
  55. HRESULT CompleteDeferral(
  56. Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IDeferral> deferral) {
  57. // Apparently deferrals may be created (aka obtained) on the main thread
  58. // initialized for STA, but must be completed on a thread with COM initialized
  59. // for MTA. If the deferral is completed on the main thread then the
  60. // Complete() call will succeed, i.e return S_OK, but the Windows Device
  61. // Association Service will be hung and all Bluetooth association changes
  62. // (system wide) will fail.
  63. base::win::AssertComApartmentType(base::win::ComApartmentType::MTA);
  64. return deferral->Complete();
  65. }
  66. // TODO: https://crbug.com/1345471, once we refactor the
  67. // BluetoothDevice::ConfirmPasskey() to use std::u16string instead of uint32_t
  68. // we can then get rid of HstringToUint32()
  69. bool HstringToUint32(HSTRING in, uint32_t& out) {
  70. if (!in) {
  71. DVLOG(2) << "HstringToUint32: HSTRING PIN is NULL.";
  72. return false;
  73. }
  74. base::win::ScopedHString scoped_hstring{in};
  75. std::string str = scoped_hstring.GetAsUTF8();
  76. // PIN has to be <= 6 digits
  77. if (str.length() > 6) {
  78. DVLOG(2) << "HstringToUint32: PIN code = " << str
  79. << " which is more than 6 digits.";
  80. return false;
  81. }
  82. // Remove leading '0' before being converted into uint32_t
  83. str.erase(0, str.find_first_not_of('0'));
  84. // If we failed to convert str into unsigned int we cancel pairing by return
  85. // false
  86. if (base::StringToUint(str, &out)) {
  87. return true;
  88. } else {
  89. DVLOG(2) << "HstringToUint32: failed to convert pin = " << str
  90. << " into uint32_t";
  91. return false;
  92. }
  93. }
  94. } // namespace
  95. BluetoothPairingWinrt::BluetoothPairingWinrt(
  96. BluetoothDeviceWinrt* device,
  97. BluetoothDevice::PairingDelegate* pairing_delegate,
  98. ComPtr<IDeviceInformationCustomPairing> custom_pairing,
  99. ConnectCallback callback)
  100. : device_(device),
  101. pairing_delegate_(pairing_delegate),
  102. custom_pairing_(std::move(custom_pairing)),
  103. callback_(std::move(callback)) {
  104. DCHECK(device_);
  105. DCHECK(pairing_delegate_);
  106. DCHECK(custom_pairing_);
  107. }
  108. BluetoothPairingWinrt::~BluetoothPairingWinrt() {
  109. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  110. if (!pairing_requested_token_)
  111. return;
  112. HRESULT hr =
  113. custom_pairing_->remove_PairingRequested(*pairing_requested_token_);
  114. if (FAILED(hr)) {
  115. DVLOG(2) << "Removing PairingRequested Handler failed: "
  116. << logging::SystemErrorCodeToString(hr);
  117. }
  118. }
  119. void BluetoothPairingWinrt::StartPairing() {
  120. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  121. pairing_requested_token_ = AddTypedEventHandler(
  122. custom_pairing_.Get(),
  123. &IDeviceInformationCustomPairing::add_PairingRequested,
  124. base::BindRepeating(&BluetoothPairingWinrt::OnPairingRequested,
  125. weak_ptr_factory_.GetWeakPtr()));
  126. if (!pairing_requested_token_) {
  127. PostTask(std::move(callback_),
  128. BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
  129. return;
  130. }
  131. ComPtr<IAsyncOperation<DevicePairingResult*>> pair_op;
  132. HRESULT hr = custom_pairing_->PairAsync(
  133. DevicePairingKinds_ConfirmOnly | DevicePairingKinds_ProvidePin |
  134. DevicePairingKinds_ConfirmPinMatch,
  135. &pair_op);
  136. if (FAILED(hr)) {
  137. DVLOG(2) << "DeviceInformationCustomPairing::PairAsync() failed: "
  138. << logging::SystemErrorCodeToString(hr);
  139. PostTask(std::move(callback_),
  140. BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
  141. return;
  142. }
  143. hr = base::win::PostAsyncResults(
  144. std::move(pair_op), base::BindOnce(&BluetoothPairingWinrt::OnPair,
  145. weak_ptr_factory_.GetWeakPtr()));
  146. if (FAILED(hr)) {
  147. DVLOG(2) << "PostAsyncResults failed: "
  148. << logging::SystemErrorCodeToString(hr);
  149. PostTask(std::move(callback_),
  150. BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
  151. return;
  152. }
  153. }
  154. bool BluetoothPairingWinrt::ExpectingPinCode() const {
  155. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  156. return expecting_pin_code_;
  157. }
  158. void BluetoothPairingWinrt::OnSetPinCodeDeferralCompletion(HRESULT hr) {
  159. if (FAILED(hr)) {
  160. DVLOG(2) << "Completing Deferred Pairing Request failed: "
  161. << logging::SystemErrorCodeToString(hr);
  162. std::move(callback_).Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
  163. }
  164. }
  165. void BluetoothPairingWinrt::OnConfirmPairingDeferralCompletion(HRESULT hr) {
  166. if (FAILED(hr)) {
  167. DVLOG(2) << "Completing Deferred Pairing Request failed: "
  168. << logging::SystemErrorCodeToString(hr);
  169. std::move(callback_).Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
  170. }
  171. }
  172. void BluetoothPairingWinrt::SetPinCode(base::StringPiece pin_code) {
  173. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  174. DVLOG(2) << "BluetoothPairingWinrt::SetPinCode(" << pin_code << ")";
  175. auto pin_hstring = base::win::ScopedHString::Create(pin_code);
  176. DCHECK(expecting_pin_code_);
  177. expecting_pin_code_ = false;
  178. DCHECK(pairing_requested_);
  179. HRESULT hr = pairing_requested_->AcceptWithPin(pin_hstring.get());
  180. if (FAILED(hr)) {
  181. DVLOG(2) << "Accepting Pairing Request With Pin failed: "
  182. << logging::SystemErrorCodeToString(hr);
  183. std::move(callback_).Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
  184. return;
  185. }
  186. DCHECK(pairing_deferral_);
  187. base::ThreadPool::PostTaskAndReplyWithResult(
  188. FROM_HERE, {base::MayBlock()},
  189. base::BindOnce(&CompleteDeferral, std::move(pairing_deferral_)),
  190. base::BindOnce(&BluetoothPairingWinrt::OnSetPinCodeDeferralCompletion,
  191. weak_ptr_factory_.GetWeakPtr()));
  192. }
  193. void BluetoothPairingWinrt::ConfirmPairing() {
  194. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  195. DVLOG(2) << "BluetoothPairingWinrt::ConfirmPairing() is called";
  196. DCHECK(pairing_requested_);
  197. HRESULT hr = pairing_requested_->Accept();
  198. if (FAILED(hr)) {
  199. DVLOG(2) << "Accepting Pairing Request in ConfirmPairing failed: "
  200. << logging::SystemErrorCodeToString(hr);
  201. std::move(callback_).Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
  202. return;
  203. }
  204. DCHECK(pairing_deferral_);
  205. base::ThreadPool::PostTaskAndReplyWithResult(
  206. FROM_HERE, {base::MayBlock()},
  207. base::BindOnce(&CompleteDeferral, std::move(pairing_deferral_)),
  208. base::BindOnce(&BluetoothPairingWinrt::OnConfirmPairingDeferralCompletion,
  209. weak_ptr_factory_.GetWeakPtr()));
  210. }
  211. void BluetoothPairingWinrt::OnRejectPairing(HRESULT hr) {
  212. if (FAILED(hr)) {
  213. DVLOG(2) << "Completing Deferred Pairing Request failed: "
  214. << logging::SystemErrorCodeToString(hr);
  215. std::move(callback_).Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
  216. return;
  217. }
  218. std::move(callback_).Run(
  219. BluetoothDevice::ConnectErrorCode::ERROR_AUTH_REJECTED);
  220. }
  221. void BluetoothPairingWinrt::RejectPairing() {
  222. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  223. DVLOG(2) << "BluetoothPairingWinrt::RejectPairing()";
  224. DCHECK(pairing_deferral_);
  225. base::ThreadPool::PostTaskAndReplyWithResult(
  226. FROM_HERE, {base::MayBlock()},
  227. base::BindOnce(&CompleteDeferral, std::move(pairing_deferral_)),
  228. base::BindOnce(&BluetoothPairingWinrt::OnRejectPairing,
  229. weak_ptr_factory_.GetWeakPtr()));
  230. }
  231. void BluetoothPairingWinrt::OnCancelPairing(HRESULT hr) {
  232. // This method is normally never called. Usually when CancelPairing() is
  233. // invoked the deferral is completed, which immediately calls OnPair(), which
  234. // runs |callback_| and destroys this object before this method can be
  235. // executed. However, if the deferral fails to complete, this will be run.
  236. if (FAILED(hr)) {
  237. DVLOG(2) << "Completing Deferred Pairing Request failed: "
  238. << logging::SystemErrorCodeToString(hr);
  239. std::move(callback_).Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
  240. return;
  241. }
  242. std::move(callback_).Run(
  243. BluetoothDevice::ConnectErrorCode::ERROR_AUTH_CANCELED);
  244. }
  245. void BluetoothPairingWinrt::CancelPairing() {
  246. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  247. DVLOG(2) << "BluetoothPairingWinrt::CancelPairing()";
  248. DCHECK(pairing_deferral_);
  249. // There is no way to explicitly cancel an in-progress pairing as
  250. // DevicePairingRequestedEventArgs has no Cancel() method. Our approach is to
  251. // complete the deferral, without accepting, which results in a
  252. // RejectedByHandler result status. |was_cancelled_| is set so that OnPair(),
  253. // which is called when the deferral is completed, will know that cancellation
  254. // was the actual result.
  255. was_cancelled_ = true;
  256. base::ThreadPool::PostTaskAndReplyWithResult(
  257. FROM_HERE, {base::MayBlock()},
  258. base::BindOnce(&CompleteDeferral, std::move(pairing_deferral_)),
  259. base::BindOnce(&BluetoothPairingWinrt::OnCancelPairing,
  260. weak_ptr_factory_.GetWeakPtr()));
  261. }
  262. void BluetoothPairingWinrt::OnPairingRequested(
  263. IDeviceInformationCustomPairing* custom_pairing,
  264. IDevicePairingRequestedEventArgs* pairing_requested) {
  265. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  266. DVLOG(2) << "BluetoothPairingWinrt::OnPairingRequested()";
  267. DevicePairingKinds pairing_kind;
  268. HRESULT hr = pairing_requested->get_PairingKind(&pairing_kind);
  269. if (FAILED(hr)) {
  270. DVLOG(2) << "Getting Pairing Kind failed: "
  271. << logging::SystemErrorCodeToString(hr);
  272. std::move(callback_).Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
  273. return;
  274. }
  275. DVLOG(2) << "DevicePairingKind: " << static_cast<int>(pairing_kind);
  276. hr = pairing_requested->GetDeferral(&pairing_deferral_);
  277. if (FAILED(hr)) {
  278. DVLOG(2) << "Getting Pairing Deferral failed: "
  279. << logging::SystemErrorCodeToString(hr);
  280. std::move(callback_).Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
  281. return;
  282. }
  283. switch (pairing_kind) {
  284. case DevicePairingKinds_ProvidePin:
  285. pairing_requested_ = pairing_requested;
  286. expecting_pin_code_ = true;
  287. pairing_delegate_->RequestPinCode(device_);
  288. return;
  289. case DevicePairingKinds_ConfirmOnly:
  290. if (base::FeatureList::IsEnabled(
  291. features::kWebBluetoothConfirmPairingSupport)) {
  292. pairing_requested_ = pairing_requested;
  293. pairing_delegate_->AuthorizePairing(device_);
  294. return;
  295. } else {
  296. DVLOG(2) << "DevicePairingKind = " << static_cast<int>(pairing_kind)
  297. << " is not enabled by "
  298. "enable-web-bluetooth-confirm-pairing-support";
  299. }
  300. break;
  301. case DevicePairingKinds_ConfirmPinMatch:
  302. if (base::FeatureList::IsEnabled(
  303. features::kWebBluetoothConfirmPairingSupport)) {
  304. pairing_requested_ = pairing_requested;
  305. HSTRING hstring_pin;
  306. pairing_requested->get_Pin(&hstring_pin);
  307. uint32_t pin;
  308. if (HstringToUint32(hstring_pin, pin)) {
  309. pairing_delegate_->ConfirmPasskey(device_, pin);
  310. return;
  311. } else {
  312. DVLOG(2) << "DevicePairingKind = " << static_cast<int>(pairing_kind)
  313. << " has invalid PIN to display, cancel pairing procedure.";
  314. }
  315. } else {
  316. DVLOG(2) << "DevicePairingKind = " << static_cast<int>(pairing_kind)
  317. << " is not enabled by "
  318. "enable-web-bluetooth-confirm-pairing-support";
  319. }
  320. break;
  321. default:
  322. DVLOG(2) << "Unsupported DevicePairingKind = "
  323. << static_cast<int>(pairing_kind);
  324. break;
  325. }
  326. std::move(callback_).Run(
  327. BluetoothDevice::ConnectErrorCode::ERROR_AUTH_FAILED);
  328. }
  329. void BluetoothPairingWinrt::OnPair(
  330. ComPtr<IDevicePairingResult> pairing_result) {
  331. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  332. DevicePairingResultStatus status;
  333. HRESULT hr = pairing_result->get_Status(&status);
  334. if (FAILED(hr)) {
  335. DVLOG(2) << "Getting Pairing Result Status failed: "
  336. << logging::SystemErrorCodeToString(hr);
  337. std::move(callback_).Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
  338. return;
  339. }
  340. if (was_cancelled_ && status == DevicePairingResultStatus_RejectedByHandler) {
  341. // See comment in CancelPairing() for explanation of why was_cancelled_
  342. // is used.
  343. status = DevicePairingResultStatus_PairingCanceled;
  344. }
  345. DVLOG(2) << "Pairing Result Status: " << static_cast<int>(status);
  346. switch (status) {
  347. case DevicePairingResultStatus_AlreadyPaired:
  348. case DevicePairingResultStatus_Paired:
  349. std::move(callback_).Run(/*error_code=*/absl::nullopt);
  350. return;
  351. case DevicePairingResultStatus_PairingCanceled:
  352. std::move(callback_).Run(
  353. BluetoothDevice::ConnectErrorCode::ERROR_AUTH_CANCELED);
  354. return;
  355. case DevicePairingResultStatus_AuthenticationFailure:
  356. std::move(callback_).Run(
  357. BluetoothDevice::ConnectErrorCode::ERROR_AUTH_FAILED);
  358. return;
  359. case DevicePairingResultStatus_ConnectionRejected:
  360. case DevicePairingResultStatus_RejectedByHandler:
  361. std::move(callback_).Run(
  362. BluetoothDevice::ConnectErrorCode::ERROR_AUTH_REJECTED);
  363. return;
  364. case DevicePairingResultStatus_AuthenticationTimeout:
  365. std::move(callback_).Run(
  366. BluetoothDevice::ConnectErrorCode::ERROR_AUTH_TIMEOUT);
  367. return;
  368. case DevicePairingResultStatus_Failed:
  369. std::move(callback_).Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
  370. return;
  371. case DevicePairingResultStatus_OperationAlreadyInProgress:
  372. std::move(callback_).Run(
  373. BluetoothDevice::ConnectErrorCode::ERROR_INPROGRESS);
  374. return;
  375. default:
  376. std::move(callback_).Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
  377. return;
  378. }
  379. }
  380. } // namespace device