blob: 9edfe9a913492ab8b7e1b837cdaab6524dea65f6 [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_L2CAP_DYNAMIC_CHANNEL_REGISTRY_H_
#define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_L2CAP_DYNAMIC_CHANNEL_REGISTRY_H_
#include <lib/fit/function.h>
#include <random>
#include <unordered_map>
#include "src/connectivity/bluetooth/core/bt-host/common/random.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/dynamic_channel.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/l2cap_defs.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/signaling_channel.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/types.h"
#include "src/lib/fxl/memory/weak_ptr.h"
namespace bt::l2cap::internal {
// Base class for registries of dynamic L2CAP channels. It serves both as the
// factory and owner of dynamic channels created on a logical link, including
// assigning and tracking dynamic channel IDs.
//
// Registry entries are DynamicChannels that do not implement the l2cap::Channel
// interface used for user data transfer.
//
// This class is not thread-safe and is intended to be created and run on the
// L2CAP thread for each logical link connected.
class DynamicChannelRegistry {
public:
// Used to pass an optional channel to clients of the registry. |channel| may
// be nullptr upon failure to open. Otherwise, it points to an instance owned
// by the registry and should not be retained by the callee.
using DynamicChannelCallback = fit::function<void(const DynamicChannel* channel)>;
using ServiceInfo = ServiceInfo<DynamicChannelCallback>;
// Used to query the upper layers for the presence of a service that is
// accepting channels. If the service exists, it should return a callback
// that accepts the inbound dynamic channel opened.
using ServiceRequestCallback = fit::function<std::optional<ServiceInfo>(PSM psm)>;
virtual ~DynamicChannelRegistry();
// Create and connect a dynamic channel. The result will be returned by
// calling |open_cb| on the L2CAP thread the channel is ready for data
// transfer, with a nullptr if unsuccessful. The DynamicChannel passed will
// contain the local and remote channel IDs to be used for user data transfer
// over the new channel. Preferred channel parameters can be set in |params|.
void OpenOutbound(PSM psm, ChannelParameters params, DynamicChannelCallback open_cb);
// Disconnect and remove the channel identified by |local_cid|. After this call completes,
// incoming PDUs with |local_cid| should be discarded as in error or considered to belong to a
// subsequent channel with that ID. Any outbound PDUs passed to the Channel interface for this
// channel should be discarded. When the close operation completes, |close_cb| will be called, the
// internal channel will be destroyed, and |local_cid| may be recycled for another dynamic
// channel. |close_cb| will be called immediately if the channel doesn't exist.
void CloseChannel(ChannelId local_cid, fit::closure close_cb);
protected:
// |max_num_channels| is the number of dynamic channel IDs that can be
// allocated on this link, and must be non-zero and less than 65473.
//
// |close_cb| will be called upon a remote-initiated closure of an open
// channel. The registry's internal channel is passed as a parameter, and it
// will be closed for user data transfer before the callback fires. When the
// callback returns, the channel is destroyed and its ID may be recycled for
// another dynamic channel. Channels that fail to open due to error or are
// closed using CloseChannel will not trigger this callback.
//
// |service_request_cb| will be called upon remote-initiated channel requests.
// For services accepting channels, it shall return a callback to accept the
// opened channel, which only be called if the channel successfully opens. To
// deny the channel creation, |service_request_cb| should return a nullptr.
//
// If |random_channel_ids| is true then the channel IDs assigned will be randomized.
// Otherwise, they will be assigned starting at the lowest available dynamic channel id.
DynamicChannelRegistry(uint16_t max_num_channels, DynamicChannelCallback close_cb,
ServiceRequestCallback service_request_cb, bool random_channel_ids);
// Factory method for a DynamicChannel implementation that represents an
// outbound channel with an endpoint on this device identified by |local_cid|.
virtual DynamicChannelPtr MakeOutbound(PSM psm, ChannelId local_cid,
ChannelParameters params) = 0;
// Factory method for a DynamicChannel implementation that represents an
// inbound channel from a remote endpoint identified by |remote_cid| to an
// endpoint on this device identified by |local_cid|.
virtual DynamicChannelPtr MakeInbound(PSM psm, ChannelId local_cid, ChannelId remote_cid,
ChannelParameters params) = 0;
// Open an inbound channel for a service |psm| from the remote endpoint
// identified by |remote_cid| to the local endpoint by |local_cid|.
DynamicChannel* RequestService(PSM psm, ChannelId local_cid, ChannelId remote_cid);
// In the range starting at kFirstDynamicChannelId with |max_num_channels_|, pick a dynamic
// channel ID that is available on this link.
// Returns kInvalidChannelId if all IDs have been exhausted.
ChannelId FindAvailableChannelId();
// Return the number of alive channels on this link.
size_t AliveChannelCount() const;
// Returns null if not found. Can be downcast to the derived DynamicChannel
// created by MakeOutbound.
DynamicChannel* FindChannelByLocalId(ChannelId local_cid) const;
// Searches for alive dynamic channel with given remote channel id.
// Returns null if not found.
DynamicChannel* FindChannelByRemoteId(ChannelId remote_cid) const;
// Iterates over all channels, running |f| on each entry synchronously.
void ForEach(fit::function<void(DynamicChannel*)> f) const;
fxl::WeakPtr<DynamicChannelRegistry> GetWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); }
private:
friend class DynamicChannel;
// Open a newly-created channel. If |pass_failed| is true, always invoke
// |open_cb| with the result of the operation, including with nullptr if the
// channel failed to open. Otherwise if |pass_failed| is false, only invoke
// |open_cb| for successfully-opened channels.
void ActivateChannel(DynamicChannel* channel, DynamicChannelCallback open_cb, bool pass_failed);
// Signal a remote-initiated closure of a channel owned by this registry, then
// delete it. |close_cb_| is invoked if the channel was ever open (see
// |DynamicChannel::opened|).
void OnChannelDisconnected(DynamicChannel* channel);
// Delete a channel owned by this registry. Then, after this returns,
// |local_cid| may be recycled for another dynamic channel.
void RemoveChannel(DynamicChannel* channel);
// Greatest dynamic channel ID that can be assigned on the kind of logical
// link associated to this registry.
const uint16_t max_num_channels_;
// Called only for channels that were already open (see
// |DynamicChannel::opened|).
DynamicChannelCallback close_cb_;
ServiceRequestCallback service_request_cb_;
// Maps local CIDs to alive dynamic channels on this logical link.
using ChannelMap = std::unordered_map<ChannelId, DynamicChannelPtr>;
ChannelMap channels_;
std::optional<std::default_random_engine> rng_;
fxl::WeakPtrFactory<DynamicChannelRegistry> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(DynamicChannelRegistry);
};
} // namespace bt::l2cap::internal
#endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_L2CAP_DYNAMIC_CHANNEL_REGISTRY_H_