fast_pair_data_parser.cc 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. // Copyright 2021 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 "ash/services/quick_pair/fast_pair_data_parser.h"
  5. #include <algorithm>
  6. #include <cstdint>
  7. #include <vector>
  8. #include "ash/quick_pair/common/fast_pair/fast_pair_decoder.h"
  9. #include "ash/quick_pair/common/logging.h"
  10. #include "ash/services/quick_pair/fast_pair_decryption.h"
  11. #include "ash/services/quick_pair/public/cpp/battery_notification.h"
  12. #include "ash/services/quick_pair/public/cpp/not_discoverable_advertisement.h"
  13. #include "ash/services/quick_pair/public/mojom/fast_pair_data_parser.mojom.h"
  14. #include "base/base64.h"
  15. #include "base/containers/circular_deque.h"
  16. #include "base/containers/flat_map.h"
  17. #include "base/strings/string_number_conversions.h"
  18. #include "crypto/openssl_util.h"
  19. #include "device/bluetooth/public/cpp/bluetooth_address.h"
  20. #include "mojo/public/cpp/bindings/pending_receiver.h"
  21. #include "third_party/abseil-cpp/absl/types/optional.h"
  22. namespace {
  23. constexpr int kHeaderIndex = 0;
  24. constexpr int kFieldTypeBitmask = 0b00001111;
  25. constexpr int kFieldLengthBitmask = 0b11110000;
  26. constexpr int kHeaderLength = 1;
  27. constexpr int kFieldLengthOffset = 4;
  28. constexpr int kFieldTypeAccountKeyFilter = 0;
  29. constexpr int kFieldTypeAccountKeyFilterSalt = 1;
  30. constexpr int kFieldTypeAccountKeyFilterNoNotification = 2;
  31. constexpr int kFieldTypeBattery = 3;
  32. constexpr int kFieldTypeBatteryNoNotification = 4;
  33. constexpr int kBluetoothEvent = 0x01;
  34. constexpr int kEnableSilenceModeCode = 0x01;
  35. constexpr int kDisableSilenceModeCode = 0x02;
  36. constexpr int kCompanionAppEvent = 0x02;
  37. constexpr int kLogBufferFullCode = 0x01;
  38. constexpr int kDeviceInformationEvent = 0x03;
  39. constexpr int kModelIdCode = 0x01;
  40. constexpr int kBleAddressUpdatedCode = 0x02;
  41. constexpr int kBatteryUpdatedCode = 0x03;
  42. constexpr int kRemainingBatteryTimeCode = 0x04;
  43. constexpr int kActiveComponentsResponseCode = 0x06;
  44. constexpr int kPlatformTypeCode = 0x08;
  45. constexpr int kAndroidPlatform = 0x01;
  46. constexpr int kDeviceActionEvent = 0x04;
  47. constexpr int kRingCode = 0x01;
  48. constexpr int kAcknowledgementEvent = 0xFF;
  49. constexpr int kAckCode = 0x01;
  50. constexpr int kNakCode = 0x02;
  51. constexpr int kNotSupportedNak = 0x00;
  52. constexpr int kDeviceBusyNak = 0x01;
  53. constexpr int kNotAllowedDueToCurrentStateNak = 0x02;
  54. constexpr uint8_t kBatteryChargeBitmask = 0b10000000;
  55. constexpr uint8_t kBatteryPercentBitmask = 0b01111111;
  56. constexpr int kMinMessageByteCount = 4;
  57. constexpr int kAddressByteSize = 6;
  58. bool ValidateInputSizes(const std::vector<uint8_t>& aes_key_bytes,
  59. const std::vector<uint8_t>& encrypted_bytes) {
  60. if (aes_key_bytes.size() != kAesBlockByteSize) {
  61. QP_LOG(WARNING) << __func__
  62. << ": AES key should have size = " << kAesBlockByteSize
  63. << ", actual = " << aes_key_bytes.size();
  64. return false;
  65. }
  66. if (encrypted_bytes.size() != kEncryptedDataByteSize) {
  67. QP_LOG(WARNING) << __func__ << ": Encrypted bytes should have size = "
  68. << kEncryptedDataByteSize
  69. << ", actual = " << encrypted_bytes.size();
  70. return false;
  71. }
  72. return true;
  73. }
  74. void ConvertVectorsToArrays(
  75. const std::vector<uint8_t>& aes_key_bytes,
  76. const std::vector<uint8_t>& encrypted_bytes,
  77. std::array<uint8_t, kAesBlockByteSize>& out_aes_key_bytes,
  78. std::array<uint8_t, kEncryptedDataByteSize>& out_encrypted_bytes) {
  79. std::copy(aes_key_bytes.begin(), aes_key_bytes.end(),
  80. out_aes_key_bytes.begin());
  81. std::copy(encrypted_bytes.begin(), encrypted_bytes.end(),
  82. out_encrypted_bytes.begin());
  83. }
  84. int GetBatteryPercentange(uint8_t battery_byte) {
  85. int battery_percent = battery_byte & kBatteryPercentBitmask;
  86. if (battery_percent < 0 || battery_percent > 100)
  87. return -1;
  88. return battery_percent;
  89. }
  90. bool IsBatteryCharging(uint8_t battery_byte) {
  91. return (battery_byte & kBatteryChargeBitmask) != 0;
  92. }
  93. } // namespace
  94. namespace ash {
  95. namespace quick_pair {
  96. absl::optional<mojom::MessageGroup> MessageGroupFromByte(
  97. uint8_t message_group) {
  98. switch (message_group) {
  99. case kBluetoothEvent:
  100. return mojom::MessageGroup::kBluetoothEvent;
  101. case kCompanionAppEvent:
  102. return mojom::MessageGroup::kCompanionAppEvent;
  103. case kDeviceInformationEvent:
  104. return mojom::MessageGroup::kDeviceInformationEvent;
  105. case kDeviceActionEvent:
  106. return mojom::MessageGroup::kDeviceActionEvent;
  107. case kAcknowledgementEvent:
  108. return mojom::MessageGroup::kAcknowledgementEvent;
  109. default:
  110. return absl::nullopt;
  111. }
  112. }
  113. absl::optional<mojom::Acknowledgement> NakReasonFromByte(uint8_t nak_reason) {
  114. switch (nak_reason) {
  115. case kNotSupportedNak:
  116. return mojom::Acknowledgement::kNotSupportedNak;
  117. case kDeviceBusyNak:
  118. return mojom::Acknowledgement::kDeviceBusyNak;
  119. case kNotAllowedDueToCurrentStateNak:
  120. return mojom::Acknowledgement::kNotAllowedDueToCurrentStateNak;
  121. default:
  122. return absl::nullopt;
  123. }
  124. }
  125. mojom::BatteryInfoPtr CreateBatteryInfo(uint8_t battery_byte) {
  126. mojom::BatteryInfoPtr battery_info = mojom::BatteryInfo::New();
  127. battery_info->is_charging = IsBatteryCharging(battery_byte);
  128. battery_info->percentage = GetBatteryPercentange(battery_byte);
  129. return battery_info;
  130. }
  131. FastPairDataParser::FastPairDataParser(
  132. mojo::PendingReceiver<mojom::FastPairDataParser> receiver)
  133. : receiver_(this, std::move(receiver)) {
  134. crypto::EnsureOpenSSLInit();
  135. }
  136. FastPairDataParser::~FastPairDataParser() = default;
  137. void FastPairDataParser::GetHexModelIdFromServiceData(
  138. const std::vector<uint8_t>& service_data,
  139. GetHexModelIdFromServiceDataCallback callback) {
  140. std::move(callback).Run(
  141. fast_pair_decoder::HasModelId(&service_data)
  142. ? fast_pair_decoder::GetHexModelIdFromServiceData(&service_data)
  143. : absl::nullopt);
  144. }
  145. void FastPairDataParser::ParseDecryptedResponse(
  146. const std::vector<uint8_t>& aes_key_bytes,
  147. const std::vector<uint8_t>& encrypted_response_bytes,
  148. ParseDecryptedResponseCallback callback) {
  149. if (!ValidateInputSizes(aes_key_bytes, encrypted_response_bytes)) {
  150. std::move(callback).Run(absl::nullopt);
  151. return;
  152. }
  153. std::array<uint8_t, kAesBlockByteSize> key;
  154. std::array<uint8_t, kEncryptedDataByteSize> bytes;
  155. ConvertVectorsToArrays(aes_key_bytes, encrypted_response_bytes, key, bytes);
  156. std::move(callback).Run(
  157. fast_pair_decryption::ParseDecryptedResponse(key, bytes));
  158. }
  159. void FastPairDataParser::ParseDecryptedPasskey(
  160. const std::vector<uint8_t>& aes_key_bytes,
  161. const std::vector<uint8_t>& encrypted_passkey_bytes,
  162. ParseDecryptedPasskeyCallback callback) {
  163. if (!ValidateInputSizes(aes_key_bytes, encrypted_passkey_bytes)) {
  164. std::move(callback).Run(absl::nullopt);
  165. return;
  166. }
  167. std::array<uint8_t, kAesBlockByteSize> key;
  168. std::array<uint8_t, kEncryptedDataByteSize> bytes;
  169. ConvertVectorsToArrays(aes_key_bytes, encrypted_passkey_bytes, key, bytes);
  170. std::move(callback).Run(
  171. fast_pair_decryption::ParseDecryptedPasskey(key, bytes));
  172. }
  173. void CopyFieldBytes(
  174. const std::vector<uint8_t>& service_data,
  175. base::flat_map<size_t, std::pair<size_t, size_t>>& field_indices,
  176. size_t key,
  177. std::vector<uint8_t>* out) {
  178. DCHECK(field_indices.contains(key));
  179. auto indices = field_indices[key];
  180. for (size_t i = indices.first; i < indices.second; i++) {
  181. out->push_back(service_data[i]);
  182. }
  183. }
  184. void FastPairDataParser::ParseNotDiscoverableAdvertisement(
  185. const std::vector<uint8_t>& service_data,
  186. const std::string& address,
  187. ParseNotDiscoverableAdvertisementCallback callback) {
  188. if (service_data.empty() ||
  189. fast_pair_decoder::GetVersion(&service_data) != 0) {
  190. std::move(callback).Run(absl::nullopt);
  191. return;
  192. }
  193. base::flat_map<size_t, std::pair<size_t, size_t>> field_indices;
  194. size_t headerIndex = kHeaderIndex + kHeaderLength +
  195. fast_pair_decoder::GetIdLength(&service_data);
  196. while (headerIndex < service_data.size()) {
  197. size_t type = service_data[headerIndex] & kFieldTypeBitmask;
  198. size_t length =
  199. (service_data[headerIndex] & kFieldLengthBitmask) >> kFieldLengthOffset;
  200. size_t index = headerIndex + kHeaderLength;
  201. size_t end = index + length;
  202. if (end <= service_data.size()) {
  203. field_indices[type] = std::make_pair(index, end);
  204. }
  205. headerIndex = end;
  206. }
  207. std::vector<uint8_t> account_key_filter_bytes;
  208. bool show_ui = false;
  209. if (field_indices.contains(kFieldTypeAccountKeyFilter)) {
  210. CopyFieldBytes(service_data, field_indices, kFieldTypeAccountKeyFilter,
  211. &account_key_filter_bytes);
  212. show_ui = true;
  213. } else if (field_indices.contains(kFieldTypeAccountKeyFilterNoNotification)) {
  214. CopyFieldBytes(service_data, field_indices,
  215. kFieldTypeAccountKeyFilterNoNotification,
  216. &account_key_filter_bytes);
  217. show_ui = false;
  218. }
  219. std::vector<uint8_t> salt_bytes;
  220. if (field_indices.contains(kFieldTypeAccountKeyFilterSalt)) {
  221. CopyFieldBytes(service_data, field_indices, kFieldTypeAccountKeyFilterSalt,
  222. &salt_bytes);
  223. }
  224. std::vector<uint8_t> battery_bytes;
  225. bool show_ui_for_battery = false;
  226. if (field_indices.contains(kFieldTypeBattery)) {
  227. CopyFieldBytes(service_data, field_indices, kFieldTypeBattery,
  228. &battery_bytes);
  229. show_ui_for_battery = true;
  230. } else if (field_indices.contains(kFieldTypeBatteryNoNotification)) {
  231. CopyFieldBytes(service_data, field_indices, kFieldTypeBatteryNoNotification,
  232. &battery_bytes);
  233. show_ui_for_battery = false;
  234. }
  235. if (account_key_filter_bytes.empty()) {
  236. std::move(callback).Run(absl::nullopt);
  237. return;
  238. }
  239. if (salt_bytes.size() > 1) {
  240. QP_LOG(WARNING) << "Parsed a salt field larger than one byte: "
  241. << salt_bytes.size();
  242. std::move(callback).Run(absl::nullopt);
  243. return;
  244. }
  245. if (salt_bytes.empty()) {
  246. QP_LOG(INFO)
  247. << __func__
  248. << ": missing salt field from device. Using device address instead. ";
  249. std::array<uint8_t, kAddressByteSize> address_bytes;
  250. device::ParseBluetoothAddress(address, address_bytes);
  251. salt_bytes = std::vector(address_bytes.begin(), address_bytes.end());
  252. }
  253. std::move(callback).Run(NotDiscoverableAdvertisement(
  254. std::move(account_key_filter_bytes), show_ui, std::move(salt_bytes),
  255. BatteryNotification::FromBytes(battery_bytes, show_ui_for_battery)));
  256. }
  257. // https://developers.google.com/nearby/fast-pair/spec#MessageStream
  258. void FastPairDataParser::ParseMessageStreamMessages(
  259. const std::vector<uint8_t>& message_bytes,
  260. ParseMessageStreamMessagesCallback callback) {
  261. std::vector<mojom::MessageStreamMessagePtr> parsed_messages;
  262. // The minimum mojom::MessageStreamMessage size is four bytes based on the
  263. // Fast Pair message stream format found here:
  264. // https://developers.google.com/nearby/fast-pair/spec#MessageStream
  265. if (message_bytes.size() < kMinMessageByteCount) {
  266. QP_LOG(WARNING) << __func__
  267. << ": Not enough bytes to parse a MessageStreamMessage. "
  268. "Needed 4, received "
  269. << message_bytes.size() << ".";
  270. std::move(callback).Run(std::move(parsed_messages));
  271. return;
  272. }
  273. base::circular_deque<uint8_t> remaining_bytes(message_bytes.begin(),
  274. message_bytes.end());
  275. while (remaining_bytes.size() >= kMinMessageByteCount) {
  276. uint8_t message_group_byte = remaining_bytes.front();
  277. remaining_bytes.pop_front();
  278. absl::optional<mojom::MessageGroup> message_group =
  279. MessageGroupFromByte(message_group_byte);
  280. if (!message_group.has_value()) {
  281. QP_LOG(WARNING) << __func__ << ": Unknown message group. Received 0x"
  282. << std::hex << message_group_byte << ".";
  283. break;
  284. }
  285. uint8_t message_code = remaining_bytes.front();
  286. remaining_bytes.pop_front();
  287. uint16_t byte_to_shift{remaining_bytes.front()};
  288. remaining_bytes.pop_front();
  289. uint16_t additional_data_length{remaining_bytes.front()};
  290. remaining_bytes.pop_front();
  291. byte_to_shift = byte_to_shift << 8;
  292. additional_data_length |= byte_to_shift;
  293. // Only initialize the additional data with the bytes if there is a size for
  294. // it in the message bytes. Additional data starts at the fourth byte of
  295. // the message. We want to verify if additional data exists in the
  296. // message bytes, if not, the data is not trusted and we will return a
  297. // null message.
  298. std::vector<uint8_t> additional_data(additional_data_length, 0);
  299. if (remaining_bytes.size() < additional_data_length)
  300. break;
  301. for (int i = 0; i < additional_data_length; ++i) {
  302. additional_data[i] = remaining_bytes.front();
  303. remaining_bytes.pop_front();
  304. }
  305. mojom::MessageStreamMessagePtr message = ParseMessageStreamMessage(
  306. message_group.value(), message_code,
  307. base::span<uint8_t>(additional_data.begin(), additional_data.end()));
  308. // Only add a completely parsed message to the return vector.
  309. if (message)
  310. parsed_messages.push_back(std::move(message));
  311. }
  312. if (!remaining_bytes.empty()) {
  313. QP_LOG(WARNING) << __func__ << ": " << remaining_bytes.size()
  314. << " remaining bytes not parsed.";
  315. }
  316. std::move(callback).Run(std::move(parsed_messages));
  317. }
  318. // https://developers.google.com/nearby/fast-pair/spec#MessageStream
  319. mojom::MessageStreamMessagePtr FastPairDataParser::ParseMessageStreamMessage(
  320. mojom::MessageGroup message_group,
  321. uint8_t message_code,
  322. const base::span<uint8_t>& additional_data) {
  323. switch (message_group) {
  324. case mojom::MessageGroup::kBluetoothEvent:
  325. if (!additional_data.empty())
  326. return nullptr;
  327. return ParseBluetoothEvent(message_code);
  328. case mojom::MessageGroup::kCompanionAppEvent:
  329. if (!additional_data.empty())
  330. return nullptr;
  331. return ParseCompanionAppEvent(message_code);
  332. case mojom::MessageGroup::kDeviceInformationEvent:
  333. return ParseDeviceInformationEvent(message_code,
  334. std::move(additional_data));
  335. case mojom::MessageGroup::kDeviceActionEvent:
  336. return ParseDeviceActionEvent(message_code, std::move(additional_data));
  337. case mojom::MessageGroup::kAcknowledgementEvent:
  338. return ParseAcknowledgementEvent(message_code,
  339. std::move(additional_data));
  340. }
  341. }
  342. // https://developers.google.com/nearby/fast-pair/spec#SilenceMode
  343. mojom::MessageStreamMessagePtr FastPairDataParser::ParseBluetoothEvent(
  344. uint8_t message_code) {
  345. if (message_code == kEnableSilenceModeCode) {
  346. return mojom::MessageStreamMessage::NewEnableSilenceMode(true);
  347. }
  348. if (message_code == kDisableSilenceModeCode) {
  349. return mojom::MessageStreamMessage::NewEnableSilenceMode(false);
  350. }
  351. QP_LOG(WARNING) << __func__ << ": Unknown message code. Received 0x"
  352. << std::hex << message_code << ".";
  353. return nullptr;
  354. }
  355. // https://developers.google.com/nearby/fast-pair/spec#companion_app_events
  356. mojom::MessageStreamMessagePtr FastPairDataParser::ParseCompanionAppEvent(
  357. uint8_t message_code) {
  358. if (message_code == kLogBufferFullCode) {
  359. return mojom::MessageStreamMessage::NewCompanionAppLogBufferFull(true);
  360. }
  361. QP_LOG(WARNING) << __func__ << ": Unknown message code. Received 0x"
  362. << std::hex << message_code << ".";
  363. return nullptr;
  364. }
  365. // https://developers.google.com/nearby/fast-pair/spec#MessageStreamDeviceInformation
  366. mojom::MessageStreamMessagePtr FastPairDataParser::ParseDeviceInformationEvent(
  367. uint8_t message_code,
  368. const base::span<uint8_t>& additional_data) {
  369. if (message_code == kModelIdCode) {
  370. // Missing additional data containing model id value, since valid model id
  371. // will be length 3.
  372. if (additional_data.size() != 3) {
  373. QP_LOG(WARNING) << __func__
  374. << ": Invalid number of additional data bytes to parse "
  375. "model id Needed 3, received "
  376. << additional_data.size();
  377. return nullptr;
  378. }
  379. return mojom::MessageStreamMessage::NewModelId(
  380. base::HexEncode(additional_data));
  381. }
  382. if (message_code == kBleAddressUpdatedCode) {
  383. // Missing additional data containing ble address updated value, which will
  384. // be 6 bytes to be valid
  385. if (additional_data.size() != 6) {
  386. QP_LOG(WARNING)
  387. << __func__
  388. << ": Invalid number of additional data bytes to parse BLE "
  389. "address. Needed 6, received "
  390. << additional_data.size();
  391. return nullptr;
  392. }
  393. std::array<uint8_t, 6> address_bytes;
  394. std::copy_n(additional_data.begin(), 6, address_bytes.begin());
  395. return mojom::MessageStreamMessage::NewBleAddressUpdate(
  396. device::CanonicalizeBluetoothAddress(address_bytes));
  397. }
  398. if (message_code == kBatteryUpdatedCode) {
  399. // Missing additional data containing battery updated value, since valid
  400. // battery update size will be length 3
  401. if (additional_data.size() != 3) {
  402. QP_LOG(WARNING) << __func__
  403. << ": Invalid number of additional data bytes to parse "
  404. "battery update. Needed 3, received "
  405. << additional_data.size();
  406. return nullptr;
  407. }
  408. // The additional data contains information about the battery components
  409. // about whether or not it is charging, and the battery percent. If
  410. // the percent is invalid (outside of range 0-100), then it is set to -1.
  411. mojom::BatteryUpdatePtr battery_update = mojom::BatteryUpdate::New();
  412. battery_update->left_bud_info = CreateBatteryInfo(additional_data[0]);
  413. battery_update->right_bud_info = CreateBatteryInfo(additional_data[1]);
  414. battery_update->case_info = CreateBatteryInfo(additional_data[2]);
  415. return mojom::MessageStreamMessage::NewBatteryUpdate(
  416. std::move(battery_update));
  417. }
  418. if (message_code == kRemainingBatteryTimeCode) {
  419. // Additional data contains the remaining battery time and will be 1 or 2
  420. // bytes.
  421. if (additional_data.size() != 1 && additional_data.size() != 2) {
  422. QP_LOG(WARNING) << __func__
  423. << ": Invalid number of additional data bytes to parse "
  424. "remaining battery time. Needed 1 or 2, received "
  425. << additional_data.size();
  426. return nullptr;
  427. }
  428. // If we have a single byte of remaining battery time, we can just set it
  429. // as the remaining battery time.
  430. if (additional_data.size() == 1) {
  431. uint16_t remaining_battery_time{additional_data[0]};
  432. return mojom::MessageStreamMessage::NewRemainingBatteryTime(
  433. remaining_battery_time);
  434. }
  435. // If we have two bytes of remaining battery time, then we need to combine
  436. // the bytes together to create a uint16_t
  437. uint16_t remaining_battery_time{additional_data[1]};
  438. uint16_t byte_to_shift{additional_data[0]};
  439. byte_to_shift = byte_to_shift << 8;
  440. remaining_battery_time |= byte_to_shift;
  441. return mojom::MessageStreamMessage::NewRemainingBatteryTime(
  442. remaining_battery_time);
  443. }
  444. if (message_code == kActiveComponentsResponseCode) {
  445. // Additional data contains the active components response, which for a
  446. // single component, will be 0 or 1 depending on whether or not it is
  447. // available, and for a device with multiple components, each bit in the
  448. // additional data represents whether that component is active. It is the
  449. // responsibility for consumers of the MessageStream to determine what
  450. // the meaning is, since a value of 0x01 can mean either a single component
  451. // is active or only the right bud is active, depending on the type of
  452. // device, which the MessageStream does not contain information
  453. // differentiating the two. See
  454. // https://developers.google.com/nearby/fast-pair/spec#MessageStreamActiveComponents
  455. if (additional_data.size() != 1) {
  456. QP_LOG(WARNING) << __func__
  457. << ": Invalid number of additional data bytes to parse "
  458. "active components. Needed 1, received "
  459. << additional_data.size();
  460. return nullptr;
  461. }
  462. return mojom::MessageStreamMessage::NewActiveComponentsByte(
  463. additional_data[0]);
  464. }
  465. if (message_code == kPlatformTypeCode) {
  466. // The additional data contains information about the Platform Type. For
  467. // now, the only platform that is supported is Android, but it may be
  468. // expanded in the future. The additional data will be 2 bytes to be valid:
  469. // the first byte to describe the platform, and the second byte is
  470. // customized per platform. For now, it describes the SDK version for
  471. // android, but when other platforms are supported, this will need to be
  472. // expanded on. See
  473. // https://developers.google.com/nearby/fast-pair/spec#PlatformType
  474. if (additional_data.size() != 2) {
  475. QP_LOG(WARNING) << __func__
  476. << ": Not enough additional data bytes to parse platform "
  477. "type. Needed 2, received "
  478. << additional_data.size();
  479. return nullptr;
  480. }
  481. if (additional_data[0] != kAndroidPlatform) {
  482. QP_LOG(WARNING)
  483. << __func__
  484. << ": Unknown platform type for MessageStreamMessage. Received 0x"
  485. << std::hex << additional_data[0];
  486. return nullptr;
  487. }
  488. int sdk_version = additional_data[1];
  489. return mojom::MessageStreamMessage::NewSdkVersion(sdk_version);
  490. }
  491. QP_LOG(WARNING) << __func__ << ": Unknown message code. Received 0x"
  492. << std::hex << message_code << ".";
  493. return nullptr;
  494. }
  495. // https://developers.google.com/nearby/fast-pair/spec#DeviceAction
  496. mojom::MessageStreamMessagePtr FastPairDataParser::ParseDeviceActionEvent(
  497. uint8_t message_code,
  498. const base::span<uint8_t>& additional_data) {
  499. // There is only one device action supported for Fast Pair: ringing a device.
  500. // This can be updated when there are more device actions supported.
  501. // Ringing a device information is contained in the first byte in additional
  502. // information, and it can dictate either a single component or devices with
  503. // multiple components. The message stream does not contain information about
  504. // if the device is a single component or multiple components, so it is the
  505. // responsibility of the mojom::MessageStreamMessage consumer to determine
  506. // what the ring value means. See
  507. // https://developers.google.com/nearby/fast-pair/spec#ringing_a_device
  508. if (additional_data.size() != 1 && additional_data.size() != 2) {
  509. QP_LOG(WARNING) << __func__
  510. << ": Invalid number of additional data bytes to parse "
  511. "device action. Needed 1 or 2, received "
  512. << additional_data.size();
  513. return nullptr;
  514. }
  515. if (message_code != kRingCode) {
  516. QP_LOG(WARNING)
  517. << __func__
  518. << ": Unknown message code to parse DeviceAction code. Received 0x"
  519. << std::hex << message_code << ".";
  520. return nullptr;
  521. }
  522. mojom::RingDevicePtr ring_device = mojom::RingDevice::New();
  523. ring_device->ring_device_byte = additional_data[0];
  524. // Optional timeout field for ringing. Set to -1 if doesn't exist.
  525. if (additional_data.size() == 2)
  526. ring_device->timeout_in_seconds = static_cast<int>(additional_data[1]);
  527. else
  528. ring_device->timeout_in_seconds = -1;
  529. return mojom::MessageStreamMessage::NewRingDeviceEvent(
  530. std::move(ring_device));
  531. }
  532. // https://developers.google.com/nearby/fast-pair/spec#MessageStreamAcknowledgements
  533. mojom::MessageStreamMessagePtr FastPairDataParser::ParseAcknowledgementEvent(
  534. uint8_t message_code,
  535. const base::span<uint8_t>& additional_data) {
  536. if (message_code != kAckCode && message_code != kNakCode) {
  537. QP_LOG(WARNING)
  538. << __func__
  539. << ": Unknown message code to parse Acknowledgement code. Received 0x"
  540. << std::hex << message_code << ".";
  541. return nullptr;
  542. }
  543. if (message_code == kAckCode) {
  544. // The length two of additional data contains the action message group and
  545. // code.
  546. if (additional_data.size() != 2) {
  547. QP_LOG(WARNING)
  548. << __func__
  549. << ": Invalid number of bytes in additional data to parse "
  550. "Acknowledgement. Needed 2, received "
  551. << additional_data.size();
  552. return nullptr;
  553. }
  554. // Get the message group pertaining to the action being acknowledged.
  555. absl::optional<mojom::MessageGroup> message_group =
  556. MessageGroupFromByte(additional_data[0]);
  557. if (!message_group.has_value()) {
  558. QP_LOG(WARNING) << __func__ << ": Unknown message group. Received 0x"
  559. << std::hex << additional_data[0] << ".";
  560. return nullptr;
  561. }
  562. mojom::AcknowledgementMessagePtr ack = mojom::AcknowledgementMessage::New();
  563. ack->action_message_code = additional_data[1];
  564. ack->acknowledgement = mojom::Acknowledgement::kAck;
  565. ack->action_message_group = message_group.value();
  566. return mojom::MessageStreamMessage::NewAcknowledgement(std::move(ack));
  567. }
  568. if (message_code == kNakCode) {
  569. // The length three of additional data contains the action message group and
  570. // code.
  571. if (additional_data.size() != 3) {
  572. QP_LOG(WARNING)
  573. << __func__
  574. << ": Invalid number of bytes in additional data to parse "
  575. "Acknowledgement. Needed 3, received "
  576. << additional_data.size();
  577. return nullptr;
  578. }
  579. absl::optional<mojom::Acknowledgement> nak_reason =
  580. NakReasonFromByte(additional_data[0]);
  581. if (!nak_reason) {
  582. QP_LOG(WARNING)
  583. << __func__
  584. << ": Unknown nak reason to parse Acknowledgement. Received 0x"
  585. << std::hex << additional_data[0];
  586. return nullptr;
  587. }
  588. // Get the message group pertaining to the action being nacknowledged.
  589. absl::optional<mojom::MessageGroup> message_group =
  590. MessageGroupFromByte(additional_data[1]);
  591. if (!message_group.has_value()) {
  592. QP_LOG(WARNING) << __func__ << ": Unknown message group. Received 0x"
  593. << std::hex << additional_data[1] << ".";
  594. return nullptr;
  595. }
  596. mojom::AcknowledgementMessagePtr ack = mojom::AcknowledgementMessage::New();
  597. ack->action_message_code = additional_data[2];
  598. ack->action_message_group = message_group.value();
  599. ack->acknowledgement = nak_reason.value();
  600. return mojom::MessageStreamMessage::NewAcknowledgement(std::move(ack));
  601. }
  602. return nullptr;
  603. }
  604. } // namespace quick_pair
  605. } // namespace ash