Files
nearby/connections/implementation/mediums/ble/discovered_peripheral_tracker.h
T
Guogang Li cce45b745e Internal refactor
PiperOrigin-RevId: 815749899
2025-10-06 09:03:08 -07:00

381 lines
16 KiB
C++

// Copyright 2022 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 CORE_INTERNAL_MEDIUMS_BLE_DISCOVERED_PERIPHERAL_TRACKER_H_
#define CORE_INTERNAL_MEDIUMS_BLE_DISCOVERED_PERIPHERAL_TRACKER_H_
#include <array>
#include <deque>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "absl/base/thread_annotations.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/functional/any_invocable.h"
#include "absl/time/time.h"
#include "connections/implementation/mediums//lost_entity_tracker.h"
#include "connections/implementation/mediums/ble/advertisement_read_result.h"
#include "connections/implementation/mediums/ble/ble_advertisement.h"
#include "connections/implementation/mediums/ble/ble_advertisement_header.h"
#include "connections/implementation/mediums/ble/discovered_peripheral_callback.h"
#include "connections/implementation/mediums/lost_entity_tracker.h"
#include "connections/implementation/pcp.h"
#include "internal/platform/atomic_boolean.h"
#include "internal/platform/ble.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/condition_variable.h"
#include "internal/platform/implementation/ble.h"
#include "internal/platform/multi_thread_executor.h"
#include "internal/platform/mutex.h"
#include "internal/platform/uuid.h"
namespace nearby {
namespace connections {
namespace mediums {
// Manages all discovered peripheral logic for {@link BluetoothLowEnergy}. This
// includes tracking found peripherals, lost peripherals, and MAC addresses
// associated with those peripherals.
//
// See go/ble-on-lost for more information. It includes the algorithms used to
// compute found and lost peripherals.
class DiscoveredPeripheralTracker {
public:
static constexpr std::array<char, 23> kDummyAdvertisementValue = {
0x51, 0x43, 0x41, 0x41, 0x41, 0x42, 0x41, 0x43, 0x41, 0x41, 0x41, 0x44,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41};
// GATT advertisement fetcher.
// Fetches relevant GATT advertisements for the peripheral found in {@link
// DiscoveredPeripheralTracker#ProcessFoundBleAdvertisement(}.
//
// `advertisement_read_result` is in/out mutable reference that the caller
// should take of its life cycle and pass a valid reference.
using AdvertisementFetcher = absl::AnyInvocable<void(
BlePeripheral peripheral, int num_slots, int psm,
const std::vector<std::string>& interesting_service_ids,
mediums::AdvertisementReadResult& advertisement_read_result)>;
explicit DiscoveredPeripheralTracker(
bool is_extended_advertisement_available = false);
~DiscoveredPeripheralTracker();
// Starts tracking discoveries for a particular service Id.
//
// service_id - The service ID to track.
// discovered_peripheral_callback - The callback to invoke for discovery
// events.
// fast_advertisement_service_uuid - The service UUID to look for fast
// advertisements on.
// Note: fast_advertisement_service_uuid can be empty UUID to indicate
// that `fast_advertisement_service_uuid` will be ignored for regular
// advertisement.
void StartTracking(
const std::string& service_id, bool include_dct_advertisement, Pcp pcp,
DiscoveredPeripheralCallback discovered_peripheral_callback,
const Uuid& fast_advertisement_service_uuid) ABSL_LOCKS_EXCLUDED(mutex_);
// Stops tracking discoveries for a particular service Id.
void StopTracking(const std::string& service_id) ABSL_LOCKS_EXCLUDED(mutex_);
// Processes a found BLE advertisement.
//
// peripheral - To access the status of the operation. The ownership
// is moved into 'discovered_peripheral_tracker' object.
// advertisement_data - The found BLE advertisement data.
// advertisement_fetcher - The advertisement fetcher callback for regular
// advertisement if the advertisement header is been
// processed.
void ProcessFoundBleAdvertisement(
BlePeripheral peripheral,
api::ble::BleAdvertisementData advertisement_data,
AdvertisementFetcher advertisement_fetcher) ABSL_LOCKS_EXCLUDED(mutex_);
// Processes the set of lost GATT advertisements and notifies the client of
// any lost peripherals.
void ProcessLostGattAdvertisements() ABSL_LOCKS_EXCLUDED(mutex_);
// Shuts down the GATT fetching thread.
void Shutdown() ABSL_LOCKS_EXCLUDED(mutex_);
private:
using BleAdvertisementSet = absl::flat_hash_set<BleAdvertisement>;
// A container to hold callback or other informations that bring from BLE
// medium when `StartTracking`.
struct ServiceIdInfo {
// Tracks what service IDs are currently active and gives us client
// callbacks to call.
DiscoveredPeripheralCallback discovered_peripheral_callback;
// Used to periodically compute lost GATT advertisements.
std::unique_ptr<LostEntityTracker<BleAdvertisement>> lost_entity_tracker;
// Used to check for fast advertisements delivered through BLE advertisement
// service data, under the given UUID.
Uuid fast_advertisement_service_uuid;
// Used to check for dct advertisements delivered through BLE advertisement
// service data.
bool include_dct_advertisement;
Pcp pcp;
};
// A container to hold the related informations for a GATT advertisement.
struct GattAdvertisementInfo {
// GATT advertisement to the service ID it's associated with. Tracks what
// GATT advertisements are currently active. Used to determine which
// LostEntityTracker to invoke when advertisements are rediscovered.
std::string service_id;
// Used to efficiently find advertisement headers to delete when GATT
// advertisements are updated. This is a reverse map of
// gatt_advertisements_.
BleAdvertisementHeader advertisement_header;
// A proxy BlePeripheral for found/lost discovery callback.
BlePeripheral peripheral;
// Hash for instant on lost.
ByteArray instant_on_lost_hash;
};
struct GattFetchTask {
BlePeripheral peripheral;
BleAdvertisementHeader advertisement_header;
AdvertisementFetcher advertisement_fetcher;
absl::Time scheduled_time;
};
// Clears stale data from any previous sessions.
void ClearDataForServiceId(const std::string& service_id)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Returns true if `advertisement_data` is AdvertisementHeader and is marked
// as exented_advertisement. This is to avoid reading advertisement from GATT
// connection, which has been advertised by extended advertisement.
bool IsSkippableGattAdvertisement(
const api::ble::BleAdvertisementData& advertisement_data)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Clears out all data related to the provided GATT advertisement. This
// includes:
// 1. Removing the corresponding GATT advertisement from
// gatt_advertisement_infos_.
// 2. Removing the corresponding advertisement header from
// advertisement_read_results.
// 3. Removing the corresponding advertisement header from
// gatt_advertisements_, only if there are no remaining GATT
// advertisements related to that header.
void ClearGattAdvertisement(const BleAdvertisement& gatt_advertisement)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Handles the legacy fast advertisement or the extended fast/regular
// advertisement.
void HandleAdvertisement(
BlePeripheral peripheral,
const api::ble::BleAdvertisementData& advertisement_data)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Extracts the advertisement byte array from `AdvertisementData`.
ByteArray ExtractInterestingAdvertisementBytes(
const api::ble::BleAdvertisementData& advertisement_data)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Creates an advertisement header that's purely a hash of the fast/regular
// advertisement, since they come with no header.
BleAdvertisementHeader CreateAdvertisementHeader(
const ByteArray& advertisement_bytes);
// Returns BleAdvertisementHeader, it may be replaced if the header is mock
// and there's a psm value in advertisement.
BleAdvertisementHeader HandleRawGattAdvertisements(
BlePeripheral peripheral,
const BleAdvertisementHeader& advertisement_header,
const std::vector<const ByteArray*>& gatt_advertisement_bytes_list,
const Uuid& service_uuid) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Returns a map of service IDs to GATT advertisements who belong to a tracked
// service ID.
absl::flat_hash_map<std::string, BleAdvertisement> ParseRawGattAdvertisements(
const std::vector<const ByteArray*>& gatt_advertisement_bytes_list,
const Uuid& service_uuid) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Returns true if `new_psm` is not default value and different with
// `old_psm`.
bool ShouldNotifyForNewPsm(int old_psm, int new_psm) const;
// Returns true if two headers are not the same.
bool ShouldRemoveHeader(
const BleAdvertisementHeader& old_advertisement_header,
const BleAdvertisementHeader& new_advertisement_header);
// Returns true if it is a faked advertisement header which is purely a hash
// of the fast/regular advertisement, since they come with no header.
bool IsDummyAdvertisementHeader(
const BleAdvertisementHeader& advertisement_header);
std::optional<api::ble::BleAdvertisementData> HandleDctAdvertisement(
const api::ble::BleAdvertisementData& advertisement_data)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Handles the advertisement header for regular advertisement.
void HandleAdvertisementHeader(
BlePeripheral peripheral,
const api::ble::BleAdvertisementData& advertisement_data,
AdvertisementFetcher advertisement_fetcher)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Extracts the advertisement header byte array from `AdvertisementData`.
ByteArray ExtractAdvertisementHeaderBytes(
const api::ble::BleAdvertisementData& advertisement_data)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Returns true if the advertisement header contains a service ID we're
// tracking.
bool IsInterestingAdvertisementHeader(
const BleAdvertisementHeader& advertisement_header)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Returns true if the `advertisement_header` is allowd to callbck to fecth
// advertisement.
bool ShouldReadRawAdvertisementFromServer(
const BleAdvertisementHeader& advertisement_header)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Fetches advertisement from BLE medium if advertisement header is read in
// AdvertisementData.
//
// advertisement_fetcher : a fetcher passed from BLE medium to read the
// advertisement from BLE characteristics by GATT server.
std::vector<const ByteArray*> FetchRawAdvertisements(
BlePeripheral peripheral,
const BleAdvertisementHeader& advertisement_header,
AdvertisementFetcher advertisement_fetcher)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
void FetchRawAdvertisementsInThread(
BlePeripheral peripheral,
const BleAdvertisementHeader& advertisement_header,
AdvertisementFetcher advertisement_fetcher);
// The main loop for the GATT fetching thread.
void GattFetchingLoop();
// Updates `gatt_advertisement_infos_` map no matter whether we read a new
// GATT advertisement by the input `advertisement_header` and 'mac_address`.
void UpdateCommonStateForFoundBleAdvertisement(
const BleAdvertisementHeader& advertisement_header)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Processes on lost advertisements. Returns true when the advertisement:
// 1. Is an Instant On Lost BLE advertisement.
// 2. Matches a peripheral's advertisement hash that has previously been
// discovered.
bool HandleOnLostAdvertisementLocked(
const ::nearby::api::ble::BleAdvertisementData& advertisement_data)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Returns true if the advertisement header met the special conditions of
// a legacy device dummy advertisement.
static bool IsLegacyDeviceAdvertisementData(
const api::ble::BleAdvertisementData& advertisement_data);
// Helps to handle advertisement for Instant On lost.
bool IsInstantLostAdvertisement(
const BleAdvertisementHeader& advertisement_header)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
void AddInstantLostAdvertisement(
const BleAdvertisementHeader& advertisement_header)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
void RemoveExpiredInstantLostAdvertisements()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
Mutex mutex_;
bool is_fetching_in_thread_ = false;
bool is_read_gatt_for_extended_advertisement_enabled_ = true;
bool is_extended_advertisement_available_;
absl::Time medium_start_scanning_time_ ABSL_GUARDED_BY(mutex_) =
absl::InfiniteFuture();
// ------------ SERVICE ID MAPS ------------
// Entries in these maps all follow the same lifecycle. Entries are added in
// StartTracking, and removed in StopTracking.
absl::flat_hash_map<std::string, ServiceIdInfo> service_id_infos_
ABSL_GUARDED_BY(mutex_);
absl::flat_hash_map<std::string, std::string>
dct_service_id_hash_to_service_id_map_ ABSL_GUARDED_BY(mutex_);
// ------------ ADVERTISEMENT HEADER MAPS ------------
// Maps advertisement headers to AdvertisementReadResult. Tells us when to
// retry reading a GATT advertisement. If no entry exists for a particular
// header, we should try reading a GATT advertisement. Entries are added
// whenever a GATT advertisement read is attempted, and removed when GATT
// advertisements are lost. Entries are also removed whenever
// gattAdvertisements removes its entry.
//
// The map is also cleared whenever StartTracking is called, due to client
// changes. For example, say clients A and B start scanning and discover
// advertisements A and B (for both clients) on advertisement header 1. Then,
// A restarts scanning, causing us to clear stale advertisement A. However,
// since B was still scanning, we don't remove advertisement header 1 from the
// map. This causes us to never re-read advertisement A.
absl::flat_hash_map<BleAdvertisementHeader,
std::unique_ptr<AdvertisementReadResult>>
advertisement_read_results_ ABSL_GUARDED_BY(mutex_);
// Maps advertisement headers to a set of GATT advertisements from a single
// peripheral. Used to retrieve GATT advertisements that we need to reprocess
// every time a header is seen. Entries are added when GATT advertisements are
// read, removed when all associated GATT advertisements are lost or become
// stale, and replaced when the advertisement header is updated for a single
// remote peripheral.
absl::flat_hash_map<BleAdvertisementHeader, BleAdvertisementSet>
gatt_advertisements_ ABSL_GUARDED_BY(mutex_);
// ------------ GATT ADVERTISEMENT MAPS ------------
// Entries are added when Gatt advertisements are read, and removed when Gatt
// advertisements are lost or become stale.
absl::flat_hash_map<BleAdvertisement, GattAdvertisementInfo>
gatt_advertisement_infos_ ABSL_GUARDED_BY(mutex_);
std::unique_ptr<MultiThreadExecutor> executor_ ABSL_GUARDED_BY(mutex_) =
nullptr;
Mutex task_mutex_;
AtomicBoolean shutting_down_{false};
ConditionVariable cond_{&task_mutex_};
std::deque<GattFetchTask> gatt_fetch_tasks_ ABSL_GUARDED_BY(task_mutex_);
std::deque<GattFetchTask> gatt_extended_fetch_tasks_
ABSL_GUARDED_BY(task_mutex_);
// Maps an advertisement header's hash with the time it's reported lost.
// Ignores subsequent discovery events for the same advertisement header.
absl::flat_hash_map<std::string, absl::Time> lost_advertisment_infos_
ABSL_GUARDED_BY(mutex_);
absl::Time last_lost_info_update_time_ ABSL_GUARDED_BY(mutex_) =
absl::InfinitePast();
};
} // namespace mediums
} // namespace connections
} // namespace nearby
#endif // CORE_INTERNAL_MEDIUMS_BLE_DISCOVERED_PERIPHERAL_TRACKER_H_