Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[bindings] Save binding table in persistent storage #15449

Merged
merged 6 commits into from
Mar 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "app/server/Server.h"
#include "controller/InvokeInteraction.h"
#include "lib/core/CHIPError.h"
#include "platform/CHIPDeviceLayer.h"

#if defined(ENABLE_CHIP_SHELL)
#include "lib/shell/Engine.h"
Expand Down Expand Up @@ -102,10 +103,21 @@ static void BoundDeviceChangedHandler(const EmberBindingTableEntry & binding, ch
}
}

CHIP_ERROR InitBindingHandlers()
static void InitBindingHandlerInternal(intptr_t arg)
{
chip::BindingManager::GetInstance().SetAppServer(&chip::Server::GetInstance());
auto & server = chip::Server::GetInstance();
chip::BindingManager::GetInstance().Init(
{ &server.GetFabricTable(), server.GetCASESessionManager(), &server.GetPersistentStorage() });
chip::BindingManager::GetInstance().RegisterBoundDeviceChangedHandler(BoundDeviceChangedHandler);
}

CHIP_ERROR InitBindingHandlers()
{
// The initialization of binding manager will try establishing connection with unicast peers
gjc13 marked this conversation as resolved.
Show resolved Hide resolved
// so it requires the Server instance to be correctly initialized. Post the init function to
// the event queue so that everything is ready when initialization is conducted.
// TODO: Fix initialization order issue in Matter server.
chip::DeviceLayer::PlatformMgr().ScheduleWork(InitBindingHandlerInternal);
bzbarsky-apple marked this conversation as resolved.
Show resolved Hide resolved
#if defined(ENABLE_CHIP_SHELL)
RegisterSwitchCommands();
#endif
Expand Down
18 changes: 14 additions & 4 deletions examples/light-switch-app/efr32/src/binding-handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "app/clusters/bindings/BindingManager.h"
#include "app/server/Server.h"
#include "controller/InvokeInteraction.h"
#include "platform/CHIPDeviceLayer.h"

#if defined(ENABLE_CHIP_SHELL)
#include "lib/shell/Engine.h"
Expand Down Expand Up @@ -132,6 +133,15 @@ static void RegisterSwitchCommands()
return;
}
#endif // ENABLE_CHIP_SHELL

void InitBindingHandlerInternal(intptr_t arg)
{
auto & server = chip::Server::GetInstance();
chip::BindingManager::GetInstance().Init(
{ &server.GetFabricTable(), server.GetCASESessionManager(), &server.GetPersistentStorage() });
chip::BindingManager::GetInstance().RegisterBoundDeviceChangedHandler(LightSwitchChangedHandler);
}

} // namespace

void SwitchToggleOnOff(intptr_t context)
Expand Down Expand Up @@ -169,12 +179,12 @@ void SwitchOnOffOff(intptr_t context)

CHIP_ERROR InitBindingHandler()
{
BindingManager::GetInstance().SetAppServer(&Server::GetInstance());
BindingManager::GetInstance().RegisterBoundDeviceChangedHandler(LightSwitchChangedHandler);

// The initialization of binding manager will try establishing connection with unicast peers
// so it requires the Server instance to be correctly initialized. Post the init function to
// the event queue so that everything is ready when initialization is conducted.
chip::DeviceLayer::PlatformMgr().ScheduleWork(InitBindingHandlerInternal);
#if defined(ENABLE_CHIP_SHELL)
RegisterSwitchCommands();
#endif

return CHIP_NO_ERROR;
}
4 changes: 3 additions & 1 deletion examples/tv-casting-app/linux/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,9 @@ static void OnBindingAdded(const EmberBindingTableEntry & binding)

