| /* |
| * |
| * Copyright (c) 2020-2021 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, softwarEchoe |
| * 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 provides an implementation of functions for sending messages. |
| */ |
| |
| #include <app/util/chip-message-send.h> |
| |
| #include <assert.h> |
| #include <inet/InetLayer.h> |
| #include <lib/support/logging/CHIPLogging.h> |
| #include <messaging/ExchangeContext.h> |
| #include <messaging/ExchangeMgr.h> |
| #include <protocols/Protocols.h> |
| #include <protocols/temp_zcl/TempZCL.h> |
| #include <transport/raw/MessageHeader.h> |
| |
| using namespace chip; |
| |
| // TODO: This is not ideal, but we're still sorting out how secure session |
| // managers end up working and whether they're singletons. In the long term, |
| // there will be some sane API that lets us send a message to a given node id. |
| // |
| // https://github.com/project-chip/connectedhomeip/issues/2566 tracks that API. |
| namespace chip { |
| // TODO: This is a placeholder delegate for exchange context created in Device::SendMessage() |
| // Delete this class when Device::SendMessage() is obsoleted. |
| class DeviceExchangeDelegate : public Messaging::ExchangeDelegate |
| { |
| CHIP_ERROR OnMessageReceived(Messaging::ExchangeContext * ec, const PayloadHeader & payloadHeader, |
| System::PacketBufferHandle && payload) override |
| { |
| return CHIP_NO_ERROR; |
| } |
| void OnResponseTimeout(Messaging::ExchangeContext * ec) override {} |
| }; |
| |
| extern Messaging::ExchangeManager * ExchangeManager(); |
| } // namespace chip |
| |
| EmberStatus chipSendUnicast(Messaging::ExchangeContext * exchange, EmberApsFrame * apsFrame, uint16_t messageLength, |
| uint8_t * message, Messaging::SendFlags sendFlags) |
| { |
| uint16_t frameSize = encodeApsFrame(nullptr, 0, apsFrame); |
| uint32_t dataLengthUnchecked = uint32_t(frameSize) + uint32_t(messageLength); |
| if (dataLengthUnchecked > UINT16_MAX) |
| { |
| // Definitely too long for a packet! |
| return EMBER_MESSAGE_TOO_LONG; |
| } |
| uint16_t dataLength = static_cast<uint16_t>(dataLengthUnchecked); |
| |
| if (frameSize == 0) |
| { |
| ChipLogError(Zcl, "Error encoding APS frame"); |
| return EMBER_ERR_FATAL; |
| } |
| |
| System::PacketBufferHandle buffer = MessagePacketBuffer::New(dataLength); |
| if (buffer.IsNull()) |
| { |
| // FIXME: Not quite right... what's the right way to indicate "out of |
| // heap"? |
| return EMBER_MESSAGE_TOO_LONG; |
| } |
| |
| if (encodeApsFrame(buffer->Start(), dataLength, apsFrame) != frameSize) |
| { |
| // Something is very wrong here; our first call lied to us! |
| ChipLogError(Zcl, "Something wrong happened trying to encode aps frame to respond with"); |
| return EMBER_ERR_FATAL; |
| } |
| |
| memcpy(buffer->Start() + frameSize, message, messageLength); |
| buffer->SetDataLength(dataLength); |
| |
| CHIP_ERROR err = exchange->SendMessage(Protocols::TempZCL::MsgType::TempZCLResponse, std::move(buffer), sendFlags); |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| // FIXME: Figure out better translations between our error types? |
| return EMBER_DELIVERY_FAILED; |
| } |
| |
| return EMBER_SUCCESS; |
| } |
| |
| EmberStatus chipSendUnicast(NodeId destination, EmberApsFrame * apsFrame, uint16_t messageLength, uint8_t * message) |
| { |
| // TODO: temporary create a handle from node id, will be fix in PR 3602 |
| Messaging::ExchangeManager * exchangeMgr = ExchangeManager(); |
| if (exchangeMgr == nullptr) |
| { |
| return EMBER_DELIVERY_FAILED; |
| } |
| |
| Messaging::ExchangeContext * exchange = |
| exchangeMgr->NewContext(SessionHandle(destination, 0, Transport::kAnyKeyId, 0), nullptr); |
| if (exchange == nullptr) |
| { |
| return EMBER_DELIVERY_FAILED; |
| } |
| |
| // TODO(#5675): This code is temporary, and must be updated to use the IM API. Currently, we use a temporary Protocol |
| // TempZCL to carry over legacy ZCL messages, use an ephemeral exchange to send message and use its unsolicited message |
| // handler to receive messages. We need to set flag kFromInitiator to allow receiver to deliver message to corresponding |
| // unsolicited message handler, and we also need to set flag kNoAutoRequestAck since there is no persistent exchange to |
| // receive the ack message. This logic needs to be deleted after we convert all legacy ZCL messages to IM messages. |
| DeviceExchangeDelegate delegate; |
| exchange->SetDelegate(&delegate); |
| |
| Messaging::SendFlags sendFlags; |
| |
| sendFlags.Set(Messaging::SendMessageFlags::kFromInitiator).Set(Messaging::SendMessageFlags::kNoAutoRequestAck); |
| |
| EmberStatus err = chipSendUnicast(exchange, apsFrame, messageLength, message, sendFlags); |
| |
| // Make sure we always close the temporary exchange we just created, unless |
| // we sent a message successfully. |
| if (err != EMBER_SUCCESS) |
| { |
| exchange->Close(); |
| } |
| |
| return err; |
| } |