blob: b308764ea065cdf4f46705fb97c693541ad5c431 [file] [log] [blame]
/*
*
* 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.
*/
/**
* @file
* This file contains implementation of Device class. The objects of this
* class will be used by Controller applications to interact with CHIP
* devices. The class provides mechanism to construct, send and receive
* messages to and from the corresponding CHIP devices.
*/
#include "OperationalDeviceProxy.h"
#include "CommandSender.h"
#include "ReadPrepareParams.h"
#include <lib/core/CHIPCore.h>
#include <lib/core/CHIPEncoding.h>
#include <lib/dnssd/Resolver.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/ErrorStr.h>
#include <lib/support/logging/CHIPLogging.h>
using namespace chip::Callback;
namespace chip {
CHIP_ERROR OperationalDeviceProxy::Connect(Callback::Callback<OnDeviceConnected> * onConnection,
Callback::Callback<OnDeviceConnectionFailure> * onFailure)
{
CHIP_ERROR err = CHIP_NO_ERROR;
switch (mState)
{
case State::Uninitialized:
err = CHIP_ERROR_INCORRECT_STATE;
break;
case State::NeedsAddress:
err = Dnssd::Resolver::Instance().ResolveNodeId(mPeerId, chip::Inet::IPAddressType::kAny);
EnqueueConnectionCallbacks(onConnection, onFailure);
break;
case State::Initialized:
err = EstablishConnection();
if (err == CHIP_NO_ERROR)
{
EnqueueConnectionCallbacks(onConnection, onFailure);
}
break;
case State::Connecting:
EnqueueConnectionCallbacks(onConnection, onFailure);
break;
case State::SecureConnected:
if (onConnection != nullptr)
{
onConnection->mCall(onConnection->mContext, this);
}
break;
default:
err = CHIP_ERROR_INCORRECT_STATE;
};
if (err != CHIP_NO_ERROR && onFailure != nullptr)
{
onFailure->mCall(onFailure->mContext, mPeerId.GetNodeId(), err);
}
return err;
}
CHIP_ERROR OperationalDeviceProxy::UpdateDeviceData(const Transport::PeerAddress & addr,
const ReliableMessageProtocolConfig & config)
{
VerifyOrReturnLogError(mState != State::Uninitialized, CHIP_ERROR_INCORRECT_STATE);
CHIP_ERROR err = CHIP_NO_ERROR;
mDeviceAddress = addr;
mMRPConfig = config;
// Initialize CASE session state with any MRP parameters that DNS-SD has provided.
// It can be overridden by CASE session protocol messages that include MRP parameters.
mCASESession.SetMRPConfig(mMRPConfig);
if (mState == State::NeedsAddress)
{
mState = State::Initialized;
err = EstablishConnection();
if (err != CHIP_NO_ERROR)
{
OnSessionEstablishmentError(err);
}
}
else
{
if (!mSecureSession.HasValue())
{
// Nothing needs to be done here. It's not an error to not have a
// secureSession. For one thing, we could have gotten an different
// UpdateAddress already and that caused connections to be torn down and
// whatnot.
return CHIP_NO_ERROR;
}
Transport::SecureSession * secureSession = mInitParams.sessionManager->GetSecureSession(mSecureSession.Value());
if (secureSession != nullptr)
{
secureSession->SetPeerAddress(addr);
}
}
return err;
}
bool OperationalDeviceProxy::GetAddress(Inet::IPAddress & addr, uint16_t & port) const
{
if (mState == State::Uninitialized || mState == State::NeedsAddress)
{
return false;
}
addr = mDeviceAddress.GetIPAddress();
port = mDeviceAddress.GetPort();
return true;
}
CHIP_ERROR OperationalDeviceProxy::EstablishConnection()
{
// Create a UnauthenticatedSession for CASE pairing.
// Don't use mSecureSession here, because mSecureSession is for encrypted communication.
Optional<SessionHandle> session = mInitParams.sessionManager->CreateUnauthenticatedSession(mDeviceAddress, mMRPConfig);
VerifyOrReturnError(session.HasValue(), CHIP_ERROR_NO_MEMORY);
Messaging::ExchangeContext * exchange = mInitParams.exchangeMgr->NewContext(session.Value(), &mCASESession);
VerifyOrReturnError(exchange != nullptr, CHIP_ERROR_INTERNAL);
ReturnErrorOnFailure(mCASESession.MessageDispatch().Init(mInitParams.sessionManager));
uint16_t keyID = 0;
ReturnErrorOnFailure(mInitParams.idAllocator->Allocate(keyID));
ReturnErrorOnFailure(
mCASESession.EstablishSession(mDeviceAddress, mInitParams.fabricInfo, mPeerId.GetNodeId(), keyID, exchange, this));
mState = State::Connecting;
return CHIP_NO_ERROR;
}
void OperationalDeviceProxy::EnqueueConnectionCallbacks(Callback::Callback<OnDeviceConnected> * onConnection,
Callback::Callback<OnDeviceConnectionFailure> * onFailure)
{
if (onConnection != nullptr)
{
mConnectionSuccess.Enqueue(onConnection->Cancel());
}
if (onFailure != nullptr)
{
mConnectionFailure.Enqueue(onFailure->Cancel());
}
}
void OperationalDeviceProxy::DequeueConnectionSuccessCallbacks(bool executeCallback)
{
Cancelable ready;
mConnectionSuccess.DequeueAll(ready);
while (ready.mNext != &ready)
{
Callback::Callback<OnDeviceConnected> * cb = Callback::Callback<OnDeviceConnected>::FromCancelable(ready.mNext);
cb->Cancel();
if (executeCallback)
{
cb->mCall(cb->mContext, this);
}
}
}
void OperationalDeviceProxy::DequeueConnectionFailureCallbacks(CHIP_ERROR error, bool executeCallback)
{
Cancelable ready;
mConnectionFailure.DequeueAll(ready);
while (ready.mNext != &ready)
{
Callback::Callback<OnDeviceConnectionFailure> * cb =
Callback::Callback<OnDeviceConnectionFailure>::FromCancelable(ready.mNext);
cb->Cancel();
if (executeCallback)
{
cb->mCall(cb->mContext, mPeerId.GetNodeId(), error);
}
}
}
void OperationalDeviceProxy::OnSessionEstablishmentError(CHIP_ERROR error)
{
VerifyOrReturn(mState != State::Uninitialized && mState != State::NeedsAddress,
ChipLogError(Controller, "OnSessionEstablishmentError was called while the device was not initialized"));
mState = State::Initialized;
mInitParams.idAllocator->Free(mCASESession.GetLocalSessionId());
DequeueConnectionSuccessCallbacks(/* executeCallback */ false);
DequeueConnectionFailureCallbacks(error, /* executeCallback */ true);
}
void OperationalDeviceProxy::OnSessionEstablished()
{
VerifyOrReturn(mState != State::Uninitialized,
ChipLogError(Controller, "OnSessionEstablished was called while the device was not initialized"));
CHIP_ERROR err = mInitParams.sessionManager->NewPairing(
Optional<Transport::PeerAddress>::Value(mDeviceAddress), mPeerId.GetNodeId(), &mCASESession,
CryptoContext::SessionRole::kInitiator, mInitParams.fabricInfo->GetFabricIndex());
if (err != CHIP_NO_ERROR)
{
ChipLogError(Controller, "Failed in setting up CASE secure channel: err %s", ErrorStr(err));
OnSessionEstablishmentError(err);
return;
}
mSecureSession.SetValue(SessionHandle(mPeerId.GetNodeId(), mCASESession.GetLocalSessionId(), mCASESession.GetPeerSessionId(),
mInitParams.fabricInfo->GetFabricIndex()));
mState = State::SecureConnected;
DequeueConnectionFailureCallbacks(CHIP_NO_ERROR, /* executeCallback */ false);
DequeueConnectionSuccessCallbacks(/* executeCallback */ true);
}
CHIP_ERROR OperationalDeviceProxy::Disconnect()
{
ReturnErrorCodeIf(mState != State::SecureConnected, CHIP_ERROR_INCORRECT_STATE);
if (mSecureSession.HasValue())
{
mInitParams.sessionManager->ExpirePairing(mSecureSession.Value());
}
mState = State::Initialized;
mCASESession.Clear();
return CHIP_NO_ERROR;
}
void OperationalDeviceProxy::Clear()
{
mCASESession.Clear();
mState = State::Uninitialized;
mInitParams = DeviceProxyInitParams();
}
void OperationalDeviceProxy::OnSessionReleased(SessionHandle session)
{
VerifyOrReturn(mSecureSession.HasValue() && mSecureSession.Value() == session,
ChipLogDetail(Controller, "Connection expired, but it doesn't match the current session"));
mState = State::Initialized;
mSecureSession.ClearValue();
}
CHIP_ERROR OperationalDeviceProxy::ShutdownSubscriptions()
{
return app::InteractionModelEngine::GetInstance()->ShutdownSubscriptions(mInitParams.fabricInfo->GetFabricIndex(),
GetDeviceId());
}
OperationalDeviceProxy::~OperationalDeviceProxy() {}
} // namespace chip