xref: /aosp_15_r20/external/pigweed/pw_bluetooth/public/pw_bluetooth/low_energy/central2.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2024 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 #pragma once
15 
16 #include <memory>
17 #include <optional>
18 
19 #include "pw_async2/once_sender.h"
20 #include "pw_bluetooth/internal/raii_ptr.h"
21 #include "pw_bluetooth/low_energy/advertising_data.h"
22 #include "pw_bluetooth/low_energy/connection2.h"
23 #include "pw_bluetooth/low_energy/phy.h"
24 #include "pw_bluetooth/types.h"
25 #include "pw_chrono/system_clock.h"
26 #include "pw_containers/vector.h"
27 #include "pw_function/function.h"
28 #include "pw_result/expected.h"
29 
30 namespace pw::bluetooth::low_energy {
31 
32 /// Represents the LE central role. Used to scan and connect to peripherals.
33 class Central2 {
34  public:
35   /// Filter parameters for use during a scan. A discovered peer only matches
36   /// the filter if it satisfies all of the present filter parameters.
37   struct ScanFilter {
38     /// Filter based on advertised service UUID.
39     std::optional<Uuid> service_uuid;
40 
41     /// Filter based on service data containing the given UUID.
42     std::optional<Uuid> service_data_uuid;
43 
44     /// Filter based on a manufacturer identifier present in the manufacturer
45     /// data. If this filter parameter is set, then the advertising payload must
46     /// contain manufacturer-specific data with the provided company identifier
47     /// to satisfy this filter. Manufacturer identifiers can be found at
48     /// [Assigned
49     /// Numbers](https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers/).
50     std::optional<uint16_t> manufacturer_id;
51 
52     /// Filter based on whether or not a device is connectable. For example, a
53     /// client that is only interested in peripherals that it can connect to can
54     /// set this to true. Similarly a client can scan only for broadcasters by
55     /// setting this to false.
56     std::optional<bool> connectable;
57 
58     /// Filter results based on a portion of the advertised device name.
59     /// Substring matches are allowed.
60     /// The name length must be at most `pw::bluetooth::kMaxDeviceNameLength`.
61     std::optional<std::string_view> name;
62 
63     /// Filter results based on the path loss of the radio wave. A device that
64     /// matches this filter must satisfy the following:
65     ///   1. Radio transmission power level and received signal strength must be
66     ///      available for the path loss calculation.
67     ///   2. The calculated path loss value must be less than, or equal to,
68     ///      `max_path_loss`.
69     ///
70     /// @note This field is calculated using the RSSI and TX Power information
71     /// obtained from advertising and scan response data during a scan
72     /// procedure. It should NOT be confused with information for an active
73     /// connection obtained using the "Path Loss Reporting" feature.
74     std::optional<uint8_t> max_path_loss;
75   };
76 
77   enum class ScanType : uint8_t {
78     kPassive,
79     /// Send scanning PDUs with the public address.
80     kActiveUsePublicAddress,
81     /// Send scanning PDUs with the random address.
82     kActiveUseRandomAddress,
83     /// Send scanning PDUs with a generated Resolvable Private Address.
84     kActiveUseResolvablePrivateAddress,
85   };
86 
87   /// Parameters used during a scan.
88   struct ScanOptions {
89     /// List of filters for use during a scan. A peripheral that satisfies any
90     /// of these filters will be reported. At least 1 filter must be specified.
91     /// While not recommended, clients that require that all peripherals be
92     /// reported can specify an empty filter.
93     /// The span memory must only be valid until the call to Scan() ends.
94     pw::span<const ScanFilter> filters;
95 
96     /// The time interval between scans.
97     /// - Time = N * 0.625ms
98     /// - Range: 0x0004 (2.5ms) - 10.24s (0x4000)
99     uint16_t interval;
100 
101     /// The duration of the scan. The window must be less than or equal to the
102     /// interval.
103     /// - Time = N * 0.625ms
104     /// - Range: 0x0004 (2.5ms) - 10.24s (0x4000)
105     uint16_t window;
106 
107     /// Specifies whether to send scan requests, and if so, what type of address
108     /// to use in scan requests.
109     ScanType scan_type;
110 
111     /// A bitmask of the PHYs to scan with. Only the 1Megabit and LeCoded PHYs
112     /// are supported.
113     Phy phys = Phy::k1Megabit;
114   };
115 
116   struct ScanResult {
117     /// Uniquely identifies this peer on the current system.
118     PeerId peer_id;
119 
120     /// Whether or not this peer is connectable. Non-connectable peers are
121     /// typically in the LE broadcaster role.
122     bool connectable;
123 
124     /// The last observed signal strength of this peer. This field is only
125     /// present for a peer that is broadcasting. The RSSI can be stale if the
126     /// peer has not been advertising.
127     ///
128     /// @note This field should NOT be confused with the "connection RSSI" of a
129     /// peer that is currently connected to the system.
130     std::optional<uint8_t> rssi;
131 
132     /// This contains the advertising data last received from the peer.
133     pw::multibuf::MultiBuf data;
134 
135     /// The name of this peer. The name is often obtained during a scan
136     /// procedure and can get updated during the name discovery procedure
137     /// following a connection.
138     ///
139     /// This field is present if the name is known.
140     std::optional<InlineString<22>> name;
141 
142     /// Timestamp of when the information in this `ScanResult` was last updated.
143     chrono::SystemClock::time_point last_updated;
144   };
145 
146   /// Represents an ongoing LE scan.
147   class ScanHandle {
148    public:
149     virtual ~ScanHandle() = 0;
150 
151     /// Returns the next `ScanResult` if available. Otherwise, invokes
152     /// `cx.waker()` when a `ScanResult` is available. Only one waker is
153     /// supported at a time.
154     /// @return @rst
155     ///
156     /// .. pw-status-codes::
157     ///
158     ///    OK: ScanResult was returned.
159     ///
160     ///    CANCELLED: An internal error occurred and the scan was cancelled.
161     ///
162     /// @endrst
163     virtual async2::Poll<pw::Result<ScanResult>> PendResult(
164         async2::Context& cx) = 0;
165 
166    private:
167     /// Stop the current scan. This method is called by the ~ScanHandle::Ptr()
168     /// when it goes out of scope, the API client should never call this method.
169     virtual void StopScan() = 0;
170 
171    public:
172     /// Movable ScanHandle smart pointer. The controller will continue scanning
173     /// until the ScanHandle::Ptr is destroyed.
174     using Ptr = internal::RaiiPtr<ScanHandle, &ScanHandle::StopScan>;
175   };
176 
177   /// Possible errors returned by `Connect`.
178   enum class ConnectError : uint8_t {
179     /// The peer ID is unknown.
180     kUnknownPeer,
181 
182     /// The `ConnectionOptions` were invalid.
183     kInvalidOptions,
184 
185     /// A connection to the peer already exists.
186     kAlreadyExists,
187 
188     /// The connection procedure failed at the link layer or timed out
189     /// immediately after being established. A "could not be established" error
190     /// was reported by the controller. This may be due to interference.
191     kCouldNotBeEstablished,
192   };
193 
194   enum class StartScanError : uint8_t {
195     /// A scan is already in progress. Only 1 scan may be active at a time.
196     kScanInProgress,
197     /// Some of the scan options are invalid.
198     kInvalidParameters,
199     /// An internal error occurred and a scan could not be started.
200     kInternal,
201   };
202 
203   /// The result type returned by Connect().
204   using ConnectResult = pw::expected<Connection2::Ptr, ConnectError>;
205 
206   /// The result type returned by Scan().
207   using ScanStartResult = pw::expected<ScanHandle::Ptr, StartScanError>;
208 
209   virtual ~Central2() = default;
210 
211   /// Connect to the peer with the given identifier.
212   ///
213   /// The returned `Connection2` represents the client's interest in the LE
214   /// connection to the peer. Destroying all `Connection2` instances for a peer
215   /// will disconnect from the peer.
216   ///
217   /// The `Connection` will be closed by the system if the connection to the
218   /// peer is lost or an error occurs, as indicated by `Connection.OnError`.
219   ///
220   /// @param peer_id Identifier of the peer to initiate a connection to.
221   /// @param options Options used to configure the connection.
222   /// @return Returns a result when a connection is successfully established, or
223   /// an error occurs.
224   ///
225   /// Possible errors are documented in `ConnectError`.
226   virtual async2::OnceReceiver<ConnectResult> Connect(
227       PeerId peer_id, Connection2::ConnectionOptions options) = 0;
228 
229   /// Scans for nearby LE peripherals and broadcasters. The lifetime of the scan
230   /// session is tied to the returned `ScanHandle` object in `ScanStartResult`.
231   /// Once a scan is started, `ScanHandle::PendResult` can be called to get scan
232   /// results. Only 1 scan may be active at a time.
233   ///
234   /// @param options Options used to configure the scan session. These options
235   ///     are *suggestions* only, and the implementation may use different
236   ///     parameters to meet power or radio requirements.
237   /// @return Returns a `ScanHandle` object if the scan successfully
238   /// starts, or a `ScanError` otherwise.
239   /// `ScanHandle::PendResult` can be called to get `ScanResult`s for LE
240   /// peers that satisfy the filters indicated in `options`. The initial results
241   /// may report recently discovered peers. Subsequent results will be reported
242   /// only when peers have been scanned or updated since the last call.
243   virtual async2::OnceReceiver<ScanStartResult> Scan(ScanOptions options) = 0;
244 };
245 
246 }  // namespace pw::bluetooth::low_energy
247