Skip to content

Commit

Permalink
Implement CASESessionManager class (#11703)
Browse files Browse the repository at this point in the history
* Implement CASESessionManager class

- This class enables device to device communication
- It extracts out relevant code from DeviceController class to a minimal
  implementation. The controller class is refactored to use this
  class internally.

* fix build errors

* address review comments

* fix apps

* cleanup

* address review comments

* address review comments
  • Loading branch information
pan-apple authored Nov 16, 2021
1 parent 1aecc69 commit eb84a9d
Show file tree
Hide file tree
Showing 7 changed files with 391 additions and 178 deletions.
2 changes: 2 additions & 0 deletions src/app/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ static_library("app") {
"AttributePathExpandIterator.h",
"AttributePathParams.cpp",
"AttributePathParams.h",
"CASESessionManager.cpp",
"CASESessionManager.h",
"Command.cpp",
"Command.h",
"CommandHandler.cpp",
Expand Down
166 changes: 166 additions & 0 deletions src/app/CASESessionManager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/*
*
* Copyright (c) 2020-2021 Project CHIP Authors
* All rights reserved.
*
* 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.
*/

#include <app/CASESessionManager.h>

namespace chip {

CHIP_ERROR CASESessionManager::FindOrEstablishSession(NodeId nodeId, Callback::Callback<OnDeviceConnected> * onConnection,
Callback::Callback<OnDeviceConnectionFailure> * onFailure)
{
Dnssd::ResolvedNodeData resolutionData;

PeerId peerId = GetFabricInfo()->GetPeerIdForNode(nodeId);

bool nodeIDWasResolved = (mConfig.dnsCache != nullptr && mConfig.dnsCache->Lookup(peerId, resolutionData) == CHIP_NO_ERROR);

OperationalDeviceProxy * session = FindExistingSession(nodeId);
if (session == nullptr)
{
// TODO - Implement LRU to evict least recently used session to handle mActiveSessions pool exhaustion
if (nodeIDWasResolved)
{
session = mActiveSessions.CreateObject(mConfig.sessionInitParams, peerId, resolutionData);
}
else
{
session = mActiveSessions.CreateObject(mConfig.sessionInitParams, peerId);
}

if (session == nullptr)
{
onFailure->mCall(onFailure->mContext, nodeId, CHIP_ERROR_NO_MEMORY);
return CHIP_ERROR_NO_MEMORY;
}
}
else if (nodeIDWasResolved)
{
session->OnNodeIdResolved(resolutionData);
}

CHIP_ERROR err = session->Connect(onConnection, onFailure);
if (err != CHIP_NO_ERROR)
{
ReleaseSession(session);
}

return err;
}

void CASESessionManager::ReleaseSession(NodeId nodeId)
{
ReleaseSession(FindExistingSession(nodeId));
}

CHIP_ERROR CASESessionManager::ResolveDeviceAddress(NodeId nodeId)
{
return Dnssd::Resolver::Instance().ResolveNodeId(GetFabricInfo()->GetPeerIdForNode(nodeId), Inet::IPAddressType::kAny);
}

void CASESessionManager::OnNodeIdResolved(const Dnssd::ResolvedNodeData & nodeData)
{
ChipLogProgress(Controller, "Address resolved for node: 0x" ChipLogFormatX64, ChipLogValueX64(nodeData.mPeerId.GetNodeId()));

if (mConfig.dnsCache != nullptr)
{
LogErrorOnFailure(mConfig.dnsCache->Insert(nodeData));
}

OperationalDeviceProxy * session = FindExistingSession(nodeData.mPeerId.GetNodeId());
VerifyOrReturn(session != nullptr,
ChipLogDetail(Controller, "OnNodeIdResolved was called for a device with no active sessions, ignoring it."));

LogErrorOnFailure(session->UpdateDeviceData(
session->ToPeerAddress(nodeData), nodeData.GetMrpRetryIntervalIdle().ValueOr(CHIP_CONFIG_MRP_DEFAULT_IDLE_RETRY_INTERVAL),
nodeData.GetMrpRetryIntervalActive().ValueOr(CHIP_CONFIG_MRP_DEFAULT_ACTIVE_RETRY_INTERVAL)));
}

void CASESessionManager::OnNodeIdResolutionFailed(const PeerId & peer, CHIP_ERROR error)
{
ChipLogError(Controller, "Error resolving node id: %s", ErrorStr(error));
}

CHIP_ERROR CASESessionManager::GetPeerAddress(NodeId nodeId, Transport::PeerAddress & addr)
{
if (mConfig.dnsCache != nullptr)
{
Dnssd::ResolvedNodeData resolutionData;
ReturnErrorOnFailure(mConfig.dnsCache->Lookup(GetFabricInfo()->GetPeerIdForNode(nodeId), resolutionData));
addr = OperationalDeviceProxy::ToPeerAddress(resolutionData);
return CHIP_NO_ERROR;
}

OperationalDeviceProxy * session = FindExistingSession(nodeId);
VerifyOrReturnError(session != nullptr, CHIP_ERROR_NOT_CONNECTED);
addr = session->GetPeerAddress();
return CHIP_NO_ERROR;
}

void CASESessionManager::OnNewConnection(SessionHandle sessionHandle, Messaging::ExchangeManager * mgr)
{
// TODO Update the MRP params based on the MRP params extracted from CASE, when this is available.
}

void CASESessionManager::OnConnectionExpired(SessionHandle sessionHandle, Messaging::ExchangeManager * mgr)
{
OperationalDeviceProxy * session = FindSession(sessionHandle);
VerifyOrReturn(session != nullptr,
ChipLogDetail(Controller, "OnConnectionExpired was called for unknown device, ignoring it."));

session->OnConnectionExpired(sessionHandle);
}

OperationalDeviceProxy * CASESessionManager::FindSession(SessionHandle session)
{
OperationalDeviceProxy * foundSession = nullptr;
mActiveSessions.ForEachActiveObject([&](auto * activeSession) {
if (activeSession->MatchesSession(session))
{
foundSession = activeSession;
return false;
}
return true;
});

return foundSession;
}

OperationalDeviceProxy * CASESessionManager::FindExistingSession(NodeId id)
{
OperationalDeviceProxy * foundSession = nullptr;
mActiveSessions.ForEachActiveObject([&](auto * activeSession) {
if (activeSession->GetDeviceId() == id)
{
foundSession = activeSession;
return false;
}
return true;
});

return foundSession;
}

void CASESessionManager::ReleaseSession(OperationalDeviceProxy * session)
{
if (session != nullptr)
{
mActiveSessions.ReleaseObject(session);
}
}

} // namespace chip
112 changes: 112 additions & 0 deletions src/app/CASESessionManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
*
* Copyright (c) 2020-2021 Project CHIP Authors
* All rights reserved.
*
* 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/OperationalDeviceProxy.h>
#include <lib/core/CHIPConfig.h>
#include <lib/core/CHIPCore.h>
#include <lib/dnssd/DnssdCache.h>
#include <lib/support/Pool.h>
#include <messaging/ExchangeMgrDelegate.h>

#include <lib/dnssd/Resolver.h>

namespace chip {

struct CASESessionManagerConfig
{
DeviceProxyInitParams sessionInitParams;
Dnssd::DnssdCache<CHIP_CONFIG_MDNS_CACHE_SIZE> * dnsCache = nullptr;
};

/**
* This class provides the following
* 1. Manage a pool of operational device proxy objects for peer nodes that have active message exchange with the local node.
* 2. The pool contains atmost one device proxy object for a given peer node.
* 3. API to lookup an existing proxy object, or allocate a new one by triggering session establishment with the peer node.
* 4. During session establishment, trigger node ID resolution (if needed), and update the DNS-SD cache (if resolution is
* successful)
*/
class CASESessionManager : public Messaging::ExchangeMgrDelegate, public Dnssd::ResolverDelegate
{
public:
CASESessionManager() = delete;

CASESessionManager(CASESessionManagerConfig & params)
{
VerifyOrReturn(params.sessionInitParams.Validate() == CHIP_NO_ERROR);

mConfig = params;
}

virtual ~CASESessionManager() {}

/**
* Find an existing session for the given node ID, or trigger a new session request.
* The caller can optionally provide `onConnection` and `onFailure` callback objects. If provided,
* these will be used to inform the caller about successful or failed connection establishment.
* If the connection is already established, the `onConnection` callback will be immediately called.
*/
CHIP_ERROR FindOrEstablishSession(NodeId nodeId, Callback::Callback<OnDeviceConnected> * onConnection,
Callback::Callback<OnDeviceConnectionFailure> * onFailure);

OperationalDeviceProxy * FindExistingSession(NodeId nodeId);

void ReleaseSession(NodeId nodeId);

FabricInfo * GetFabricInfo() { return mConfig.sessionInitParams.fabricInfo; }

/**
* This API triggers the DNS-SD resolution for the given node ID. The node ID will be looked up
* on the fabric that was configured for the CASESessionManager object.
*
* The results of the DNS-SD resolution request is provided to the class via `ResolverDelegate`
* implementation of CASESessionManager.
*/
CHIP_ERROR ResolveDeviceAddress(NodeId nodeId);

/**
* This API returns the address for the given node ID.
* If the CASESessionManager is configured with a DNS-SD cache, the cache is looked up
* for the node ID.
* If the DNS-SD cache is not available, the CASESessionManager looks up the list for
* an ongoing session with the peer node. If the session doesn't exist, the API will return
* `CHIP_ERROR_NOT_CONNECTED` error.
*/
CHIP_ERROR GetPeerAddress(NodeId nodeId, Transport::PeerAddress & addr);

//////////// ExchangeMgrDelegate Implementation ///////////////
void OnNewConnection(SessionHandle session, Messaging::ExchangeManager * mgr) override;
void OnConnectionExpired(SessionHandle session, Messaging::ExchangeManager * mgr) override;

//////////// ResolverDelegate Implementation ///////////////
void OnNodeIdResolved(const Dnssd::ResolvedNodeData & nodeData) override;
void OnNodeIdResolutionFailed(const PeerId & peerId, CHIP_ERROR error) override;
void OnNodeDiscoveryComplete(const Dnssd::DiscoveredNodeData & nodeData) override {}

private:
OperationalDeviceProxy * FindSession(SessionHandle session);
void ReleaseSession(OperationalDeviceProxy * device);

BitMapObjectPool<OperationalDeviceProxy, CHIP_CONFIG_CONTROLLER_MAX_ACTIVE_DEVICES> mActiveSessions;

CASESessionManagerConfig mConfig;
};

} // namespace chip
7 changes: 4 additions & 3 deletions src/app/OperationalDeviceProxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@
* messages to and from the corresponding CHIP devices.
*/

#include <app/OperationalDeviceProxy.h>
#include "OperationalDeviceProxy.h"

#include "CommandSender.h"
#include "ReadPrepareParams.h"

#include <app/CommandSender.h>
#include <app/ReadPrepareParams.h>
#include <lib/core/CHIPCore.h>
#include <lib/core/CHIPEncoding.h>
#include <lib/dnssd/Resolver.h>
Expand Down
Loading

0 comments on commit eb84a9d

Please sign in to comment.