1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #pragma once
16 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
17 
18 namespace bt::hci {
19 
20 // Extended advertising HCI commands refer to a particular advertising set via
21 // an AdvertisingHandle. An AdvertisingHandle is an eight bit unsigned integer
22 // that uniquely identifies an advertising set and vice versa. This means that
23 // we frequently need to convert between a DeviceAddress and an
24 // AdvertisingHandle. AdvertisingHandleMap provides a 1:1 bidirectional mapping
25 // between a DeviceAddress and an AdvertisingHandle, allocating the next
26 // available AdvertisingHandle to new DeviceAddresses.
27 //
28 // When using extended advertising, there are two types of advertising PDU
29 // formats available: legacy PDUs and extended PDUs. Legacy advertising PDUs are
30 // currently the most widely compatible type, discoverable by devices deployed
31 // prior to the adoption of Bluetooth 5.0. Devices deployed more recently are
32 // also able to discover this type of advertising packet. Conversely, extended
33 // advertising PDUs are a newer format that offers a number of improvements,
34 // including the ability to advertise larger amounts of data. However, devices
35 // not specifically scanning for them or who are running on an older version of
36 // Bluetooth (pre-5.0) won't be able to see them.
37 //
38 // When advertising using extended advertising PDUs, users often choose to emit
39 // legacy advertising PDUs as well in order to maintain backwards compatibility
40 // with older Bluetooth devices. As such, advertisers such as
41 // ExtendedLowEnergyAdvertiser may need to track two real AdvertisingHandles
42 // for each logical advertisement, one for legacy advertising PDUs and one for
43 // extended advertising PDUs. Along with DeviceAddress, AdvertisingHandleMap
44 // tracks whether the mapping is for an extended PDU or a legacy PDU.
45 //
46 // Users shouldn't rely on any particular ordering of the next available
47 // mapping. Any available AdvertisingHandle may be used.
48 class AdvertisingHandleMap {
49  public:
50   // Instantiate an AdvertisingHandleMap. The capacity parameter specifies the
51   // maximum number of mappings that this instance will support. Setting the
52   // capacity also restricts the range of advertising handles
53   // AdvertisingHandleMap will return: [0, capacity).
54   explicit AdvertisingHandleMap(
55       uint8_t capacity = hci_spec::kMaxAdvertisingHandle + 1)
capacity_(capacity)56       : capacity_(capacity) {}
57 
58   // Convert a DeviceAddress to an AdvertisingHandle, creating the mapping if it
59   // doesn't already exist. The conversion may fail if there are already
60   // hci_spec::kMaxAdvertisingHandles in the container.
61   std::optional<hci_spec::AdvertisingHandle> MapHandle(
62       const DeviceAddress& address, bool extended_pdu);
63 
64   // Convert a DeviceAddress to an AdvertisingHandle. The conversion may fail if
65   // there is no AdvertisingHandle currently mapping to the provided device
66   // address.
67   std::optional<hci_spec::AdvertisingHandle> GetHandle(
68       const DeviceAddress& address, bool extended_pdu) const;
69 
70   // Convert an AdvertisingHandle to a DeviceAddress. The conversion may fail if
71   // there is no DeviceAddress currently mapping to the provided handle.
72   std::optional<DeviceAddress> GetAddress(
73       hci_spec::AdvertisingHandle handle) const;
74 
75   // Remove the mapping between an AdvertisingHandle and the DeviceAddress it
76   // maps to. The container may reuse the AdvertisingHandle for other
77   // DeviceAddresses in the future. Immediate future calls to GetAddress(...)
78   // with the same AdvertisingHandle will fail because the mapping no longer
79   // exists. Immediate future calls to GetHandle(...) will result in a new
80   // mapping with a new AdvertisingHandle.
81   //
82   // If the given handle doesn't map to any (DeviceAddress, bool) tuple, this
83   // function does nothing.
84   void RemoveHandle(hci_spec::AdvertisingHandle handle);
85 
86   // Remove the mapping between a DeviceAddress and the AdvertisingHandle it
87   // maps to. The container may reuse the AdvertisingHandle for other
88   // DeviceAddresses in the future. Immediate future calls to GetAddress(...)
89   // with the preivously mapped AdvertisingHandle will fail because the mapping
90   // no longer exists. Immediate future calls to GetHandle(...) will result in a
91   // new mapping with a new AdvertisingHandle.
92   //
93   // If the given (DeviceAddress, bool) tuple doesn't map to any
94   // AdvertisingHandle, this function does nothing.
95   void RemoveAddress(const DeviceAddress& address, bool extended_pdu);
96 
97   // Get the maximum number of mappings the AdvertisingHandleMap will support.
capacity()98   uint8_t capacity() const { return capacity_; }
99 
100   // Retrieve the advertising handle that was most recently generated. This
101   // function is primarily used by unit tests so as to avoid hardcoding values
102   // or making assumptions about the starting point or ordering of advertising
103   // handle generation.
104   std::optional<hci_spec::AdvertisingHandle> LastUsedHandleForTesting() const;
105 
106   // Get the number of unique mappings in the container
107   std::size_t Size() const;
108 
109   // Return true if the container has no mappings, false otherwise
110   bool Empty() const;
111 
112   // Remove all mappings in the container
113   void Clear();
114 
115  private:
116   // Although not in the range of valid advertising handles (0x00 to 0xEF),
117   // kStartHandle is chosen to be 0xFF because adding one to it will overflow to
118   // 0, the first valid advertising handle.
119   constexpr static hci_spec::AdvertisingHandle kStartHandle = 0xFF;
120 
121   // Tracks the maximum number of elements that can be stored in this container.
122   //
123   // NOTE: AdvertisingHandles have a range of [0, capacity_). This value isn't
124   // set using default member initialization because it is set within the
125   // constructor itself.
126   uint8_t capacity_;
127 
128   // Generate the next valid, available, and within range AdvertisingHandle.
129   // This function may fail if there are already
130   // hci_spec::kMaxAdvertisingHandles in the container: there are no more valid
131   // AdvertisingHandles.
132   std::optional<hci_spec::AdvertisingHandle> NextHandle();
133 
134   // The last generated advertising handle used as a hint to generate the next
135   // handle; defaults to kStartHandle if no handles have been generated yet.
136   hci_spec::AdvertisingHandle last_handle_ = kStartHandle;
137 
138   struct TupleKeyHasher {
operatorTupleKeyHasher139     size_t operator()(const std::tuple<DeviceAddress, bool>& t) const {
140       std::hash<DeviceAddress> device_address_hasher;
141       std::hash<bool> bool_hasher;
142       const auto& [address, extended_pdu] = t;
143       return device_address_hasher(address) ^ bool_hasher(extended_pdu);
144     }
145   };
146 
147   std::unordered_map<std::tuple<DeviceAddress, bool>,
148                      hci_spec::AdvertisingHandle,
149                      TupleKeyHasher>
150       addr_to_handle_;
151 
152   std::unordered_map<hci_spec::AdvertisingHandle,
153                      std::tuple<DeviceAddress, bool>>
154       handle_to_addr_;
155 };
156 
157 }  // namespace bt::hci
158