Internal bug fix

PiperOrigin-RevId: 738897253
This commit is contained in:
Guogang Li
2025-03-20 12:23:51 -07:00
committed by Copybara-Service
parent 57773f9b80
commit a3049fe436
3 changed files with 217 additions and 159 deletions
@@ -17,8 +17,11 @@
#include <list>
#include <memory>
#include <string>
#include <utility>
#include "absl/functional/any_invocable.h"
#include "absl/strings/escaping.h"
#include "absl/strings/string_view.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "connections/implementation/mediums/ble_v2/ble_utils.h"
@@ -26,9 +29,11 @@
#include "internal/platform/ble_v2.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/cancelable_alarm.h"
#include "internal/platform/count_down_latch.h"
#include "internal/platform/exception.h"
#include "internal/platform/implementation/ble_v2.h"
#include "internal/platform/implementation/system_clock.h"
#include "internal/platform/logging.h"
#include "internal/platform/mutex_lock.h"
namespace nearby {
namespace connections {
@@ -38,123 +43,144 @@ namespace {
constexpr int kMaxAdvertisingOnLostHashCount =
InstantOnLostAdvertisement::kMaxHashCount;
constexpr absl::Duration kInstantOnLostAdvertiseDuration = absl::Seconds(2);
constexpr absl::Duration kShutdownWaitDuration = absl::Seconds(1);
constexpr absl::Duration kTestWaitDuration = absl::Seconds(3);
} // namespace
void InstantOnLostManager::OnAdvertisingStarted(
const std::string& service_id, const ByteArray& advertisement_data) {
MutexLock lock(&mutex_);
if (is_shutdown_) {
NEARBY_LOGS(WARNING) << __func__ << ": InstantOnLostManager is shutdown.";
return;
}
if (service_id.empty()) {
NEARBY_LOGS(WARNING) << __func__ << ": Invalid service ID.";
return;
}
if (advertisement_data.Empty()) {
NEARBY_LOGS(WARNING) << __func__ << ": Invalid advertisement data.";
return;
}
ByteArray advertisement_hash =
bleutils::GenerateAdvertisementHash(advertisement_data);
// Check whether the hash is in on lost list.
for (auto& it : active_on_lost_advertising_list_) {
if (it.hash == std::string(advertisement_hash)) {
StopOnLostAdvertising();
if (stop_advertising_alarm_ != nullptr) {
stop_advertising_alarm_->Cancel();
}
active_on_lost_advertising_list_.remove(it);
if (!active_on_lost_advertising_list_.empty()) {
StartInstantOnLostAdvertisement();
}
NEARBY_LOGS(INFO) << __func__ << ": Remove the lost hash "
<< absl::BytesToHexString(
advertisement_hash.AsStringView())
<< " from the list.";
break;
RunOnInstantOnLostThread("OnAdvertisingStarted", [this, service_id,
advertisement_data]() {
if (service_id.empty()) {
LOG(WARNING) << __func__ << ": Invalid service ID.";
return;
}
}
active_advertising_map_[service_id] = advertisement_hash;
NEARBY_LOGS(INFO) << __func__
<< ": OnAdvertisingStarted from service ID: " << service_id
<< " with hash: "
<< absl::BytesToHexString(
advertisement_hash.AsStringView());
if (advertisement_data.Empty()) {
LOG(WARNING) << __func__ << ": Invalid advertisement data.";
return;
}
ByteArray advertisement_hash =
bleutils::GenerateAdvertisementHash(advertisement_data);
// Check whether the hash is in on lost list.
for (auto& it : active_on_lost_advertising_list_) {
if (it.hash == std::string(advertisement_hash)) {
StopOnLostAdvertising();
if (stop_advertising_alarm_ != nullptr) {
stop_advertising_alarm_->Cancel();
}
active_on_lost_advertising_list_.remove(it);
if (!active_on_lost_advertising_list_.empty()) {
StartInstantOnLostAdvertisement();
}
LOG(INFO) << __func__ << ": Remove the lost hash "
<< absl::BytesToHexString(advertisement_hash.AsStringView())
<< " from the list.";
break;
}
}
active_advertising_map_[service_id] = advertisement_hash;
LOG(INFO) << __func__
<< ": OnAdvertisingStarted from service ID: " << service_id
<< " with hash: "
<< absl::BytesToHexString(advertisement_hash.AsStringView());
});
}
void InstantOnLostManager::OnAdvertisingStopped(const std::string& service_id) {
MutexLock lock(&mutex_);
RunOnInstantOnLostThread("OnAdvertisingStopped", [this, service_id]() {
auto active_advertising = active_advertising_map_.extract(service_id);
if (is_shutdown_) {
NEARBY_LOGS(WARNING) << __func__ << ": InstantOnLostManager is shutdown.";
return;
}
if (active_advertising.empty()) {
LOG(WARNING) << __func__
<< ": Stopped advertising for service ID: " << service_id
<< " but it is not found in the active advertising map.";
return;
}
auto active_advertising = active_advertising_map_.extract(service_id);
const ByteArray& advertisement_hash = active_advertising.mapped();
if (active_advertising.empty()) {
NEARBY_LOGS(WARNING)
<< __func__ << ": Stopped advertising for service ID: " << service_id
<< " but it is not found in the active advertising map.";
return;
}
if (active_on_lost_advertising_list_.size() >=
kMaxAdvertisingOnLostHashCount) {
active_on_lost_advertising_list_.pop_front();
}
const ByteArray& advertisement_hash = active_advertising.mapped();
active_on_lost_advertising_list_.push_back(
{absl::Now(), std::string(advertisement_hash)});
if (active_on_lost_advertising_list_.size() >=
kMaxAdvertisingOnLostHashCount) {
active_on_lost_advertising_list_.pop_front();
}
if (!StartInstantOnLostAdvertisement()) {
LOG(ERROR) << __func__ << ": Failed to advertise instant onLost BLE.";
}
active_on_lost_advertising_list_.push_back(
{absl::Now(), std::string(advertisement_hash)});
if (!StartInstantOnLostAdvertisement()) {
NEARBY_LOGS(ERROR) << __func__
<< ": Failed to advertise instant onLost BLE.";
}
NEARBY_LOGS(INFO) << __func__
<< ": OnAdvertisingStopped from service ID: " << service_id;
LOG(INFO) << __func__
<< ": OnAdvertisingStopped from service ID: " << service_id;
});
}
bool InstantOnLostManager::Shutdown() {
MutexLock lock(&mutex_);
if (is_shutdown_) {
NEARBY_LOGS(WARNING) << __func__
<< ": InstantOnLostManager is already shutdown.";
return false;
CountDownLatch latch(1);
RunOnInstantOnLostThread("Shutdown", [this, &latch]() {
if (is_shutdown_) {
LOG(WARNING) << __func__ << ": InstantOnLostManager is already shutdown.";
latch.CountDown();
return;
}
is_shutdown_ = true;
if (stop_advertising_alarm_ != nullptr) {
stop_advertising_alarm_->Cancel();
}
StopOnLostAdvertising();
active_on_lost_advertising_list_.clear();
active_advertising_map_.clear();
LOG(INFO) << __func__ << ": InstantOnLostManager is shutdown.";
latch.CountDown();
});
ExceptionOr<bool> result = latch.Await(kShutdownWaitDuration);
if (result.ok()) {
return result.result();
}
if (stop_advertising_alarm_ != nullptr) {
stop_advertising_alarm_->Cancel();
}
StopOnLostAdvertising();
active_on_lost_advertising_list_.clear();
active_advertising_map_.clear();
is_shutdown_ = true;
NEARBY_LOGS(INFO) << __func__ << ": InstantOnLostManager is shutdown.";
return true;
return false;
}
std::list<std::string> InstantOnLostManager::GetOnLostHashes() {
MutexLock lock(&mutex_);
return GetOnLostHashesInternal();
std::list<std::string> InstantOnLostManager::GetOnLostHashesForTesting() {
CountDownLatch latch(1);
std::list<std::string> result;
RunOnInstantOnLostThread("GetOnLostHashes", [&latch, &result, this]() {
result = GetOnLostHashesInternal();
latch.CountDown();
});
ExceptionOr<bool> latch_result = latch.Await(kTestWaitDuration);
if (latch_result.ok() && latch_result.result()) {
return result;
}
return {};
}
bool InstantOnLostManager::IsOnLostAdvertising() {
MutexLock lock(&mutex_);
return is_on_lost_advertising_;
bool InstantOnLostManager::IsOnLostAdvertisingForTesting() {
CountDownLatch latch(1);
bool is_on_lost_advertising = false;
RunOnInstantOnLostThread("IsOnLostAdvertising",
[&latch, &is_on_lost_advertising, this]() {
is_on_lost_advertising = is_on_lost_advertising_;
latch.CountDown();
});
ExceptionOr<bool> result = latch.Await(kTestWaitDuration);
if (result.ok() && result.result()) {
return is_on_lost_advertising;
}
return false;
}
bool InstantOnLostManager::StartInstantOnLostAdvertisement() {
@@ -167,8 +193,8 @@ bool InstantOnLostManager::StartInstantOnLostAdvertisement() {
absl::StatusOr<InstantOnLostAdvertisement> on_lost_advertisement =
InstantOnLostAdvertisement::CreateFromHashes(GetOnLostHashesInternal());
if (!on_lost_advertisement.ok()) {
NEARBY_LOGS(ERROR) << __func__
<< ": Failed to create instant on lost advertisement.";
LOG(ERROR) << __func__
<< ": Failed to create instant on lost advertisement.";
return false;
}
@@ -180,11 +206,12 @@ bool InstantOnLostManager::StartInstantOnLostAdvertisement() {
StopOnLostAdvertising();
if (!ble_medium_.StartAdvertising(advertisement_data, advertise_parameters)) {
NEARBY_LOGS(ERROR) << __func__
<< ": Failed to start advertising for instant on lost.";
LOG(ERROR) << __func__
<< ": Failed to start advertising for instant on lost.";
return false;
}
last_advertising_start_time_ = SystemClock::ElapsedRealtime();
if (stop_advertising_alarm_ != nullptr) {
stop_advertising_alarm_->Cancel();
}
@@ -192,20 +219,33 @@ bool InstantOnLostManager::StartInstantOnLostAdvertisement() {
// Schedule to stop the advertising.
stop_advertising_alarm_ = std::make_unique<CancelableAlarm>(
"stop_instant_on_lost_advertising",
[this]() {
MutexLock lock(&mutex_);
StopOnLostAdvertising();
// All hashes already advertised for enough time, we should clear the
// list.
active_on_lost_advertising_list_.clear();
is_on_lost_advertising_ = false;
[this, start_time = last_advertising_start_time_]() {
// It is running on timer thread, so we need to run it on instant on
// lost thread.
RunOnInstantOnLostThread(
"stop_instant_on_lost_advertising", [this, start_time]() {
if (start_time != last_advertising_start_time_) {
LOG(WARNING)
<< "Skip to stop instant on lost advertising due to "
"the start time is changed.";
return;
}
LOG(INFO) << "Stop instant on lost advertising after duration.";
StopOnLostAdvertising();
// All hashes already advertised for enough time, we should clear
// the list.
active_on_lost_advertising_list_.clear();
is_on_lost_advertising_ = false;
});
},
kInstantOnLostAdvertiseDuration, &executor_);
kInstantOnLostAdvertiseDuration, &service_thread_);
is_on_lost_advertising_ = true;
NEARBY_LOGS(INFO)
<< __func__ << ": Started instant on lost advertising with hashes count: "
<< active_on_lost_advertising_list_.size();
LOG(INFO) << __func__
<< ": Started instant on lost advertising with hashes count: "
<< active_on_lost_advertising_list_.size();
return true;
}
@@ -215,12 +255,12 @@ bool InstantOnLostManager::StopOnLostAdvertising() {
}
if (!ble_medium_.StopAdvertising()) {
NEARBY_LOGS(ERROR) << __func__ << ": Failed to stop on lost advertising.";
LOG(ERROR) << __func__ << ": Failed to stop on lost advertising.";
return false;
}
is_on_lost_advertising_ = false;
NEARBY_LOGS(INFO) << __func__ << ": Stopped instant on lost advertising";
LOG(INFO) << __func__ << ": Stopped instant on lost advertising";
return true;
}
@@ -244,6 +284,28 @@ std::list<std::string> InstantOnLostManager::GetOnLostHashesInternal() {
return result;
}
void InstantOnLostManager::RunOnInstantOnLostThread(
absl::string_view task_name, absl::AnyInvocable<void()> task) {
service_thread_.Execute(
[this, name = std::string(task_name), task = std::move(task)]() mutable {
if (is_shutdown_) {
LOG(WARNING) << "Skip to run InstantOnLost task " << name
<< " due to InstantOnLost is closed";
return;
}
LOG(INFO) << __func__ << ": Scheduled to run task " << name
<< " on InstantOnLost thread.";
VLOG(1) << __func__ << ": Started to run task " << name
<< " on InstantOnLost thread. ";
task();
VLOG(1) << __func__ << ": Completed to run task " << name
<< " on InstantOnLost thread.";
});
}
} // namespace mediums
} // namespace connections
} // namespace nearby
@@ -19,35 +19,36 @@
#include <memory>
#include <string>
#include "absl/base/thread_annotations.h"
#include "absl/container/flat_hash_map.h"
#include "absl/functional/any_invocable.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "internal/platform/ble_v2.h"
#include "internal/platform/bluetooth_adapter.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/cancelable_alarm.h"
#include "internal/platform/mutex.h"
#include "internal/platform/scheduled_executor.h"
namespace nearby {
namespace connections {
namespace mediums {
// This InstantOnLostManager class is thread-compatible, and all methods are
// executed on a single thread.
class InstantOnLostManager {
public:
InstantOnLostManager() = default;
~InstantOnLostManager() = default;
void OnAdvertisingStarted(const std::string& service_id,
const ByteArray& advertisement_data)
ABSL_LOCKS_EXCLUDED(mutex_);
void OnAdvertisingStopped(const std::string& service_id)
ABSL_LOCKS_EXCLUDED(mutex_);
const ByteArray& advertisement_data);
void OnAdvertisingStopped(const std::string& service_id);
bool Shutdown() ABSL_LOCKS_EXCLUDED(mutex_);
bool Shutdown();
std::list<std::string> GetOnLostHashes() ABSL_LOCKS_EXCLUDED(mutex_);
bool IsOnLostAdvertising() ABSL_LOCKS_EXCLUDED(mutex_);
// For testing only.
std::list<std::string> GetOnLostHashesForTesting();
bool IsOnLostAdvertisingForTesting();
private:
struct OnLostAdvertisementHashInfo {
@@ -59,34 +60,29 @@ class InstantOnLostManager {
}
};
bool StartInstantOnLostAdvertisement() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
bool StopOnLostAdvertising() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
void RemoveExpiredOnLostAdvertisements()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
std::list<std::string> GetOnLostHashesInternal()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
bool StartInstantOnLostAdvertisement();
bool StopOnLostAdvertising();
void RemoveExpiredOnLostAdvertisements();
std::list<std::string> GetOnLostHashesInternal();
Mutex mutex_;
void RunOnInstantOnLostThread(absl::string_view task_name,
absl::AnyInvocable<void()> task);
std::unique_ptr<CancelableAlarm> stop_advertising_alarm_
ABSL_GUARDED_BY(mutex_);
bool is_shutdown_ = false;
ScheduledExecutor service_thread_;
std::unique_ptr<CancelableAlarm> stop_advertising_alarm_;
// BLE medium used for lost packet advertising.
BluetoothAdapter adapter_ ABSL_GUARDED_BY(mutex_);
BleV2Medium ble_medium_ ABSL_GUARDED_BY(mutex_) = BleV2Medium{adapter_};
bool is_on_lost_advertising_ ABSL_GUARDED_BY(mutex_) = false;
bool is_shutdown_ ABSL_GUARDED_BY(mutex_) = false;
ScheduledExecutor executor_ ABSL_GUARDED_BY(mutex_);
BluetoothAdapter adapter_;
BleV2Medium ble_medium_ = BleV2Medium{adapter_};
bool is_on_lost_advertising_ = false;
absl::Time last_advertising_start_time_ = absl::InfinitePast();
// Active on lost advertising, the maximum size is 5.
std::list<OnLostAdvertisementHashInfo> active_on_lost_advertising_list_
ABSL_GUARDED_BY(mutex_);
std::list<OnLostAdvertisementHashInfo> active_on_lost_advertising_list_;
// Map of service ID to advertisement data hash.
absl::flat_hash_map<std::string, ByteArray> active_advertising_map_
ABSL_GUARDED_BY(mutex_);
absl::flat_hash_map<std::string, ByteArray> active_advertising_map_;
};
} // namespace mediums
@@ -44,9 +44,9 @@ TEST(InstantOnLostManager, StartOnLostAdvertisingAfterStopAdvertising) {
instant_on_lost_manager.OnAdvertisingStarted(
std::string(kServiceIdA), ByteArray(kData1.data(), kData1.size()));
instant_on_lost_manager.OnAdvertisingStopped(std::string(kServiceIdA));
EXPECT_TRUE(instant_on_lost_manager.IsOnLostAdvertising());
EXPECT_TRUE(instant_on_lost_manager.IsOnLostAdvertisingForTesting());
absl::SleepFor(kOnLostAdvertisingDuration);
EXPECT_FALSE(instant_on_lost_manager.IsOnLostAdvertising());
EXPECT_FALSE(instant_on_lost_manager.IsOnLostAdvertisingForTesting());
}
TEST(InstantOnLostManager, NoOnLostAdvertisingWhenAdvertiseAgain) {
@@ -56,7 +56,7 @@ TEST(InstantOnLostManager, NoOnLostAdvertisingWhenAdvertiseAgain) {
instant_on_lost_manager.OnAdvertisingStopped(std::string(kServiceIdA));
instant_on_lost_manager.OnAdvertisingStarted(
std::string(kServiceIdA), ByteArray(kData1.data(), kData1.size()));
EXPECT_FALSE(instant_on_lost_manager.IsOnLostAdvertising());
EXPECT_FALSE(instant_on_lost_manager.IsOnLostAdvertisingForTesting());
instant_on_lost_manager.Shutdown();
}
@@ -65,18 +65,18 @@ TEST(InstantOnLostManager, MultipleAdvertisingOnSameServiceId) {
instant_on_lost_manager.OnAdvertisingStarted(
std::string(kServiceIdA), ByteArray(kData1.data(), kData1.size()));
instant_on_lost_manager.OnAdvertisingStopped(std::string(kServiceIdA));
EXPECT_TRUE(instant_on_lost_manager.IsOnLostAdvertising());
EXPECT_EQ(instant_on_lost_manager.GetOnLostHashes().size(), 1);
EXPECT_TRUE(instant_on_lost_manager.IsOnLostAdvertisingForTesting());
EXPECT_EQ(instant_on_lost_manager.GetOnLostHashesForTesting().size(), 1);
instant_on_lost_manager.OnAdvertisingStarted(
std::string(kServiceIdA), ByteArray(kData2.data(), kData2.size()));
instant_on_lost_manager.OnAdvertisingStopped(std::string(kServiceIdA));
EXPECT_TRUE(instant_on_lost_manager.IsOnLostAdvertising());
EXPECT_EQ(instant_on_lost_manager.GetOnLostHashes().size(), 2);
EXPECT_TRUE(instant_on_lost_manager.IsOnLostAdvertisingForTesting());
EXPECT_EQ(instant_on_lost_manager.GetOnLostHashesForTesting().size(), 2);
instant_on_lost_manager.OnAdvertisingStarted(
std::string(kServiceIdA), ByteArray(kData3.data(), kData3.size()));
instant_on_lost_manager.OnAdvertisingStopped(std::string(kServiceIdA));
EXPECT_TRUE(instant_on_lost_manager.IsOnLostAdvertising());
EXPECT_EQ(instant_on_lost_manager.GetOnLostHashes().size(), 3);
EXPECT_TRUE(instant_on_lost_manager.IsOnLostAdvertisingForTesting());
EXPECT_EQ(instant_on_lost_manager.GetOnLostHashesForTesting().size(), 3);
instant_on_lost_manager.Shutdown();
}
@@ -100,8 +100,8 @@ TEST(InstantOnLostManager, MaximumOnLostHashesInOnLostAdvertising) {
instant_on_lost_manager.OnAdvertisingStarted(
std::string(kServiceIdC), ByteArray(kData6.data(), kData6.size()));
instant_on_lost_manager.OnAdvertisingStopped(std::string(kServiceIdC));
EXPECT_TRUE(instant_on_lost_manager.IsOnLostAdvertising());
EXPECT_EQ(instant_on_lost_manager.GetOnLostHashes().size(), 5);
EXPECT_TRUE(instant_on_lost_manager.IsOnLostAdvertisingForTesting());
EXPECT_EQ(instant_on_lost_manager.GetOnLostHashesForTesting().size(), 5);
instant_on_lost_manager.Shutdown();
}
@@ -119,7 +119,7 @@ TEST(InstantOnLostManager, AdvertisingOnShutdownManager) {
instant_on_lost_manager.OnAdvertisingStarted(
std::string(kServiceIdA), ByteArray(kData1.data(), kData1.size()));
instant_on_lost_manager.OnAdvertisingStopped(std::string(kServiceIdA));
EXPECT_FALSE(instant_on_lost_manager.IsOnLostAdvertising());
EXPECT_FALSE(instant_on_lost_manager.IsOnLostAdvertisingForTesting());
}
TEST(InstantOnLostManager, RemoveExpiredOnLostAdvertisement) {
@@ -127,20 +127,20 @@ TEST(InstantOnLostManager, RemoveExpiredOnLostAdvertisement) {
instant_on_lost_manager.OnAdvertisingStarted(
std::string(kServiceIdA), ByteArray(kData1.data(), kData1.size()));
instant_on_lost_manager.OnAdvertisingStopped(std::string(kServiceIdA));
EXPECT_TRUE(instant_on_lost_manager.IsOnLostAdvertising());
EXPECT_EQ(instant_on_lost_manager.GetOnLostHashes().size(), 1);
EXPECT_TRUE(instant_on_lost_manager.IsOnLostAdvertisingForTesting());
EXPECT_EQ(instant_on_lost_manager.GetOnLostHashesForTesting().size(), 1);
absl::SleepFor(absl::Milliseconds(1050));
instant_on_lost_manager.OnAdvertisingStarted(
std::string(kServiceIdA), ByteArray(kData2.data(), kData2.size()));
instant_on_lost_manager.OnAdvertisingStopped(std::string(kServiceIdA));
EXPECT_TRUE(instant_on_lost_manager.IsOnLostAdvertising());
EXPECT_EQ(instant_on_lost_manager.GetOnLostHashes().size(), 2);
EXPECT_TRUE(instant_on_lost_manager.IsOnLostAdvertisingForTesting());
EXPECT_EQ(instant_on_lost_manager.GetOnLostHashesForTesting().size(), 2);
absl::SleepFor(absl::Milliseconds(1050));
instant_on_lost_manager.OnAdvertisingStarted(
std::string(kServiceIdA), ByteArray(kData3.data(), kData3.size()));
instant_on_lost_manager.OnAdvertisingStopped(std::string(kServiceIdA));
EXPECT_TRUE(instant_on_lost_manager.IsOnLostAdvertising());
EXPECT_EQ(instant_on_lost_manager.GetOnLostHashes().size(), 2);
EXPECT_TRUE(instant_on_lost_manager.IsOnLostAdvertisingForTesting());
EXPECT_EQ(instant_on_lost_manager.GetOnLostHashesForTesting().size(), 2);
instant_on_lost_manager.Shutdown();
}