CHIP_ERROR InitBindingHandlers()
{
chip::BindingManager::GetInstance().SetAppServer(&chip::Server::GetInstance());
auto & server = chip::Server::GetInstance();
chip::BindingManager::GetInstance().Init(
{ &server.GetFabricTable(), server.GetCASESessionManager(), &server.GetPersistentStorage() });
ReturnErrorOnFailure(chip::BindingManager::GetInstance().RegisterBindingAddedHandler(OnBindingAdded));
return CHIP_NO_ERROR;
}
Expand Down
68 changes: 46 additions & 22 deletions src/app/clusters/bindings/BindingManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,21 @@
#include <app/clusters/bindings/BindingManager.h>
#include <app/util/binding-table.h>
#include <credentials/FabricTable.h>
#include <lib/support/CHIPMem.h>

namespace {

class BindingFabricTableDelegate : public chip::FabricTableDelegate
{
void OnFabricDeletedFromStorage(chip::CompressedFabricId compressedFabricId, chip::FabricIndex fabricIndex)
{
auto iter = chip::BindingTable::GetInstance().begin();
while (iter != chip::BindingTable::GetInstance().end())
chip::BindingTable & bindingTable = chip::BindingTable::GetInstance();
auto iter = bindingTable.begin();
while (iter != bindingTable.end())
{
if (iter->fabricIndex == fabricIndex)
{
iter = chip::BindingTable::GetInstance().RemoveAt(iter);
bindingTable.RemoveAt(iter);
}
else
{
Expand All @@ -53,9 +55,9 @@ BindingFabricTableDelegate gFabricTableDelegate;

namespace {

chip::PeerId PeerIdForNode(chip::FabricTable & fabricTable, chip::FabricIndex fabric, chip::NodeId node)
chip::PeerId PeerIdForNode(chip::FabricTable * fabricTable, chip::FabricIndex fabric, chip::NodeId node)
{
chip::FabricInfo * fabricInfo = fabricTable.FindFabricWithIndex(fabric);
chip::FabricInfo * fabricInfo = fabricTable->FindFabricWithIndex(fabric);
if (fabricInfo == nullptr)
{
return chip::PeerId();
Expand All @@ -80,20 +82,42 @@ CHIP_ERROR BindingManager::UnicastBindingRemoved(uint8_t bindingEntryId)
return CHIP_NO_ERROR;
}

void BindingManager::SetAppServer(Server * appServer)
CHIP_ERROR BindingManager::Init(const BindingManagerInitParams & params)
{
mAppServer = appServer;
mAppServer->GetFabricTable().AddFabricDelegate(&gFabricTableDelegate);
VerifyOrReturnError(params.mCASESessionManager != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(params.mFabricTable != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(params.mStorage != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
mInitParams = params;
params.mFabricTable->AddFabricDelegate(&gFabricTableDelegate);
BindingTable::GetInstance().SetPersistentStorage(params.mStorage);
CHIP_ERROR error = BindingTable::GetInstance().LoadFromStorage();
if (error != CHIP_NO_ERROR)
{
// This can happen during first boot of the device.
ChipLogProgress(AppServer, "Cannot load binding table: %" CHIP_ERROR_FORMAT, error.Format());
}
else
{
for (const EmberBindingTableEntry & entry : BindingTable::GetInstance())
{
if (entry.type == EMBER_UNICAST_BINDING)
{
// The CASE connection can also fail if the unicast peer is offline.
// There is recovery mechanism to retry connection on-demand so ignore error.
(void) UnicastBindingCreated(entry.fabricIndex, entry.nodeId);
}
}
}
return CHIP_NO_ERROR;
}

CHIP_ERROR BindingManager::EstablishConnection(FabricIndex fabric, NodeId node)
{
VerifyOrReturnError(mAppServer != nullptr, CHIP_ERROR_INCORRECT_STATE);

PeerId peer = PeerIdForNode(mAppServer->GetFabricTable(), fabric, node);
VerifyOrReturnError(mInitParams.mCASESessionManager != nullptr, CHIP_ERROR_INCORRECT_STATE);
PeerId peer = PeerIdForNode(mInitParams.mFabricTable, fabric, node);
VerifyOrReturnError(peer.GetNodeId() != kUndefinedNodeId, CHIP_ERROR_NOT_FOUND);
CHIP_ERROR error =
mAppServer->GetCASESessionManager()->FindOrEstablishSession(peer, &mOnConnectedCallback, &mOnConnectionFailureCallback);
mInitParams.mCASESessionManager->FindOrEstablishSession(peer, &mOnConnectedCallback, &mOnConnectionFailureCallback);
if (error == CHIP_ERROR_NO_MEMORY)
{
// Release the least recently used entry
Expand All @@ -104,11 +128,11 @@ CHIP_ERROR BindingManager::EstablishConnection(FabricIndex fabric, NodeId node)
if (mPendingNotificationMap.FindLRUConnectPeer(&fabricToRemove, &nodeToRemove) == CHIP_NO_ERROR)
{
mPendingNotificationMap.RemoveAllEntriesForNode(fabricToRemove, nodeToRemove);
PeerId lruPeer = PeerIdForNode(mAppServer->GetFabricTable(), fabricToRemove, nodeToRemove);
mAppServer->GetCASESessionManager()->ReleaseSession(lruPeer);
PeerId lruPeer = PeerIdForNode(mInitParams.mFabricTable, fabricToRemove, nodeToRemove);
mInitParams.mCASESessionManager->ReleaseSession(lruPeer);
// Now retry
error = mAppServer->GetCASESessionManager()->FindOrEstablishSession(peer, &mOnConnectedCallback,
&mOnConnectionFailureCallback);
error =
mInitParams.mCASESessionManager->FindOrEstablishSession(peer, &mOnConnectedCallback, &mOnConnectionFailureCallback);
}
}
return error;
Expand All @@ -131,7 +155,7 @@ void BindingManager::HandleDeviceConnected(OperationalDeviceProxy * device)
{
EmberBindingTableEntry entry = BindingTable::GetInstance().GetAt(pendingNotification.mBindingEntryId);

PeerId peer = PeerIdForNode(mAppServer->GetFabricTable(), entry.fabricIndex, entry.nodeId);
PeerId peer = PeerIdForNode(mInitParams.mFabricTable, entry.fabricIndex, entry.nodeId);
if (device->GetPeerId() == peer)
{
fabricToRemove = entry.fabricIndex;
Expand All @@ -152,13 +176,13 @@ void BindingManager::HandleDeviceConnectionFailure(PeerId peerId, CHIP_ERROR err
{
// Simply release the entry, the connection will be re-established as needed.
ChipLogError(AppServer, "Failed to establish connection to node 0x" ChipLogFormatX64, ChipLogValueX64(peerId.GetNodeId()));
mAppServer->GetCASESessionManager()->ReleaseSession(peerId);
mInitParams.mCASESessionManager->ReleaseSession(peerId);
}

void BindingManager::FabricRemoved(CompressedFabricId compressedFabricId, FabricIndex fabricIndex)
{
mPendingNotificationMap.RemoveAllEntriesForFabric(fabricIndex);
mAppServer->GetCASESessionManager()->ReleaseSessionsForFabric(compressedFabricId);
mInitParams.mCASESessionManager->ReleaseSessionsForFabric(compressedFabricId);
}

CHIP_ERROR BindingManager::NotifyBindingAdded(const EmberBindingTableEntry & binding)
Expand All @@ -172,18 +196,18 @@ CHIP_ERROR BindingManager::NotifyBindingAdded(const EmberBindingTableEntry & bin

CHIP_ERROR BindingManager::NotifyBoundClusterChanged(EndpointId endpoint, ClusterId cluster, void * context)
{
VerifyOrReturnError(mAppServer != nullptr, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(mInitParams.mFabricTable != nullptr, CHIP_ERROR_INCORRECT_STATE);

for (auto iter = BindingTable::GetInstance().begin(); iter != BindingTable::GetInstance().end(); ++iter)
{
if (iter->local == endpoint && (!iter->clusterId.HasValue() || iter->clusterId.Value() == cluster))
{
if (iter->type == EMBER_UNICAST_BINDING)
{
FabricInfo * fabricInfo = mAppServer->GetFabricTable().FindFabricWithIndex(iter->fabricIndex);
FabricInfo * fabricInfo = mInitParams.mFabricTable->FindFabricWithIndex(iter->fabricIndex);
VerifyOrReturnError(fabricInfo != nullptr, CHIP_ERROR_NOT_FOUND);
PeerId peer = fabricInfo->GetPeerIdForNode(iter->nodeId);
OperationalDeviceProxy * peerDevice = mAppServer->GetCASESessionManager()->FindExistingSession(peer);
OperationalDeviceProxy * peerDevice = mInitParams.mCASESessionManager->FindExistingSession(peer);
if (peerDevice != nullptr && peerDevice->IsConnected() && mBoundDeviceChangedHandler)
{
// We already have an active connection
Expand Down
13 changes: 11 additions & 2 deletions src/app/clusters/bindings/BindingManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#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 {

Expand All @@ -38,6 +40,13 @@ namespace chip {
*/
using BoundDeviceChangedHandler = void (*)(const EmberBindingTableEntry & binding, DeviceProxy * peer_device, void * context);

struct BindingManagerInitParams
{
FabricTable * mFabricTable = nullptr;
CASESessionManager * mCASESessionManager = nullptr;
PersistentStorageDelegate * mStorage = nullptr;
};

using BindingAddedHandler = void (*)(const EmberBindingTableEntry & binding);

/**
Expand Down Expand Up @@ -72,7 +81,7 @@ class BindingManager
return CHIP_ERROR_INCORRECT_STATE;
}

void SetAppServer(Server * appServer);
CHIP_ERROR Init(const BindingManagerInitParams & params);

/*
* Notifies the BindingManager that a new unicast binding is created.
Expand Down Expand Up @@ -124,8 +133,8 @@ class BindingManager

PendingNotificationMap mPendingNotificationMap;
BoundDeviceChangedHandler mBoundDeviceChangedHandler;
BindingManagerInitParams mInitParams;
BindingAddedHandler mBindingAddedHandler;
Server * mAppServer = nullptr;

Callback::Callback<OnDeviceConnected> mOnConnectedCallback;
Callback::Callback<OnDeviceConnectionFailure> mOnConnectionFailureCallback;
Expand Down
3 changes: 2 additions & 1 deletion src/app/clusters/bindings/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ void AddBindingEntry(const TargetStructType & entry, EndpointId localEndpoint)
CHIP_ERROR err = BindingManager::GetInstance().UnicastBindingCreated(entry.fabricIndex, entry.node.Value());
if (err != CHIP_NO_ERROR)
{
// Unicast connection failure can happen if peer is offline. We'll retry connection on-demand.
ChipLogProgress(
Zcl, "Binding: Failed to create session for unicast binding to device " ChipLogFormatX64 ": %" CHIP_ERROR_FORMAT,
ChipLogValueX64(entry.node.Value()), err.Format());
Expand Down Expand Up @@ -187,7 +188,7 @@ CHIP_ERROR BindingTableAccess::WriteBindingTable(const ConcreteDataAttributePath
{
BindingManager::GetInstance().UnicastBindingRemoved(bindingTableIter.GetIndex());
}
bindingTableIter = BindingTable::GetInstance().RemoveAt(bindingTableIter);
ReturnErrorOnFailure(BindingTable::GetInstance().RemoveAt(bindingTableIter));
}
else
{
Expand Down
2 changes: 2 additions & 0 deletions src/app/server/Server.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ class Server

CommissioningWindowManager & GetCommissioningWindowManager() { return mCommissioningWindowManager; }

PersistentStorageDelegate & GetPersistentStorage() { return mDeviceStorage; }

/**
* This function send the ShutDown event before stopping
* the event loop.
Expand Down
Loading