123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696 |
- // Copyright 2021 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "ash/services/quick_pair/fast_pair_data_parser.h"
- #include <algorithm>
- #include <cstdint>
- #include <vector>
- #include "ash/quick_pair/common/fast_pair/fast_pair_decoder.h"
- #include "ash/quick_pair/common/logging.h"
- #include "ash/services/quick_pair/fast_pair_decryption.h"
- #include "ash/services/quick_pair/public/cpp/battery_notification.h"
- #include "ash/services/quick_pair/public/cpp/not_discoverable_advertisement.h"
- #include "ash/services/quick_pair/public/mojom/fast_pair_data_parser.mojom.h"
- #include "base/base64.h"
- #include "base/containers/circular_deque.h"
- #include "base/containers/flat_map.h"
- #include "base/strings/string_number_conversions.h"
- #include "crypto/openssl_util.h"
- #include "device/bluetooth/public/cpp/bluetooth_address.h"
- #include "mojo/public/cpp/bindings/pending_receiver.h"
- #include "third_party/abseil-cpp/absl/types/optional.h"
- namespace {
- constexpr int kHeaderIndex = 0;
- constexpr int kFieldTypeBitmask = 0b00001111;
- constexpr int kFieldLengthBitmask = 0b11110000;
- constexpr int kHeaderLength = 1;
- constexpr int kFieldLengthOffset = 4;
- constexpr int kFieldTypeAccountKeyFilter = 0;
- constexpr int kFieldTypeAccountKeyFilterSalt = 1;
- constexpr int kFieldTypeAccountKeyFilterNoNotification = 2;
- constexpr int kFieldTypeBattery = 3;
- constexpr int kFieldTypeBatteryNoNotification = 4;
- constexpr int kBluetoothEvent = 0x01;
- constexpr int kEnableSilenceModeCode = 0x01;
- constexpr int kDisableSilenceModeCode = 0x02;
- constexpr int kCompanionAppEvent = 0x02;
- constexpr int kLogBufferFullCode = 0x01;
- constexpr int kDeviceInformationEvent = 0x03;
- constexpr int kModelIdCode = 0x01;
- constexpr int kBleAddressUpdatedCode = 0x02;
- constexpr int kBatteryUpdatedCode = 0x03;
- constexpr int kRemainingBatteryTimeCode = 0x04;
- constexpr int kActiveComponentsResponseCode = 0x06;
- constexpr int kPlatformTypeCode = 0x08;
- constexpr int kAndroidPlatform = 0x01;
- constexpr int kDeviceActionEvent = 0x04;
- constexpr int kRingCode = 0x01;
- constexpr int kAcknowledgementEvent = 0xFF;
- constexpr int kAckCode = 0x01;
- constexpr int kNakCode = 0x02;
- constexpr int kNotSupportedNak = 0x00;
- constexpr int kDeviceBusyNak = 0x01;
- constexpr int kNotAllowedDueToCurrentStateNak = 0x02;
- constexpr uint8_t kBatteryChargeBitmask = 0b10000000;
- constexpr uint8_t kBatteryPercentBitmask = 0b01111111;
- constexpr int kMinMessageByteCount = 4;
- constexpr int kAddressByteSize = 6;
- bool ValidateInputSizes(const std::vector<uint8_t>& aes_key_bytes,
- const std::vector<uint8_t>& encrypted_bytes) {
- if (aes_key_bytes.size() != kAesBlockByteSize) {
- QP_LOG(WARNING) << __func__
- << ": AES key should have size = " << kAesBlockByteSize
- << ", actual = " << aes_key_bytes.size();
- return false;
- }
- if (encrypted_bytes.size() != kEncryptedDataByteSize) {
- QP_LOG(WARNING) << __func__ << ": Encrypted bytes should have size = "
- << kEncryptedDataByteSize
- << ", actual = " << encrypted_bytes.size();
- return false;
- }
- return true;
- }
- void ConvertVectorsToArrays(
- const std::vector<uint8_t>& aes_key_bytes,
- const std::vector<uint8_t>& encrypted_bytes,
- std::array<uint8_t, kAesBlockByteSize>& out_aes_key_bytes,
- std::array<uint8_t, kEncryptedDataByteSize>& out_encrypted_bytes) {
- std::copy(aes_key_bytes.begin(), aes_key_bytes.end(),
- out_aes_key_bytes.begin());
- std::copy(encrypted_bytes.begin(), encrypted_bytes.end(),
- out_encrypted_bytes.begin());
- }
- int GetBatteryPercentange(uint8_t battery_byte) {
- int battery_percent = battery_byte & kBatteryPercentBitmask;
- if (battery_percent < 0 || battery_percent > 100)
- return -1;
- return battery_percent;
- }
- bool IsBatteryCharging(uint8_t battery_byte) {
- return (battery_byte & kBatteryChargeBitmask) != 0;
- }
- } // namespace
- namespace ash {
- namespace quick_pair {
- absl::optional<mojom::MessageGroup> MessageGroupFromByte(
- uint8_t message_group) {
- switch (message_group) {
- case kBluetoothEvent:
- return mojom::MessageGroup::kBluetoothEvent;
- case kCompanionAppEvent:
- return mojom::MessageGroup::kCompanionAppEvent;
- case kDeviceInformationEvent:
- return mojom::MessageGroup::kDeviceInformationEvent;
- case kDeviceActionEvent:
- return mojom::MessageGroup::kDeviceActionEvent;
- case kAcknowledgementEvent:
- return mojom::MessageGroup::kAcknowledgementEvent;
- default:
- return absl::nullopt;
- }
- }
- absl::optional<mojom::Acknowledgement> NakReasonFromByte(uint8_t nak_reason) {
- switch (nak_reason) {
- case kNotSupportedNak:
- return mojom::Acknowledgement::kNotSupportedNak;
- case kDeviceBusyNak:
- return mojom::Acknowledgement::kDeviceBusyNak;
- case kNotAllowedDueToCurrentStateNak:
- return mojom::Acknowledgement::kNotAllowedDueToCurrentStateNak;
- default:
- return absl::nullopt;
- }
- }
- mojom::BatteryInfoPtr CreateBatteryInfo(uint8_t battery_byte) {
- mojom::BatteryInfoPtr battery_info = mojom::BatteryInfo::New();
- battery_info->is_charging = IsBatteryCharging(battery_byte);
- battery_info->percentage = GetBatteryPercentange(battery_byte);
- return battery_info;
- }
- FastPairDataParser::FastPairDataParser(
- mojo::PendingReceiver<mojom::FastPairDataParser> receiver)
- : receiver_(this, std::move(receiver)) {
- crypto::EnsureOpenSSLInit();
- }
- FastPairDataParser::~FastPairDataParser() = default;
- void FastPairDataParser::GetHexModelIdFromServiceData(
- const std::vector<uint8_t>& service_data,
- GetHexModelIdFromServiceDataCallback callback) {
- std::move(callback).Run(
- fast_pair_decoder::HasModelId(&service_data)
- ? fast_pair_decoder::GetHexModelIdFromServiceData(&service_data)
- : absl::nullopt);
- }
- void FastPairDataParser::ParseDecryptedResponse(
- const std::vector<uint8_t>& aes_key_bytes,
- const std::vector<uint8_t>& encrypted_response_bytes,
- ParseDecryptedResponseCallback callback) {
- if (!ValidateInputSizes(aes_key_bytes, encrypted_response_bytes)) {
- std::move(callback).Run(absl::nullopt);
- return;
- }
- std::array<uint8_t, kAesBlockByteSize> key;
- std::array<uint8_t, kEncryptedDataByteSize> bytes;
- ConvertVectorsToArrays(aes_key_bytes, encrypted_response_bytes, key, bytes);
- std::move(callback).Run(
- fast_pair_decryption::ParseDecryptedResponse(key, bytes));
- }
- void FastPairDataParser::ParseDecryptedPasskey(
- const std::vector<uint8_t>& aes_key_bytes,
- const std::vector<uint8_t>& encrypted_passkey_bytes,
- ParseDecryptedPasskeyCallback callback) {
- if (!ValidateInputSizes(aes_key_bytes, encrypted_passkey_bytes)) {
- std::move(callback).Run(absl::nullopt);
- return;
- }
- std::array<uint8_t, kAesBlockByteSize> key;
- std::array<uint8_t, kEncryptedDataByteSize> bytes;
- ConvertVectorsToArrays(aes_key_bytes, encrypted_passkey_bytes, key, bytes);
- std::move(callback).Run(
- fast_pair_decryption::ParseDecryptedPasskey(key, bytes));
- }
- void CopyFieldBytes(
- const std::vector<uint8_t>& service_data,
- base::flat_map<size_t, std::pair<size_t, size_t>>& field_indices,
- size_t key,
- std::vector<uint8_t>* out) {
- DCHECK(field_indices.contains(key));
- auto indices = field_indices[key];
- for (size_t i = indices.first; i < indices.second; i++) {
- out->push_back(service_data[i]);
- }
- }
- void FastPairDataParser::ParseNotDiscoverableAdvertisement(
- const std::vector<uint8_t>& service_data,
- const std::string& address,
- ParseNotDiscoverableAdvertisementCallback callback) {
- if (service_data.empty() ||
- fast_pair_decoder::GetVersion(&service_data) != 0) {
- std::move(callback).Run(absl::nullopt);
- return;
- }
- base::flat_map<size_t, std::pair<size_t, size_t>> field_indices;
- size_t headerIndex = kHeaderIndex + kHeaderLength +
- fast_pair_decoder::GetIdLength(&service_data);
- while (headerIndex < service_data.size()) {
- size_t type = service_data[headerIndex] & kFieldTypeBitmask;
- size_t length =
- (service_data[headerIndex] & kFieldLengthBitmask) >> kFieldLengthOffset;
- size_t index = headerIndex + kHeaderLength;
- size_t end = index + length;
- if (end <= service_data.size()) {
- field_indices[type] = std::make_pair(index, end);
- }
- headerIndex = end;
- }
- std::vector<uint8_t> account_key_filter_bytes;
- bool show_ui = false;
- if (field_indices.contains(kFieldTypeAccountKeyFilter)) {
- CopyFieldBytes(service_data, field_indices, kFieldTypeAccountKeyFilter,
- &account_key_filter_bytes);
- show_ui = true;
- } else if (field_indices.contains(kFieldTypeAccountKeyFilterNoNotification)) {
- CopyFieldBytes(service_data, field_indices,
- kFieldTypeAccountKeyFilterNoNotification,
- &account_key_filter_bytes);
- show_ui = false;
- }
- std::vector<uint8_t> salt_bytes;
- if (field_indices.contains(kFieldTypeAccountKeyFilterSalt)) {
- CopyFieldBytes(service_data, field_indices, kFieldTypeAccountKeyFilterSalt,
- &salt_bytes);
- }
- std::vector<uint8_t> battery_bytes;
- bool show_ui_for_battery = false;
- if (field_indices.contains(kFieldTypeBattery)) {
- CopyFieldBytes(service_data, field_indices, kFieldTypeBattery,
- &battery_bytes);
- show_ui_for_battery = true;
- } else if (field_indices.contains(kFieldTypeBatteryNoNotification)) {
- CopyFieldBytes(service_data, field_indices, kFieldTypeBatteryNoNotification,
- &battery_bytes);
- show_ui_for_battery = false;
- }
- if (account_key_filter_bytes.empty()) {
- std::move(callback).Run(absl::nullopt);
- return;
- }
- if (salt_bytes.size() > 1) {
- QP_LOG(WARNING) << "Parsed a salt field larger than one byte: "
- << salt_bytes.size();
- std::move(callback).Run(absl::nullopt);
- return;
- }
- if (salt_bytes.empty()) {
- QP_LOG(INFO)
- << __func__
- << ": missing salt field from device. Using device address instead. ";
- std::array<uint8_t, kAddressByteSize> address_bytes;
- device::ParseBluetoothAddress(address, address_bytes);
- salt_bytes = std::vector(address_bytes.begin(), address_bytes.end());
- }
- std::move(callback).Run(NotDiscoverableAdvertisement(
- std::move(account_key_filter_bytes), show_ui, std::move(salt_bytes),
- BatteryNotification::FromBytes(battery_bytes, show_ui_for_battery)));
- }
- // https://developers.google.com/nearby/fast-pair/spec#MessageStream
- void FastPairDataParser::ParseMessageStreamMessages(
- const std::vector<uint8_t>& message_bytes,
- ParseMessageStreamMessagesCallback callback) {
- std::vector<mojom::MessageStreamMessagePtr> parsed_messages;
- // The minimum mojom::MessageStreamMessage size is four bytes based on the
- // Fast Pair message stream format found here:
- // https://developers.google.com/nearby/fast-pair/spec#MessageStream
- if (message_bytes.size() < kMinMessageByteCount) {
- QP_LOG(WARNING) << __func__
- << ": Not enough bytes to parse a MessageStreamMessage. "
- "Needed 4, received "
- << message_bytes.size() << ".";
- std::move(callback).Run(std::move(parsed_messages));
- return;
- }
- base::circular_deque<uint8_t> remaining_bytes(message_bytes.begin(),
- message_bytes.end());
- while (remaining_bytes.size() >= kMinMessageByteCount) {
- uint8_t message_group_byte = remaining_bytes.front();
- remaining_bytes.pop_front();
- absl::optional<mojom::MessageGroup> message_group =
- MessageGroupFromByte(message_group_byte);
- if (!message_group.has_value()) {
- QP_LOG(WARNING) << __func__ << ": Unknown message group. Received 0x"
- << std::hex << message_group_byte << ".";
- break;
- }
- uint8_t message_code = remaining_bytes.front();
- remaining_bytes.pop_front();
- uint16_t byte_to_shift{remaining_bytes.front()};
- remaining_bytes.pop_front();
- uint16_t additional_data_length{remaining_bytes.front()};
- remaining_bytes.pop_front();
- byte_to_shift = byte_to_shift << 8;
- additional_data_length |= byte_to_shift;
- // Only initialize the additional data with the bytes if there is a size for
- // it in the message bytes. Additional data starts at the fourth byte of
- // the message. We want to verify if additional data exists in the
- // message bytes, if not, the data is not trusted and we will return a
- // null message.
- std::vector<uint8_t> additional_data(additional_data_length, 0);
- if (remaining_bytes.size() < additional_data_length)
- break;
- for (int i = 0; i < additional_data_length; ++i) {
- additional_data[i] = remaining_bytes.front();
- remaining_bytes.pop_front();
- }
- mojom::MessageStreamMessagePtr message = ParseMessageStreamMessage(
- message_group.value(), message_code,
- base::span<uint8_t>(additional_data.begin(), additional_data.end()));
- // Only add a completely parsed message to the return vector.
- if (message)
- parsed_messages.push_back(std::move(message));
- }
- if (!remaining_bytes.empty()) {
- QP_LOG(WARNING) << __func__ << ": " << remaining_bytes.size()
- << " remaining bytes not parsed.";
- }
- std::move(callback).Run(std::move(parsed_messages));
- }
- // https://developers.google.com/nearby/fast-pair/spec#MessageStream
- mojom::MessageStreamMessagePtr FastPairDataParser::ParseMessageStreamMessage(
- mojom::MessageGroup message_group,
- uint8_t message_code,
- const base::span<uint8_t>& additional_data) {
- switch (message_group) {
- case mojom::MessageGroup::kBluetoothEvent:
- if (!additional_data.empty())
- return nullptr;
- return ParseBluetoothEvent(message_code);
- case mojom::MessageGroup::kCompanionAppEvent:
- if (!additional_data.empty())
- return nullptr;
- return ParseCompanionAppEvent(message_code);
- case mojom::MessageGroup::kDeviceInformationEvent:
- return ParseDeviceInformationEvent(message_code,
- std::move(additional_data));
- case mojom::MessageGroup::kDeviceActionEvent:
- return ParseDeviceActionEvent(message_code, std::move(additional_data));
- case mojom::MessageGroup::kAcknowledgementEvent:
- return ParseAcknowledgementEvent(message_code,
- std::move(additional_data));
- }
- }
- // https://developers.google.com/nearby/fast-pair/spec#SilenceMode
- mojom::MessageStreamMessagePtr FastPairDataParser::ParseBluetoothEvent(
- uint8_t message_code) {
- if (message_code == kEnableSilenceModeCode) {
- return mojom::MessageStreamMessage::NewEnableSilenceMode(true);
- }
- if (message_code == kDisableSilenceModeCode) {
- return mojom::MessageStreamMessage::NewEnableSilenceMode(false);
- }
- QP_LOG(WARNING) << __func__ << ": Unknown message code. Received 0x"
- << std::hex << message_code << ".";
- return nullptr;
- }
- // https://developers.google.com/nearby/fast-pair/spec#companion_app_events
- mojom::MessageStreamMessagePtr FastPairDataParser::ParseCompanionAppEvent(
- uint8_t message_code) {
- if (message_code == kLogBufferFullCode) {
- return mojom::MessageStreamMessage::NewCompanionAppLogBufferFull(true);
- }
- QP_LOG(WARNING) << __func__ << ": Unknown message code. Received 0x"
- << std::hex << message_code << ".";
- return nullptr;
- }
- // https://developers.google.com/nearby/fast-pair/spec#MessageStreamDeviceInformation
- mojom::MessageStreamMessagePtr FastPairDataParser::ParseDeviceInformationEvent(
- uint8_t message_code,
- const base::span<uint8_t>& additional_data) {
- if (message_code == kModelIdCode) {
- // Missing additional data containing model id value, since valid model id
- // will be length 3.
- if (additional_data.size() != 3) {
- QP_LOG(WARNING) << __func__
- << ": Invalid number of additional data bytes to parse "
- "model id Needed 3, received "
- << additional_data.size();
- return nullptr;
- }
- return mojom::MessageStreamMessage::NewModelId(
- base::HexEncode(additional_data));
- }
- if (message_code == kBleAddressUpdatedCode) {
- // Missing additional data containing ble address updated value, which will
- // be 6 bytes to be valid
- if (additional_data.size() != 6) {
- QP_LOG(WARNING)
- << __func__
- << ": Invalid number of additional data bytes to parse BLE "
- "address. Needed 6, received "
- << additional_data.size();
- return nullptr;
- }
- std::array<uint8_t, 6> address_bytes;
- std::copy_n(additional_data.begin(), 6, address_bytes.begin());
- return mojom::MessageStreamMessage::NewBleAddressUpdate(
- device::CanonicalizeBluetoothAddress(address_bytes));
- }
- if (message_code == kBatteryUpdatedCode) {
- // Missing additional data containing battery updated value, since valid
- // battery update size will be length 3
- if (additional_data.size() != 3) {
- QP_LOG(WARNING) << __func__
- << ": Invalid number of additional data bytes to parse "
- "battery update. Needed 3, received "
- << additional_data.size();
- return nullptr;
- }
- // The additional data contains information about the battery components
- // about whether or not it is charging, and the battery percent. If
- // the percent is invalid (outside of range 0-100), then it is set to -1.
- mojom::BatteryUpdatePtr battery_update = mojom::BatteryUpdate::New();
- battery_update->left_bud_info = CreateBatteryInfo(additional_data[0]);
- battery_update->right_bud_info = CreateBatteryInfo(additional_data[1]);
- battery_update->case_info = CreateBatteryInfo(additional_data[2]);
- return mojom::MessageStreamMessage::NewBatteryUpdate(
- std::move(battery_update));
- }
- if (message_code == kRemainingBatteryTimeCode) {
- // Additional data contains the remaining battery time and will be 1 or 2
- // bytes.
- if (additional_data.size() != 1 && additional_data.size() != 2) {
- QP_LOG(WARNING) << __func__
- << ": Invalid number of additional data bytes to parse "
- "remaining battery time. Needed 1 or 2, received "
- << additional_data.size();
- return nullptr;
- }
- // If we have a single byte of remaining battery time, we can just set it
- // as the remaining battery time.
- if (additional_data.size() == 1) {
- uint16_t remaining_battery_time{additional_data[0]};
- return mojom::MessageStreamMessage::NewRemainingBatteryTime(
- remaining_battery_time);
- }
- // If we have two bytes of remaining battery time, then we need to combine
- // the bytes together to create a uint16_t
- uint16_t remaining_battery_time{additional_data[1]};
- uint16_t byte_to_shift{additional_data[0]};
- byte_to_shift = byte_to_shift << 8;
- remaining_battery_time |= byte_to_shift;
- return mojom::MessageStreamMessage::NewRemainingBatteryTime(
- remaining_battery_time);
- }
- if (message_code == kActiveComponentsResponseCode) {
- // Additional data contains the active components response, which for a
- // single component, will be 0 or 1 depending on whether or not it is
- // available, and for a device with multiple components, each bit in the
- // additional data represents whether that component is active. It is the
- // responsibility for consumers of the MessageStream to determine what
- // the meaning is, since a value of 0x01 can mean either a single component
- // is active or only the right bud is active, depending on the type of
- // device, which the MessageStream does not contain information
- // differentiating the two. See
- // https://developers.google.com/nearby/fast-pair/spec#MessageStreamActiveComponents
- if (additional_data.size() != 1) {
- QP_LOG(WARNING) << __func__
- << ": Invalid number of additional data bytes to parse "
- "active components. Needed 1, received "
- << additional_data.size();
- return nullptr;
- }
- return mojom::MessageStreamMessage::NewActiveComponentsByte(
- additional_data[0]);
- }
- if (message_code == kPlatformTypeCode) {
- // The additional data contains information about the Platform Type. For
- // now, the only platform that is supported is Android, but it may be
- // expanded in the future. The additional data will be 2 bytes to be valid:
- // the first byte to describe the platform, and the second byte is
- // customized per platform. For now, it describes the SDK version for
- // android, but when other platforms are supported, this will need to be
- // expanded on. See
- // https://developers.google.com/nearby/fast-pair/spec#PlatformType
- if (additional_data.size() != 2) {
- QP_LOG(WARNING) << __func__
- << ": Not enough additional data bytes to parse platform "
- "type. Needed 2, received "
- << additional_data.size();
- return nullptr;
- }
- if (additional_data[0] != kAndroidPlatform) {
- QP_LOG(WARNING)
- << __func__
- << ": Unknown platform type for MessageStreamMessage. Received 0x"
- << std::hex << additional_data[0];
- return nullptr;
- }
- int sdk_version = additional_data[1];
- return mojom::MessageStreamMessage::NewSdkVersion(sdk_version);
- }
- QP_LOG(WARNING) << __func__ << ": Unknown message code. Received 0x"
- << std::hex << message_code << ".";
- return nullptr;
- }
- // https://developers.google.com/nearby/fast-pair/spec#DeviceAction
- mojom::MessageStreamMessagePtr FastPairDataParser::ParseDeviceActionEvent(
- uint8_t message_code,
- const base::span<uint8_t>& additional_data) {
- // There is only one device action supported for Fast Pair: ringing a device.
- // This can be updated when there are more device actions supported.
- // Ringing a device information is contained in the first byte in additional
- // information, and it can dictate either a single component or devices with
- // multiple components. The message stream does not contain information about
- // if the device is a single component or multiple components, so it is the
- // responsibility of the mojom::MessageStreamMessage consumer to determine
- // what the ring value means. See
- // https://developers.google.com/nearby/fast-pair/spec#ringing_a_device
- if (additional_data.size() != 1 && additional_data.size() != 2) {
- QP_LOG(WARNING) << __func__
- << ": Invalid number of additional data bytes to parse "
- "device action. Needed 1 or 2, received "
- << additional_data.size();
- return nullptr;
- }
- if (message_code != kRingCode) {
- QP_LOG(WARNING)
- << __func__
- << ": Unknown message code to parse DeviceAction code. Received 0x"
- << std::hex << message_code << ".";
- return nullptr;
- }
- mojom::RingDevicePtr ring_device = mojom::RingDevice::New();
- ring_device->ring_device_byte = additional_data[0];
- // Optional timeout field for ringing. Set to -1 if doesn't exist.
- if (additional_data.size() == 2)
- ring_device->timeout_in_seconds = static_cast<int>(additional_data[1]);
- else
- ring_device->timeout_in_seconds = -1;
- return mojom::MessageStreamMessage::NewRingDeviceEvent(
- std::move(ring_device));
- }
- // https://developers.google.com/nearby/fast-pair/spec#MessageStreamAcknowledgements
- mojom::MessageStreamMessagePtr FastPairDataParser::ParseAcknowledgementEvent(
- uint8_t message_code,
- const base::span<uint8_t>& additional_data) {
- if (message_code != kAckCode && message_code != kNakCode) {
- QP_LOG(WARNING)
- << __func__
- << ": Unknown message code to parse Acknowledgement code. Received 0x"
- << std::hex << message_code << ".";
- return nullptr;
- }
- if (message_code == kAckCode) {
- // The length two of additional data contains the action message group and
- // code.
- if (additional_data.size() != 2) {
- QP_LOG(WARNING)
- << __func__
- << ": Invalid number of bytes in additional data to parse "
- "Acknowledgement. Needed 2, received "
- << additional_data.size();
- return nullptr;
- }
- // Get the message group pertaining to the action being acknowledged.
- absl::optional<mojom::MessageGroup> message_group =
- MessageGroupFromByte(additional_data[0]);
- if (!message_group.has_value()) {
- QP_LOG(WARNING) << __func__ << ": Unknown message group. Received 0x"
- << std::hex << additional_data[0] << ".";
- return nullptr;
- }
- mojom::AcknowledgementMessagePtr ack = mojom::AcknowledgementMessage::New();
- ack->action_message_code = additional_data[1];
- ack->acknowledgement = mojom::Acknowledgement::kAck;
- ack->action_message_group = message_group.value();
- return mojom::MessageStreamMessage::NewAcknowledgement(std::move(ack));
- }
- if (message_code == kNakCode) {
- // The length three of additional data contains the action message group and
- // code.
- if (additional_data.size() != 3) {
- QP_LOG(WARNING)
- << __func__
- << ": Invalid number of bytes in additional data to parse "
- "Acknowledgement. Needed 3, received "
- << additional_data.size();
- return nullptr;
- }
- absl::optional<mojom::Acknowledgement> nak_reason =
- NakReasonFromByte(additional_data[0]);
- if (!nak_reason) {
- QP_LOG(WARNING)
- << __func__
- << ": Unknown nak reason to parse Acknowledgement. Received 0x"
- << std::hex << additional_data[0];
- return nullptr;
- }
- // Get the message group pertaining to the action being nacknowledged.
- absl::optional<mojom::MessageGroup> message_group =
- MessageGroupFromByte(additional_data[1]);
- if (!message_group.has_value()) {
- QP_LOG(WARNING) << __func__ << ": Unknown message group. Received 0x"
- << std::hex << additional_data[1] << ".";
- return nullptr;
- }
- mojom::AcknowledgementMessagePtr ack = mojom::AcknowledgementMessage::New();
- ack->action_message_code = additional_data[2];
- ack->action_message_group = message_group.value();
- ack->acknowledgement = nak_reason.value();
- return mojom::MessageStreamMessage::NewAcknowledgement(std::move(ack));
- }
- return nullptr;
- }
- } // namespace quick_pair
- } // namespace ash
|