| /* |
| * |
| * 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 definitions for 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. |
| */ |
| |
| #pragma once |
| |
| #include <app/CASEClient.h> |
| #include <app/CASEClientPool.h> |
| #include <app/DeviceProxy.h> |
| #include <app/util/attribute-filter.h> |
| #include <app/util/basic-types.h> |
| #include <messaging/ExchangeContext.h> |
| #include <messaging/ExchangeDelegate.h> |
| #include <messaging/ExchangeMgr.h> |
| #include <messaging/Flags.h> |
| #include <protocols/secure_channel/CASESession.h> |
| #include <protocols/secure_channel/SessionIDAllocator.h> |
| #include <system/SystemLayer.h> |
| #include <transport/SessionManager.h> |
| #include <transport/TransportMgr.h> |
| #include <transport/raw/MessageHeader.h> |
| #include <transport/raw/UDP.h> |
| |
| #include <lib/dnssd/ResolverProxy.h> |
| |
| namespace chip { |
| |
| struct DeviceProxyInitParams |
| { |
| SessionManager * sessionManager = nullptr; |
| Messaging::ExchangeManager * exchangeMgr = nullptr; |
| SessionIDAllocator * idAllocator = nullptr; |
| FabricTable * fabricTable = nullptr; |
| CASEClientPoolDelegate * clientPool = nullptr; |
| |
| Controller::DeviceControllerInteractionModelDelegate * imDelegate = nullptr; |
| |
| Optional<ReliableMessageProtocolConfig> mrpLocalConfig = Optional<ReliableMessageProtocolConfig>::Missing(); |
| |
| CHIP_ERROR Validate() const |
| { |
| ReturnErrorCodeIf(sessionManager == nullptr, CHIP_ERROR_INCORRECT_STATE); |
| ReturnErrorCodeIf(exchangeMgr == nullptr, CHIP_ERROR_INCORRECT_STATE); |
| ReturnErrorCodeIf(idAllocator == nullptr, CHIP_ERROR_INCORRECT_STATE); |
| ReturnErrorCodeIf(fabricTable == nullptr, CHIP_ERROR_INCORRECT_STATE); |
| ReturnErrorCodeIf(clientPool == nullptr, CHIP_ERROR_INCORRECT_STATE); |
| |
| return CHIP_NO_ERROR; |
| } |
| }; |
| |
| class OperationalDeviceProxy; |
| |
| typedef void (*OnDeviceConnected)(void * context, OperationalDeviceProxy * device); |
| typedef void (*OnDeviceConnectionFailure)(void * context, PeerId peerId, CHIP_ERROR error); |
| |
| class DLL_EXPORT OperationalDeviceProxy : public DeviceProxy, SessionReleaseDelegate, public SessionEstablishmentDelegate |
| { |
| public: |
| virtual ~OperationalDeviceProxy(); |
| OperationalDeviceProxy(DeviceProxyInitParams & params, PeerId peerId) |
| { |
| VerifyOrReturn(params.Validate() == CHIP_NO_ERROR); |
| |
| mSystemLayer = params.exchangeMgr->GetSessionManager()->SystemLayer(); |
| mInitParams = params; |
| mPeerId = peerId; |
| mFabricInfo = params.fabricTable->FindFabricWithCompressedId(peerId.GetCompressedFabricId()); |
| |
| mState = State::NeedsAddress; |
| } |
| |
| OperationalDeviceProxy(DeviceProxyInitParams & params, PeerId peerId, const Dnssd::ResolvedNodeData & nodeResolutionData) : |
| OperationalDeviceProxy(params, peerId) |
| { |
| OnNodeIdResolved(nodeResolutionData); |
| } |
| |
| void Clear(); |
| |
| /* |
| * This function can be called to establish a secure session with the device. |
| * |
| * The device is expected to have been commissioned, A CASE session |
| * setup will be triggered. |
| * |
| * On establishing the session, the callback function `onConnection` will be called. If the |
| * session setup fails, `onFailure` will be called. |
| * |
| * If the session already exists, `onConnection` will be called immediately. |
| * If the resolver is null and the device state is State::NeedsAddress, CHIP_ERROR_INVALID_ARGUMENT will be |
| * returned. |
| */ |
| CHIP_ERROR Connect(Callback::Callback<OnDeviceConnected> * onConnection, |
| Callback::Callback<OnDeviceConnectionFailure> * onFailure, Dnssd::ResolverProxy * resolver); |
| |
| bool IsConnected() const { return mState == State::SecureConnected; } |
| |
| bool IsConnecting() const { return mState == State::Connecting; } |
| |
| /** |
| * Called when a connection is closing. |
| * The object releases all resources associated with the connection. |
| */ |
| void OnSessionReleased(const SessionHandle & session) override; |
| |
| void OnNodeIdResolved(const Dnssd::ResolvedNodeData & nodeResolutionData) |
| { |
| mDeviceAddress = ToPeerAddress(nodeResolutionData); |
| |
| mMRPConfig = nodeResolutionData.GetMRPConfig(); |
| |
| if (mState == State::NeedsAddress) |
| { |
| mState = State::Initialized; |
| } |
| } |
| |
| /** |
| * Mark any open session with the device as expired. |
| */ |
| CHIP_ERROR Disconnect() override; |
| |
| /** |
| * Use SetConnectedSession if 'this' object is a newly allocated device proxy. |
| * It will take an existing session, such as the one established |
| * during commissioning, and use it for this device proxy. |
| * |
| * Note: Avoid using this function generally as it is Deprecated |
| */ |
| void SetConnectedSession(SessionHandle handle); |
| |
| NodeId GetDeviceId() const override { return mPeerId.GetNodeId(); } |
| |
| /** |
| * Update data of the device. |
| * This function will set new IP address, port and MRP retransmission intervals of the device. |
| * Since the device settings might have been moved from RAM to the persistent storage, the function |
| * will load the device settings first, before making the changes. |
| */ |
| CHIP_ERROR UpdateDeviceData(const Transport::PeerAddress & addr, const ReliableMessageProtocolConfig & config); |
| |
| PeerId GetPeerId() const { return mPeerId; } |
| |
| bool MatchesSession(const SessionHandle & session) const { return mSecureSession.Contains(session); } |
| |
| uint8_t GetNextSequenceNumber() override { return mSequenceNumber++; }; |
| |
| CHIP_ERROR ShutdownSubscriptions() override; |
| |
| Controller::DeviceControllerInteractionModelDelegate * GetInteractionModelDelegate() override { return mInitParams.imDelegate; } |
| |
| Messaging::ExchangeManager * GetExchangeManager() const override { return mInitParams.exchangeMgr; } |
| |
| chip::Optional<SessionHandle> GetSecureSession() const override { return mSecureSession.ToOptional(); } |
| |
| bool GetAddress(Inet::IPAddress & addr, uint16_t & port) const override; |
| |
| Transport::PeerAddress GetPeerAddress() const { return mDeviceAddress; } |
| |
| static Transport::PeerAddress ToPeerAddress(const Dnssd::ResolvedNodeData & nodeData) |
| { |
| Inet::InterfaceId interfaceId = Inet::InterfaceId::Null(); |
| |
| // TODO - Revisit usage of InterfaceID only for addresses that are IPv6 LLA |
| // Only use the DNS-SD resolution's InterfaceID for addresses that are IPv6 LLA. |
| // For all other addresses, we should rely on the device's routing table to route messages sent. |
| // Forcing messages down an InterfaceId might fail. For example, in bridged networks like Thread, |
| // mDNS advertisements are not usually received on the same interface the peer is reachable on. |
| if (nodeData.mAddress[0].IsIPv6LinkLocal()) |
| { |
| interfaceId = nodeData.mInterfaceId; |
| } |
| |
| return Transport::PeerAddress::UDP(nodeData.mAddress[0], nodeData.mPort, interfaceId); |
| } |
| |
| private: |
| enum class State |
| { |
| Uninitialized, |
| NeedsAddress, |
| Initialized, |
| Connecting, |
| SecureConnected, |
| }; |
| |
| DeviceProxyInitParams mInitParams; |
| FabricInfo * mFabricInfo; |
| System::Layer * mSystemLayer; |
| |
| CASEClient * mCASEClient = nullptr; |
| |
| PeerId mPeerId; |
| |
| Transport::PeerAddress mDeviceAddress = Transport::PeerAddress::UDP(Inet::IPAddress::Any); |
| |
| State mState = State::Uninitialized; |
| |
| SessionHolder mSecureSession; |
| |
| uint8_t mSequenceNumber = 0; |
| |
| Callback::CallbackDeque mConnectionSuccess; |
| Callback::CallbackDeque mConnectionFailure; |
| |
| CHIP_ERROR EstablishConnection(); |
| |
| bool IsSecureConnected() const override { return mState == State::SecureConnected; } |
| |
| static void HandleCASEConnected(void * context, CASEClient * client); |
| static void HandleCASEConnectionFailure(void * context, CASEClient * client, CHIP_ERROR error); |
| |
| static void CloseCASESessionTask(System::Layer * layer, void * context); |
| |
| void DeferCloseCASESession(); |
| |
| void EnqueueConnectionCallbacks(Callback::Callback<OnDeviceConnected> * onConnection, |
| Callback::Callback<OnDeviceConnectionFailure> * onFailure); |
| |
| void DequeueConnectionSuccessCallbacks(bool executeCallback); |
| void DequeueConnectionFailureCallbacks(CHIP_ERROR error, bool executeCallback); |
| }; |
| |
| } // namespace chip |