fido_ble_transaction.cc 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. // Copyright 2017 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/fido/cable/fido_ble_transaction.h"
  5. #include <utility>
  6. #include "base/bind.h"
  7. #include "base/callback_helpers.h"
  8. #include "base/strings/string_number_conversions.h"
  9. #include "base/threading/thread_task_runner_handle.h"
  10. #include "components/device_event_log/device_event_log.h"
  11. #include "device/fido/cable/fido_ble_connection.h"
  12. #include "device/fido/fido_constants.h"
  13. namespace device {
  14. FidoBleTransaction::FidoBleTransaction(FidoBleConnection* connection,
  15. uint16_t control_point_length)
  16. : connection_(connection), control_point_length_(control_point_length) {
  17. buffer_.reserve(control_point_length_);
  18. }
  19. FidoBleTransaction::~FidoBleTransaction() = default;
  20. void FidoBleTransaction::WriteRequestFrame(FidoBleFrame request_frame,
  21. FrameCallback callback) {
  22. if (control_point_length_ < 3u) {
  23. FIDO_LOG(DEBUG) << "Control Point Length is too short: "
  24. << control_point_length_;
  25. base::ThreadTaskRunnerHandle::Get()->PostTask(
  26. FROM_HERE, base::BindOnce(std::move(callback), absl::nullopt));
  27. return;
  28. }
  29. DCHECK(!request_frame_ && callback_.is_null());
  30. request_frame_ = std::move(request_frame);
  31. callback_ = std::move(callback);
  32. FidoBleFrameInitializationFragment request_init_fragment;
  33. std::tie(request_init_fragment, request_cont_fragments_) =
  34. request_frame_->ToFragments(control_point_length_);
  35. WriteRequestFragment(request_init_fragment);
  36. }
  37. void FidoBleTransaction::WriteRequestFragment(
  38. const FidoBleFrameFragment& fragment) {
  39. buffer_.clear();
  40. fragment.Serialize(&buffer_);
  41. DCHECK(!has_pending_request_fragment_write_);
  42. has_pending_request_fragment_write_ = true;
  43. FIDO_LOG(DEBUG) << "Writing request fragment: " +
  44. base::HexEncode(buffer_.data(), buffer_.size());
  45. // A weak pointer is required, since this call might time out. If that
  46. // happens, the current FidoBleTransaction could be destroyed.
  47. connection_->WriteControlPoint(
  48. buffer_, base::BindOnce(&FidoBleTransaction::OnRequestFragmentWritten,
  49. weak_factory_.GetWeakPtr()));
  50. // WriteRequestFragment() expects an invocation of OnRequestFragmentWritten()
  51. // soon after.
  52. StartTimeout();
  53. }
  54. static void WriteCancel(FidoBleConnection* connection) {
  55. FIDO_LOG(DEBUG) << "Writing control point 'Cancel'";
  56. connection->WriteControlPoint(
  57. {static_cast<uint8_t>(FidoBleDeviceCommand::kCancel), 0, 0},
  58. base::DoNothing());
  59. }
  60. void FidoBleTransaction::OnRequestFragmentWritten(bool success) {
  61. DCHECK(has_pending_request_fragment_write_);
  62. has_pending_request_fragment_write_ = false;
  63. StopTimeout();
  64. if (!success) {
  65. OnError(absl::nullopt);
  66. return;
  67. }
  68. if (!request_cont_fragments_.empty()) {
  69. auto next_request_fragment = std::move(request_cont_fragments_.front());
  70. request_cont_fragments_.pop();
  71. WriteRequestFragment(next_request_fragment);
  72. return;
  73. }
  74. if (cancel_pending_) {
  75. cancel_pending_ = false;
  76. cancel_sent_ = true;
  77. WriteCancel(connection_);
  78. }
  79. // The transaction wrote the full request frame. It is possible that the full
  80. // response frame was already received, at which point we process it and run
  81. // the completim callback.
  82. if (response_frame_assembler_ && response_frame_assembler_->IsDone()) {
  83. ProcessResponseFrame();
  84. return;
  85. }
  86. // Otherwise, a response should follow soon after.
  87. StartTimeout();
  88. }
  89. void FidoBleTransaction::OnResponseFragment(std::vector<uint8_t> data) {
  90. StopTimeout();
  91. if (!response_frame_assembler_) {
  92. FidoBleFrameInitializationFragment fragment;
  93. if (!FidoBleFrameInitializationFragment::Parse(data, &fragment)) {
  94. FIDO_LOG(ERROR) << "Malformed Frame Initialization Fragment";
  95. OnError(absl::nullopt);
  96. return;
  97. }
  98. response_frame_assembler_.emplace(fragment);
  99. } else {
  100. FidoBleFrameContinuationFragment fragment;
  101. if (!FidoBleFrameContinuationFragment::Parse(data, &fragment) ||
  102. !response_frame_assembler_->AddFragment(fragment)) {
  103. FIDO_LOG(ERROR) << "Malformed Frame Continuation Fragment";
  104. OnError(absl::nullopt);
  105. return;
  106. }
  107. }
  108. if (!response_frame_assembler_->IsDone()) {
  109. // Expect the next reponse fragment to arrive soon.
  110. StartTimeout();
  111. return;
  112. }
  113. // It is possible to receive the last response fragment before the write of
  114. // the last request fragment has been acknowledged. If this is the case, do
  115. // not run the completion callback yet.
  116. // It is OK to process keep alive frames before the request frame is
  117. // acknowledged.
  118. if (!has_pending_request_fragment_write_ ||
  119. response_frame_assembler_->GetFrame()->command() ==
  120. FidoBleDeviceCommand::kKeepAlive) {
  121. ProcessResponseFrame();
  122. }
  123. }
  124. void FidoBleTransaction::Cancel() {
  125. if (cancel_sent_) {
  126. return;
  127. }
  128. if (has_pending_request_fragment_write_) {
  129. // A mesasge is still being written. Signal that the cancelation should be
  130. // written once complete.
  131. cancel_pending_ = true;
  132. } else {
  133. cancel_sent_ = true;
  134. WriteCancel(connection_);
  135. }
  136. }
  137. void FidoBleTransaction::ProcessResponseFrame() {
  138. DCHECK(response_frame_assembler_ && response_frame_assembler_->IsDone());
  139. auto response_frame = std::move(*response_frame_assembler_->GetFrame());
  140. response_frame_assembler_.reset();
  141. DCHECK(request_frame_.has_value());
  142. if (response_frame.command() == request_frame_->command()) {
  143. request_frame_.reset();
  144. std::move(callback_).Run(std::move(response_frame));
  145. return;
  146. }
  147. if (response_frame.command() == FidoBleDeviceCommand::kKeepAlive) {
  148. if (!response_frame.IsValid()) {
  149. FIDO_LOG(ERROR) << "Got invalid KeepAlive Command.";
  150. OnError(absl::nullopt);
  151. return;
  152. }
  153. FIDO_LOG(DEBUG) << "CMD_KEEPALIVE: "
  154. << static_cast<int>(response_frame.GetKeepaliveCode());
  155. // Expect another reponse frame soon.
  156. StartTimeout();
  157. return;
  158. }
  159. if (response_frame.command() == FidoBleDeviceCommand::kError) {
  160. if (!response_frame.IsValid()) {
  161. FIDO_LOG(ERROR) << "Got invald Error Command.";
  162. OnError(absl::nullopt);
  163. return;
  164. }
  165. FIDO_LOG(ERROR) << "CMD_ERROR: "
  166. << static_cast<int>(response_frame.GetErrorCode());
  167. OnError(std::move(response_frame));
  168. return;
  169. }
  170. FIDO_LOG(ERROR) << "Got unexpected Command: "
  171. << static_cast<int>(response_frame.command());
  172. OnError(absl::nullopt);
  173. }
  174. void FidoBleTransaction::StartTimeout() {
  175. timer_.Start(FROM_HERE, kDeviceTimeout,
  176. base::BindOnce(&FidoBleTransaction::OnError,
  177. base::Unretained(this), absl::nullopt));
  178. }
  179. void FidoBleTransaction::StopTimeout() {
  180. timer_.Stop();
  181. }
  182. void FidoBleTransaction::OnError(absl::optional<FidoBleFrame> response_frame) {
  183. request_frame_.reset();
  184. request_cont_fragments_ = base::queue<FidoBleFrameContinuationFragment>();
  185. response_frame_assembler_.reset();
  186. // |callback_| might have been run due to a previous error.
  187. if (callback_)
  188. std::move(callback_).Run(std::move(response_frame));
  189. }
  190. } // namespace device