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 <cpp-string/string_printf.h>
17 
18 #include <string>
19 
20 #include "pw_bluetooth_sapphire/internal/host/hci/connection.h"
21 #include "pw_bluetooth_sapphire/internal/host/sm/pairing_channel.h"
22 #include "pw_bluetooth_sapphire/internal/host/sm/types.h"
23 
24 namespace bt::sm {
25 
26 // Abstract class representing one of the four in-progress phases of pairing
27 // described in Vol. 3 Part H 2.1.
28 //
29 // After a PairingPhase fails (i.e. through calling OnFailure), it is invalid to
30 // make any further method calls on the phase.
31 using PairingChannelHandler = PairingChannel::Handler;
32 class PairingPhase {
33  public:
34   // Interface for notifying the owner of the phase object.
35   class Listener {
36    public:
37     virtual ~Listener() = default;
38 
39     // Polls for the local identity information, which must be handled by
40     // another component of the Bluetooth stack. Returns std::nullopt if no
41     // local identity info is available.
42     virtual std::optional<IdentityInfo> OnIdentityRequest() = 0;
43 
44     using ConfirmCallback = fit::function<void(bool confirm)>;
45     virtual void ConfirmPairing(ConfirmCallback confirm) = 0;
46 
47     // Show the user the 6-digit |passkey| that should be compared to the peer's
48     // passkey or entered into the peer. |confirm| may be called to accept a
49     // comparison or to reject the pairing.
50     virtual void DisplayPasskey(uint32_t passkey,
51                                 Delegate::DisplayMethod method,
52                                 ConfirmCallback confirm) = 0;
53 
54     // Ask the user to enter a 6-digit passkey or reject pairing. Reports the
55     // result by invoking |respond| with |passkey| - a negative value of
56     // |passkey| indicates entry failed.
57     // TODO(fxbug.dev/42126992): Use an optional to convey success/failure
58     // instead of the signedness of passkey.
59     using PasskeyResponseCallback = fit::function<void(int64_t passkey)>;
60     virtual void RequestPasskey(PasskeyResponseCallback respond) = 0;
61 
62     // Called when an on-going pairing procedure terminates with an error. This
63     // method should destroy the Phase that calls it.
64     virtual void OnPairingFailed(Error error) = 0;
65 
66     using WeakPtr = WeakSelf<Listener>::WeakPtr;
67   };
68 
69   virtual ~PairingPhase() = default;
70 
71   // Kick off the state machine for the concrete PairingPhase.
72   virtual void Start() = 0;
73 
74   // Return a representation of the current state of the pairing phase for
75   // display purposes.
ToString()76   std::string ToString() {
77     return bt_lib_cpp_string::StringPrintf(
78         "%s Role: SMP %s%s",
79         ToStringInternal().c_str(),
80         role_ == Role::kInitiator ? "initiator" : "responder",
81         has_failed_ ? " - pairing has failed" : "");
82   }
83 
84   // Cleans up pairing state and and invokes Listener::OnPairingFailed.
85   void OnFailure(Error error);
86 
87   // Ends the current pairing procedure unsuccessfully with |ecode| as the
88   // reason, and calls OnFailure.
89   void Abort(ErrorCode ecode);
90 
role()91   Role role() const { return role_; }
92 
93  protected:
94   // Protected constructor as PairingPhases should not be created directly.
95   // Initializes this PairingPhase with the following parameters:
96   //   - |chan|: The L2CAP SMP fixed channel.
97   //   - |listener|: The class that will handle higher-level requests from the
98   //   current phase.
99   //   - |role|: The local connection role.
100   PairingPhase(PairingChannel::WeakPtr chan,
101                Listener::WeakPtr listener,
102                Role role);
103 
104   // For derived final classes to implement PairingChannel::Handler:
105   void HandleChannelClosed();
106 
sm_chan()107   PairingChannel& sm_chan() const {
108     PW_CHECK(sm_chan_.is_alive());
109     return sm_chan_.get();
110   }
111 
listener()112   Listener::WeakPtr listener() const { return listener_; }
113 
114   // Concrete classes of PairingPhase must be PairingChannelHandlers and call
115   // this function when the phase is ready to handle requests.
116   void SetPairingChannelHandler(PairingChannelHandler& self);
117 
118   // Invalidate handling requests to this phase.  This function should only be
119   // called once during destruction of the phase.
120   void InvalidatePairingChannelHandler();
121 
122   // To BT_ASSERT that methods are not called on a phase that has already
123   // failed.
has_failed()124   bool has_failed() const { return has_failed_; }
125 
126   // For subclasses to provide more detailed inspect information.
127   virtual std::string ToStringInternal() = 0;
128 
129  private:
130   PairingChannel::WeakPtr sm_chan_;
131   Listener::WeakPtr listener_;
132   Role role_;
133   bool has_failed_;
134   WeakSelf<PairingChannel::Handler> weak_channel_handler_;
135   BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(PairingPhase);
136 };
137 
138 }  // namespace bt::sm
139