blob: 646281f6718e4faee76df9c8cebd8af1467ddb9c [file] [log] [blame]
/*
*
* Copyright (c) 2022 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <app/CASESessionManager.h>
#include <app/clusters/bindings/PendingNotificationMap.h>
#include <app/server/Server.h>
#include <app/util/binding-table.h>
#include <credentials/FabricTable.h>
#include <lib/core/CHIPPersistentStorageDelegate.h>
namespace chip {
/**
* Application callback function when a cluster associated with a binding changes.
*
* The connection is managed by the stack and peer_device is guaranteed to be available.
* The application shall decide the content to be sent to the peer.
*
* For unicast bindings peer_device will be a connected peer and group will be empty.
* For multicast bindings peer_device will be nullptr.
*
* E.g. The application will send on/off commands to peer for the OnOff cluster.
*
* The handler is not allowed to hold onto the pointer to the SessionHandler that is passed in.
*/
using BoundDeviceChangedHandler = void (*)(const EmberBindingTableEntry & binding, OperationalDeviceProxy * peer_device,
void * context);
/**
* Application callback function when a context used in NotifyBoundClusterChanged will not be needed and should be
* released.
*/
using BoundDeviceContextReleaseHandler = PendingNotificationContextReleaseHandler;
struct BindingManagerInitParams
{
FabricTable * mFabricTable = nullptr;
CASESessionManager * mCASESessionManager = nullptr;
PersistentStorageDelegate * mStorage = nullptr;
};
/**
*
* The BindingManager class manages the connections for unicast bindings and notifies the application
* when a binding is ready to be communicated with.
*
* A CASE connection will be triggered when:
* - The binding cluster adds a unicast entry to the binding table.
* - A watched cluster changes with a unicast binding but we cannot find an active connection to the peer.
*
* The class uses an LRU mechanism to choose the connection to eliminate when there is no space for a new connection.
* The BindingManager class will not actively re-establish connection and will connect on-demand (when binding cluster
* or watched cluster is changed).
*
*/
class BindingManager
{
public:
BindingManager() {}
void RegisterBoundDeviceChangedHandler(BoundDeviceChangedHandler handler) { mBoundDeviceChangedHandler = handler; }
/*
* Registers handler that will be called when context used in NotifyBoundClusterChanged will not be needed and could be
* released.
*
*/
void RegisterBoundDeviceContextReleaseHandler(BoundDeviceContextReleaseHandler handler)
{
mPendingNotificationMap.RegisterPendingNotificationContextReleaseHandler(handler);
}
CHIP_ERROR Init(const BindingManagerInitParams & params);
/*
* Notifies the BindingManager that a new unicast binding is created.
*
*/
CHIP_ERROR UnicastBindingCreated(uint8_t fabricIndex, NodeId nodeId);
/*
* Notifies the BindingManager that a unicast binding is about to be removed from the given index.
*
*/
CHIP_ERROR UnicastBindingRemoved(uint8_t bindingEntryId);
/*
* Notifies the BindingManager that a fabric is removed from the device
*
*/
void FabricRemoved(FabricIndex fabricIndex);
/*
* Notify a cluster change to **all** bound devices associated with the (endpoint, cluster) tuple.
*
* For unicast bindings with an active session and multicast bindings, the BoundDeviceChangedHandler
* will be called before the function returns.
*
* For unicast bindings without an active session, the notification will be queued and a new session will
* be initiated. The BoundDeviceChangedHandler will be called once the session is established.
*
*/
CHIP_ERROR NotifyBoundClusterChanged(EndpointId endpoint, ClusterId cluster, void * context);
static BindingManager & GetInstance() { return sBindingManager; }
private:
/*
* Used when providing OnConnection/Failure callbacks to CASESessionManager when establishing session.
*
* Since the BindingManager calls EstablishConnection inside of a loop, and it is possible that the
* callback is called some time after the loop is completed, we need a separate callbacks for each
* connection we are trying to establish. Failure to provide different instances of the callback
* to CASESessionManager may result in the callback only be called for that last EstablishConnection
* that was called when it establishes the connections asynchronously.
*
*/
class ConnectionCallback
{
public:
ConnectionCallback(BindingManager & bindingManager) :
mBindingManager(bindingManager), mOnConnectedCallback(HandleDeviceConnected, this),
mOnConnectionFailureCallback(HandleDeviceConnectionFailure, this)
{}
Callback::Callback<OnDeviceConnected> * GetOnDeviceConnected() { return &mOnConnectedCallback; }
Callback::Callback<OnDeviceConnectionFailure> * GetOnDeviceConnectionFailure() { return &mOnConnectionFailureCallback; }
private:
static void HandleDeviceConnected(void * context, Messaging::ExchangeManager & exchangeMgr, SessionHandle & sessionHandle)
{
ConnectionCallback * _this = static_cast<ConnectionCallback *>(context);
_this->mBindingManager.HandleDeviceConnected(exchangeMgr, sessionHandle);
Platform::Delete(_this);
}
static void HandleDeviceConnectionFailure(void * context, const ScopedNodeId & peerId, CHIP_ERROR error)
{
ConnectionCallback * _this = static_cast<ConnectionCallback *>(context);
_this->mBindingManager.HandleDeviceConnectionFailure(peerId, error);
Platform::Delete(_this);
}
BindingManager & mBindingManager;
Callback::Callback<OnDeviceConnected> mOnConnectedCallback;
Callback::Callback<OnDeviceConnectionFailure> mOnConnectionFailureCallback;
};
static BindingManager sBindingManager;
CHIP_ERROR EstablishConnection(const ScopedNodeId & nodeId);
PendingNotificationMap mPendingNotificationMap;
BoundDeviceChangedHandler mBoundDeviceChangedHandler;
BindingManagerInitParams mInitParams;
void HandleDeviceConnected(Messaging::ExchangeManager & exchangeMgr, SessionHandle & sessionHandle);
void HandleDeviceConnectionFailure(const ScopedNodeId & peerId, CHIP_ERROR error);
// Used to keep track of synchronous failures from FindOrEstablishSession.
CHIP_ERROR mLastSessionEstablishmentError;
};
} // namespace chip