blob: e83f4bac87dc3f510d614faae15f1f375af3554c [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_GAP_BREDR_DISCOVERY_MANAGER_H_
#define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_GAP_BREDR_DISCOVERY_MANAGER_H_
#include <fbl/macros.h>
#include <lib/async/dispatcher.h>
#include <lib/fit/function.h>
#include <queue>
#include <unordered_set>
#include "src/connectivity/bluetooth/core/bt-host/gap/peer.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/command_channel.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/control_packets.h"
#include "src/lib/fxl/memory/weak_ptr.h"
namespace bt {
namespace hci {
class Transport;
} // namespace hci
namespace gap {
class BrEdrDiscoveryManager;
class PeerCache;
// BrEdrDiscoveryManager implements discovery for BR/EDR peers. We provide a
// mechanism for multiple clients to simultaneously request discovery. Peers
// discovered will be added to the PeerCache.
//
// Only one instance of BrEdrDiscoveryManager should be created for a bt-host.
//
// Request discovery using RequestDiscovery() which will provide a
// BrEdrDiscoverySession object in the |callback| when discovery is started.
// Ownership of this session is passed to the caller; when no sessions exist,
// discovery is halted.
//
// TODO(jamuraa): Name resolution should also happen here. (NET-509)
//
// This class is not thread-safe, BrEdrDiscoverySessions should be created and
// accessed on the same thread the BrEdrDiscoveryManager is created.
class BrEdrDiscoverySession final {
public:
// Destroying a session instance ends this discovery session. Discovery may
// continue if other clients have started discovery sesisons.
~BrEdrDiscoverySession();
// Set a result callback that will be notified whenever a result is returned
// from the controller. You will get duplicate results when using this
// method.
// Prefer PeerCache.set_peer_updated_callback() instead.
using PeerFoundCallback = fit::function<void(const Peer& peer)>;
void set_result_callback(PeerFoundCallback callback) {
peer_found_callback_ = std::move(callback);
}
// Set a callback to be notified if the session becomes inactive because
// of internal errors.
void set_error_callback(fit::closure callback) {
error_callback_ = std::move(callback);
}
private:
friend class BrEdrDiscoveryManager;
// Used by the BrEdrDiscoveryManager to create a session.
explicit BrEdrDiscoverySession(fxl::WeakPtr<BrEdrDiscoveryManager> manager);
// Called by the BrEdrDiscoveryManager when a peer report is found.
void NotifyDiscoveryResult(const Peer& peer) const;
// Marks this session as ended because of an error.
void NotifyError() const;
fxl::WeakPtr<BrEdrDiscoveryManager> manager_;
fit::closure error_callback_;
PeerFoundCallback peer_found_callback_;
fxl::ThreadChecker thread_checker_;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(BrEdrDiscoverySession);
};
class BrEdrDiscoverableSession final {
public:
// Destroying a session instance relinquishes the request.
// The peer may still be discoverable if others are requesting so.
~BrEdrDiscoverableSession();
private:
friend class BrEdrDiscoveryManager;
// Used by the BrEdrDiscoveryManager to create a session.
explicit BrEdrDiscoverableSession(
fxl::WeakPtr<BrEdrDiscoveryManager> manager);
fxl::WeakPtr<BrEdrDiscoveryManager> manager_;
fxl::ThreadChecker thread_checker_;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(BrEdrDiscoverableSession);
};
class BrEdrDiscoveryManager final {
public:
// |peer_cache| MUST out-live this BrEdrDiscoveryManager.
BrEdrDiscoveryManager(fxl::RefPtr<hci::Transport> hci, hci::InquiryMode mode,
PeerCache* peer_cache);
~BrEdrDiscoveryManager();
// Starts discovery and reports the status via |callback|. If discovery has
// been successfully started, the callback will receive a session object that
// it owns. If no sessions are owned, peer discovery is stopped.
using DiscoveryCallback =
fit::function<void(const hci::Status& status,
std::unique_ptr<BrEdrDiscoverySession> session)>;
void RequestDiscovery(DiscoveryCallback callback);
// Returns whether a discovery session is active.
bool discovering() const { return !discovering_.empty(); }
// Requests this device be discoverable. We are discoverable as long as
// anyone holds a discoverable session.
using DiscoverableCallback =
fit::function<void(const hci::Status& status,
std::unique_ptr<BrEdrDiscoverableSession> session)>;
void RequestDiscoverable(DiscoverableCallback callback);
bool discoverable() const { return !discoverable_.empty(); }
private:
friend class BrEdrDiscoverySession;
friend class BrEdrDiscoverableSession;
// Starts the inquiry procedure if any sessions exist.
void MaybeStartInquiry();
// Stops the inquiry procedure.
void StopInquiry();
// Used to receive Inquiry Results.
void InquiryResult(const hci::EventPacket& event);
// Used to receive Inquiry Results.
void ExtendedInquiryResult(const hci::EventPacket& event);
// Creates and stores a new session object and returns it.
std::unique_ptr<BrEdrDiscoverySession> AddDiscoverySession();
// Removes |session_| from the active sessions.
void RemoveDiscoverySession(BrEdrDiscoverySession* session);
// Invalidates all discovery sessions, invoking their error callbacks.
void InvalidateDiscoverySessions();
// Sets the Inquiry Scan to the correct state given discoverable sessions,
// pending requests and the current scan state.
void SetInquiryScan();
// Writes the Inquiry Scan Settings to the controller.
// If |interlaced| is true, and the controller does not supoport interlaces
// inquiry scan mode, standard mode is used.
void WriteInquiryScanSettings(uint16_t interval, uint16_t window,
bool interlaced);
// Creates and stores a new session object and returns it.
std::unique_ptr<BrEdrDiscoverableSession> AddDiscoverableSession();
// Removes |session_| from the active sessions.
void RemoveDiscoverableSession(BrEdrDiscoverableSession* session);
// Sends a RemoteNameRequest to the peer with |id|.
void RequestPeerName(PeerId id);
// The HCI Transport
fxl::RefPtr<hci::Transport> hci_;
// The dispatcher that we use for invoking callbacks asynchronously.
async_dispatcher_t* dispatcher_;
// Peer cache to use.
// We hold a raw pointer is because it must out-live us.
PeerCache* cache_;
// The list of discovering sessions. We store raw pointers here as we
// don't own the sessions. Sessions notify us when they are destroyed to
// maintain this list.
//
// When |discovering_| becomes empty then scanning is stopped.
std::unordered_set<BrEdrDiscoverySession*> discovering_;
// Sessions that have been removed but are still active.
// Inquiry persists until we receive a Inquiry Complete event.
// TODO(NET-619): we should not need these once we can Inquiry Cancel.
std::unordered_set<BrEdrDiscoverySession*> zombie_discovering_;
// The set of peers that we have pending name requests for.
std::unordered_set<PeerId> requesting_names_;
// The set of callbacks that are waiting on inquiry to start.
std::queue<DiscoveryCallback> pending_discovery_;
// The list of discoverable sessions. We store raw pointers here as we
// don't own the sessions. Sessions notify us when they are destroyed to
// maintain this list.
//
// When |discoverable_| becomes empty then inquiry scan is disabled.
std::unordered_set<BrEdrDiscoverableSession*> discoverable_;
// The set of callbacks that are waiting on inquiry scan to be active.
std::queue<hci::StatusCallback> pending_discoverable_;
// The Handler IDs of the event handlers for inquiry results.
hci::CommandChannel::EventHandlerId result_handler_id_;
hci::CommandChannel::EventHandlerId rssi_handler_id_;
hci::CommandChannel::EventHandlerId eir_handler_id_;
// The inquiry mode that we should use.
hci::InquiryMode desired_inquiry_mode_;
// The current inquiry mode.
hci::InquiryMode current_inquiry_mode_;
fxl::ThreadChecker thread_checker_;
fxl::WeakPtrFactory<BrEdrDiscoveryManager> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(BrEdrDiscoveryManager);
};
} // namespace gap
} // namespace bt
#endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_GAP_BREDR_DISCOVERY_MANAGER_H_