Add battery notification and not discoverable advertisement

PiperOrigin-RevId: 532187475
This commit is contained in:
Qin Wang
2023-05-15 12:19:45 -07:00
committed by Copybara-Service
parent 0e1170c52e
commit 6d3704f50a
6 changed files with 391 additions and 1 deletions
+21
View File
@@ -3,6 +3,7 @@ licenses(["notice"])
cc_library(
name = "common",
srcs = [
"battery_notification.cc",
"fast_pair_device.cc",
"fast_pair_http_result.cc",
"pair_failure.cc",
@@ -10,9 +11,11 @@ cc_library(
],
hdrs = [
"account_key.h",
"battery_notification.h",
"constant.h",
"fast_pair_device.h",
"fast_pair_http_result.h",
"non_discoverable_advertisement.h",
"pair_failure.h",
"protocol.h",
],
@@ -21,6 +24,7 @@ cc_library(
],
deps = [
"//internal/crypto",
"//internal/platform:logging",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/status",
"@com_google_absl//absl/strings",
@@ -36,6 +40,7 @@ cc_test(
shard_count = 16,
deps = [
":common",
"//internal/platform/implementation/g3", # build_cleaner: keep
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/strings",
"@com_google_googletest//:gtest_main",
@@ -51,6 +56,22 @@ cc_test(
shard_count = 16,
deps = [
":common",
"//internal/platform/implementation/g3", # build_cleaner: keep
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "battery_notification_test",
size = "small",
srcs = [
"battery_notification_test.cc",
],
shard_count = 16,
deps = [
":common",
"//internal/platform/implementation/g3", # build_cleaner: keep
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_googletest//:gtest_main",
],
+86
View File
@@ -0,0 +1,86 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "fastpair/common/battery_notification.h"
#include <optional>
#include <utility>
#include <vector>
#include "fastpair/common/constant.h"
#include "internal/platform/logging.h"
namespace nearby {
namespace fastpair {
BatteryInfo::BatteryInfo(bool is_charging)
: is_charging(is_charging), percentage(std::nullopt) {}
BatteryInfo::BatteryInfo(bool is_charging, int8_t percentage)
: is_charging(is_charging), percentage(percentage) {}
// static
BatteryInfo BatteryInfo::FromByte(uint8_t byte) {
// Battery value is in the form 0bSVVVVVVV.
// S = charging (0b1) or not (0b0).
// V = value, Ranges from 0-100, or 0bS1111111 if unknown.
bool is_charging = byte & kBatteryChargingMask;
uint8_t percentage = byte & kBatteryPercentageMask;
int8_t percentage_signed = static_cast<int8_t>(percentage);
if (percentage_signed < 0 || percentage_signed > 100) {
NEARBY_LOGS(INFO) << __func__ << "Invalid battery percentage.";
return BatteryInfo(is_charging);
}
return BatteryInfo(is_charging, percentage_signed);
}
uint8_t BatteryInfo::ToByte() const {
// Battery value is in the form 0bSVVVVVVV.
// S = charging (0b1) or not (0b0).
// V = value, Ranges from 0-100, or 1111111 if unknown.
if (!percentage) {
return is_charging ? kBatteryIsChargingByte : kBatteryNotChargingByte;
} else {
return percentage.value() | (is_charging ? kBatteryChargingMask : 0);
}
}
BatteryNotification::BatteryNotification(
Type type, const std::vector<BatteryInfo>& battery_infos)
: type(type), battery_infos(battery_infos) {}
// static
std::optional<BatteryNotification> BatteryNotification::FromBytes(
const std::vector<uint8_t>& bytes, Type type) {
if (bytes.size() == 1) {
// Single component device.
NEARBY_LOGS(INFO) << __func__ << " : Single component device.";
std::vector<BatteryInfo> battery_infos = {BatteryInfo::FromByte(bytes[0])};
return std::make_optional<BatteryNotification>(type, battery_infos);
} else if (bytes.size() == 3) {
// True wireless headset expecting 3 bytes - Left bud, Right bud and case.
NEARBY_LOGS(INFO) << __func__ << " : True wireless headset.";
std::vector<BatteryInfo> battery_infos = {
/* left bud info */ BatteryInfo::FromByte(bytes[0]),
/* right bud info */ BatteryInfo::FromByte(bytes[1]),
/* case info */ BatteryInfo::FromByte(bytes[2])};
return std::make_optional<BatteryNotification>(type, battery_infos);
}
NEARBY_LOGS(WARNING) << __func__
<< " : Unexpected battery notification length :"
<< bytes.size();
return std::nullopt;
}
} // namespace fastpair
} // namespace nearby
+66
View File
@@ -0,0 +1,66 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef THIRD_PARTY_NEARBY_FASTPAIR_COMMON_BATTERY_NOTIFICATION_H_
#define THIRD_PARTY_NEARBY_FASTPAIR_COMMON_BATTERY_NOTIFICATION_H_
#include <cstdint>
#include <optional>
#include <vector>
namespace nearby {
namespace fastpair {
// Fast Pair battery information from notification. See
// https://developers.google.com/nearby/fast-pair/spec#BatteryNotification
struct BatteryInfo {
BatteryInfo() = default;
explicit BatteryInfo(bool is_charging);
BatteryInfo(bool is_charging, int8_t percentage);
~BatteryInfo() = default;
static BatteryInfo FromByte(uint8_t byte);
uint8_t ToByte() const;
bool is_charging = false;
std::optional<int8_t> percentage;
};
// Fast Pair battery notification. See
// https://developers.google.com/nearby/fast-pair/spec#BatteryNotification
struct BatteryNotification {
// Represents if the provider wants to show an indication
// of the battery values
enum class Type {
kNone = 0,
kShowUi = 3, /* Show UI indication: 0b0011 */
kHideUi = 4, /* Hide UI indication: 0b0100 */
};
BatteryNotification() = default;
BatteryNotification(Type type, const std::vector<BatteryInfo>& battery_infos);
~BatteryNotification() = default;
static std::optional<BatteryNotification> FromBytes(
const std::vector<uint8_t>& bytes, Type type);
Type type = Type::kNone;
std::vector<BatteryInfo> battery_infos;
};
} // namespace fastpair
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_FASTPAIR_COMMON_BATTERY_NOTIFICATION_H_
@@ -0,0 +1,152 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "fastpair/common/battery_notification.h"
#include <cstdint>
#include <vector>
#include "gtest/gtest.h"
namespace nearby {
namespace fastpair {
namespace {
// Test data comes from:
// https://developers.google.com/nearby/fast-pair/specifications/appendix/testcases#test_cases
TEST(BatteryNotificationTest, TestBatteryInfo) {
// Tests default constructor
BatteryInfo battery_info_1;
EXPECT_FALSE(battery_info_1.is_charging);
// Tests Constructor with is_charging
BatteryInfo battery_info_2(true);
EXPECT_TRUE(battery_info_2.is_charging);
// Tests ToByte and FromByte
BatteryInfo battery_info_3 = BatteryInfo::FromByte(battery_info_2.ToByte());
EXPECT_TRUE(battery_info_3.is_charging);
EXPECT_FALSE(battery_info_3.percentage.has_value());
// Tests Constructor with is_charging and percentage value
BatteryInfo battery_info_4(true, 80);
EXPECT_TRUE(battery_info_4.is_charging);
EXPECT_EQ(battery_info_4.percentage.value(), 80);
// Tests ToByte and FromByte
BatteryInfo battery_info_5 = BatteryInfo::FromByte(battery_info_4.ToByte());
EXPECT_TRUE(battery_info_5.is_charging);
EXPECT_EQ(battery_info_5.percentage.value(), 80);
// Tests Constructor with is_charging and wrong percentage value
BatteryInfo battery_info_6(true, 110);
// Tests ToByte and FromByte
BatteryInfo battery_info_7 = BatteryInfo::FromByte(battery_info_6.ToByte());
EXPECT_TRUE(battery_info_7.is_charging);
EXPECT_FALSE(battery_info_7.percentage.has_value());
// Tests FromByte with battery percentage = 0
BatteryInfo battery_info_8 = BatteryInfo::FromByte(0);
EXPECT_FALSE(battery_info_8.is_charging);
EXPECT_EQ(battery_info_8.percentage.value(), 0);
BatteryInfo battery_info_10 = BatteryInfo::FromByte(0x80);
EXPECT_TRUE(battery_info_10.is_charging);
EXPECT_EQ(battery_info_10.percentage.value(), 0);
// Tests FromByte with battery percentage = 100
BatteryInfo battery_info_9 = BatteryInfo::FromByte(100);
EXPECT_FALSE(battery_info_9.is_charging);
EXPECT_EQ(battery_info_9.percentage.value(), 100);
BatteryInfo battery_info_11 = BatteryInfo::FromByte(0x80 | 100);
EXPECT_TRUE(battery_info_11.is_charging);
EXPECT_EQ(battery_info_11.percentage.value(), 100);
// Test battery with invalid battery percentage
BatteryInfo battery_info_12 = BatteryInfo::FromByte(0x7F);
EXPECT_FALSE(battery_info_12.is_charging);
EXPECT_FALSE(battery_info_12.percentage.has_value());
BatteryInfo battery_info_13 = BatteryInfo::FromByte(0xFF);
EXPECT_TRUE(battery_info_13.is_charging);
EXPECT_FALSE(battery_info_13.percentage.has_value());
}
TEST(BatteryNotificationTest, TestBatteryNotificationDefaultConstructor) {
// Tests default constructor
BatteryNotification battery_notification;
EXPECT_EQ(battery_notification.type, BatteryNotification::Type::kNone);
EXPECT_EQ(battery_notification.battery_infos.size(), 0);
}
TEST(BatteryNotificationTest, TestBatteryNotificationForSingleComponentDevice) {
std::vector<BatteryInfo> battery_infos = {BatteryInfo(false, 70)};
BatteryNotification battery_notification(BatteryNotification::Type::kShowUi,
battery_infos);
EXPECT_EQ(battery_notification.type, BatteryNotification::Type::kShowUi);
EXPECT_FALSE(battery_notification.battery_infos.at(0).is_charging);
EXPECT_EQ(battery_notification.battery_infos.at(0).percentage.value(), 70);
}
TEST(BatteryNotificationTest, TestBatteryNotificationForTrueWirelessHeadset) {
std::vector<BatteryInfo> battery_infos = {
BatteryInfo(false, 70), BatteryInfo(false, 80), BatteryInfo(true, 90)};
BatteryNotification battery_notification(BatteryNotification::Type::kShowUi,
battery_infos);
EXPECT_EQ(battery_notification.type, BatteryNotification::Type::kShowUi);
// Left bud
EXPECT_FALSE(battery_notification.battery_infos.at(0).is_charging);
EXPECT_EQ(battery_notification.battery_infos.at(0).percentage.value(), 70);
// Right bud
EXPECT_FALSE(battery_notification.battery_infos.at(1).is_charging);
EXPECT_EQ(battery_notification.battery_infos.at(1).percentage.value(), 80);
// Case
EXPECT_TRUE(battery_notification.battery_infos.at(2).is_charging);
EXPECT_EQ(battery_notification.battery_infos.at(2).percentage.value(), 90);
}
TEST(BatteryNotificationTest,
TestBatteryNotificationFromBytesForSingleComponentDevice) {
const std::vector<uint8_t> batteryData{0b01000000};
BatteryNotification battery_notification =
BatteryNotification::FromBytes(batteryData,
BatteryNotification::Type::kShowUi)
.value();
EXPECT_EQ(battery_notification.type, BatteryNotification::Type::kShowUi);
EXPECT_EQ(battery_notification.battery_infos.at(0).percentage.value(), 64);
}
TEST(BatteryNotificationTest,
TestBatteryNotificationFromBytesForTrueWirelessHeadset) {
const std::vector<uint8_t> batteryData{0b01000000, 0b01000000, 0b01000000};
BatteryNotification battery_notification =
BatteryNotification::FromBytes(batteryData,
BatteryNotification::Type::kShowUi)
.value();
EXPECT_EQ(battery_notification.type, BatteryNotification::Type::kShowUi);
EXPECT_EQ(battery_notification.battery_infos.at(0).percentage.value(), 64);
EXPECT_EQ(battery_notification.battery_infos.at(1).percentage.value(), 64);
EXPECT_EQ(battery_notification.battery_infos.at(2).percentage.value(), 64);
}
TEST(BatteryNotificationTest, TestBatteryNotificationFromWrongBytes) {
const std::vector<uint8_t> batteryDataWithWrongLenth{0b01000000, 0b01000000};
EXPECT_FALSE(
BatteryNotification::FromBytes(batteryDataWithWrongLenth,
BatteryNotification::Type::kShowUi)
.has_value());
}
} // namespace
} // namespace fastpair
} // namespace nearby
+7 -1
View File
@@ -24,7 +24,6 @@ namespace fastpair {
constexpr char kServiceId[] = "Fast Pair";
constexpr char kRfcommUuid[] = "df21fe2c-2515-4fdb-8886-f12c4d67927c";
constexpr int kAccountKeySize = 16;
// Key pair
@@ -60,6 +59,13 @@ constexpr uint8_t kAccountKeyStartByte = 0x04;
constexpr uint8_t kKeyBasedPairingType = 0x00;
constexpr uint8_t kInitialOrSubsequentFlags = 0x00;
constexpr uint8_t kRetroactiveFlags = 0x10;
// Battery Info
constexpr int kBatteryChargingMask = 0b10000000;
constexpr int kBatteryPercentageMask = 0b01111111;
constexpr int kBatteryIsChargingByte = 0b11111111;
constexpr int kBatteryNotChargingByte = 0b01111111;
} // namespace fastpair
} // namespace nearby
@@ -0,0 +1,59 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef THIRD_PARTY_NEARBY_FASTPAIR_COMMON_NON_DISCOVERABLE_ADVERTISEMENT_H_
#define THIRD_PARTY_NEARBY_FASTPAIR_COMMON_NON_DISCOVERABLE_ADVERTISEMENT_H_
#include <cstdint>
#include <optional>
#include <utility>
#include <vector>
#include "fastpair/common/battery_notification.h"
namespace nearby {
namespace fastpair {
// Fast Pair 'Not Discoverable' advertisement. See
// https://developers.google.com/nearby/fast-pair/specifications/service/provider#AdvertisingWhenNotDiscoverable
struct NonDiscoverableAdvertisement {
// Represents showing UI indication
enum class Type {
kShowUi = 0, /* Show UI indication: 0b0000*/
kNone = 1,
kHideUi = 2, /* Hide UI indication: 0b0010*/
};
NonDiscoverableAdvertisement() = default;
NonDiscoverableAdvertisement(
std::vector<uint8_t> account_key_filter, Type type,
std::vector<uint8_t> salt,
std::optional<BatteryNotification> battery_notification)
: account_key_filter(std::move(account_key_filter)),
type(type),
salt(std::move(salt)),
battery_notification(std::move(battery_notification)) {}
~NonDiscoverableAdvertisement() = default;
std::vector<uint8_t> account_key_filter;
Type type = Type::kNone;
std::vector<uint8_t> salt;
std::optional<BatteryNotification> battery_notification;
};
} // namespace fastpair
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_FASTPAIR_COMMON_NON_DISCOVERABLE_ADVERTISEMENT_H_