Coverage Report

Created: 2025-09-02 06:46

/src/connectedhomeip/src/protocols/bdx/BdxTransferSession.h
Line
Count
Source (jump to first uncovered line)
1
/**
2
 *    @file
3
 *      This file defines a TransferSession state machine that contains the main logic governing a Bulk Data Transfer session. It
4
 *      provides APIs for starting a transfer or preparing to receive a transfer request, providing input to be processed, and
5
 *      accessing output data (including messages to be sent, message data received by the TransferSession, or state information).
6
 */
7
8
#pragma once
9
10
#include <lib/core/CHIPError.h>
11
#include <protocols/bdx/BdxMessages.h>
12
#include <system/SystemClock.h>
13
#include <system/SystemPacketBuffer.h>
14
#include <transport/raw/MessageHeader.h>
15
16
#include <type_traits>
17
18
namespace chip {
19
namespace bdx {
20
21
enum class TransferRole : uint8_t
22
{
23
    kReceiver = 0,
24
    kSender   = 1,
25
};
26
27
class DLL_EXPORT TransferSession
28
{
29
public:
30
    enum class OutputEventType : uint16_t
31
    {
32
        kNone = 0,
33
        kMsgToSend,
34
        kInitReceived,
35
        kAcceptReceived,
36
        kBlockReceived,
37
        kQueryReceived,
38
        kQueryWithSkipReceived,
39
        kAckReceived,
40
        kAckEOFReceived,
41
        kStatusReceived,
42
        kInternalError,
43
        kTransferTimeout
44
    };
45
46
    struct TransferInitData
47
    {
48
        TransferControlFlags TransferCtlFlags;
49
50
        uint16_t MaxBlockSize = 0;
51
        uint64_t StartOffset  = 0;
52
        uint64_t Length       = 0;
53
54
        const uint8_t * FileDesignator = nullptr;
55
        uint16_t FileDesLength         = 0;
56
57
        // Additional metadata (optional, TLV format)
58
        const uint8_t * Metadata = nullptr;
59
        size_t MetadataLength    = 0;
60
    };
61
62
    struct TransferAcceptData
63
    {
64
        TransferControlFlags ControlMode;
65
66
        uint16_t MaxBlockSize = 0;
67
        uint64_t StartOffset  = 0; ///< Not used for SendAccept message
68
        uint64_t Length       = 0; ///< Not used for SendAccept message
69
70
        // Additional metadata (optional, TLV format)
71
        const uint8_t * Metadata = nullptr;
72
        size_t MetadataLength    = 0;
73
    };
74
75
    struct StatusReportData
76
    {
77
        StatusCode statusCode;
78
    };
79
80
    struct BlockData
81
    {
82
        const uint8_t * Data  = nullptr;
83
        size_t Length         = 0;
84
        bool IsEof            = false;
85
        uint32_t BlockCounter = 0;
86
    };
87
88
    struct MessageTypeData
89
    {
90
        Protocols::Id ProtocolId; // Should only ever be SecureChannel or BDX
91
        uint8_t MessageType;
92
93
0
        MessageTypeData() : ProtocolId(Protocols::NotSpecified), MessageType(0) {}
94
95
0
        bool HasProtocol(Protocols::Id protocol) const { return ProtocolId == protocol; }
96
0
        bool HasMessageType(uint8_t type) const { return MessageType == type; }
97
        template <typename TMessageType, typename = std::enable_if_t<std::is_enum<TMessageType>::value>>
98
        bool HasMessageType(TMessageType type) const
99
0
        {
100
0
            return HasProtocol(Protocols::MessageTypeTraits<TMessageType>::ProtocolId()) && HasMessageType(to_underlying(type));
101
0
        }
Unexecuted instantiation: bool chip::bdx::TransferSession::MessageTypeData::HasMessageType<chip::bdx::MessageType, void>(chip::bdx::MessageType) const
Unexecuted instantiation: bool chip::bdx::TransferSession::MessageTypeData::HasMessageType<chip::Protocols::SecureChannel::MsgType, void>(chip::Protocols::SecureChannel::MsgType) const
102
    };
103
104
    struct TransferSkipData
105
    {
106
        uint64_t BytesToSkip = 0;
107
    };
108
109
    /**
110
     * @brief
111
     *   All output data processed by the TransferSession object will be passed to the caller using this struct via PollOutput().
112
     *
113
     *   NOTE: Some sub-structs may contain pointers to data in a PacketBuffer (see Blockdata). In this case, the MsgData field MUST
114
     *   be populated with a PacketBufferHandle that encapsulates the respective PacketBuffer, in order to ensure valid memory
115
     *   access.
116
     *
117
     *   NOTE: MsgData can contain messages that have been received or messages that should be sent by the caller. The underlying
118
     *   buffer will always start at the data, never at the payload header. Outgoing messages do not have a header prepended.
119
     */
120
    struct OutputEvent
121
    {
122
        OutputEventType EventType;
123
        System::PacketBufferHandle MsgData;
124
        union
125
        {
126
            TransferInitData transferInitData;
127
            TransferAcceptData transferAcceptData;
128
            BlockData blockdata;
129
            StatusReportData statusData;
130
            MessageTypeData msgTypeData;
131
            TransferSkipData bytesToSkip;
132
        };
133
134
0
        OutputEvent() : EventType(OutputEventType::kNone) { statusData = { StatusCode::kUnknown }; }
135
0
        OutputEvent(OutputEventType type) : EventType(type) { statusData = { StatusCode::kUnknown }; }
136
137
        const char * ToString(OutputEventType outputEventType);
138
139
        static const char * TypeToString(OutputEventType outputEventType);
140
141
        static OutputEvent TransferInitEvent(TransferInitData data, System::PacketBufferHandle msg);
142
        static OutputEvent TransferAcceptEvent(TransferAcceptData data);
143
        static OutputEvent TransferAcceptEvent(TransferAcceptData data, System::PacketBufferHandle msg);
144
        static OutputEvent BlockDataEvent(BlockData data, System::PacketBufferHandle msg);
145
        static OutputEvent StatusReportEvent(OutputEventType type, StatusReportData data);
146
        static OutputEvent MsgToSendEvent(MessageTypeData typeData, System::PacketBufferHandle msg);
147
        static OutputEvent QueryWithSkipEvent(TransferSkipData bytesToSkip);
148
    };
149
150
    /**
151
     * @brief
152
     *   Indicates the presence of pending output and includes any data for the caller to take action on.
153
     *
154
     *   This method should be called frequently in order to be notified about any messages received. It should also be called after
155
     *   most other methods in order to notify the user of any message that needs to be sent, or errors that occurred internally.
156
     *
157
     *   It is possible that consecutive calls to this method may emit different outputs depending on the state of the
158
     *   TransferSession object.
159
     *
160
     *   Note that if the type outputted is kMsgToSend, the caller is expected to send the message immediately, and the session
161
     *   timeout timer will begin at curTime.
162
     *
163
     *   See OutputEventType for all possible output event types.
164
     *
165
     * @param event     Reference to an OutputEvent struct that will be filled out with any pending output data
166
     * @param curTime   Current time
167
     */
168
    void PollOutput(OutputEvent & event, System::Clock::Timestamp curTime);
169
170
    /**
171
     * @brief
172
     *   Gets the pending output event from the transfer session in the event param passed in by the caller.
173
     *   The output event may contain some data for the caller to act upon. If there is no pending output event,
174
     *   the caller will get an event of type OutputEventType::kNone.
175
     *
176
     *   It is possible that consecutive calls to this method may emit different outputs depending on the state of the
177
     *   TransferSession object.  The caller is generally expected to keep calling this method until it gets an event of type
178
     * OutputEventType::kNone.
179
     *
180
     *   If the output event type is kMsgToSend, the caller is expected to send the message immediately on the
181
     *   relevant exchange.  In this case the BDX session timeout timer will start when GetNextAction is called.
182
     *
183
     *   See OutputEventType for all possible output event types.
184
     *
185
     * @param event     Reference to an OutputEvent struct that will be filled out with any pending output event data
186
     */
187
    void GetNextAction(OutputEvent & event);
188
189
    /**
190
     * @brief
191
     *   Initializes the TransferSession object and prepares a TransferInit message (emitted via PollOutput()).
192
     *
193
     *   A TransferSession object must be initialized with either StartTransfer() or WaitForTransfer().
194
     *
195
     * @param role      Inidcates whether this object will be sending or receiving data
196
     * @param initData  Data for initializing this object and for populating a TransferInit message
197
     *                  The role parameter will determine whether to populate a ReceiveInit or SendInit
198
     * @param timeout   The amount of time to wait for a response before considering the transfer failed
199
     *
200
     * @return CHIP_ERROR Result of initialization and preparation of a TransferInit message. May also indicate if the
201
     *                    TransferSession object is unable to handle this request.
202
     */
203
    CHIP_ERROR StartTransfer(TransferRole role, const TransferInitData & initData, System::Clock::Timeout timeout);
204
205
    /**
206
     * @brief
207
     *   Initialize the TransferSession object and prepare to receive a TransferInit message at some point.
208
     *
209
     *   A TransferSession object must be initialized with either StartTransfer() or WaitForTransfer().
210
     *
211
     * @param role            Inidcates whether this object will be sending or receiving data
212
     * @param xferControlOpts Indicates all supported control modes. Used to respond to a TransferInit message
213
     * @param maxBlockSize    The max Block size that this object supports.
214
     * @param timeout         The amount of time to wait for a response before considering the transfer failed
215
     *
216
     * @return CHIP_ERROR Result of initialization. May also indicate if the TransferSession object is unable to handle this
217
     *                    request.
218
     */
219
    CHIP_ERROR WaitForTransfer(TransferRole role, BitFlags<TransferControlFlags> xferControlOpts, uint16_t maxBlockSize,
220
                               System::Clock::Timeout timeout);
221
222
    /**
223
     * @brief
224
     *   Indicate that all transfer parameters are acceptable and prepare a SendAccept or ReceiveAccept message (depending on role).
225
     *
226
     * @param acceptData Data used to populate an Accept message (some fields may differ from the original Init message)
227
     *
228
     * @return CHIP_ERROR Result of preparation of an Accept message. May also indicate if the TransferSession object is unable to
229
     *                    handle this request.
230
     */
231
    CHIP_ERROR AcceptTransfer(const TransferAcceptData & acceptData);
232
233
    /**
234
     * @brief
235
     *   Reject a TransferInit message. Use Reset() to prepare this object for another transfer.
236
     *
237
     * @param reason A StatusCode indicating the reason for rejecting the transfer
238
     *
239
     * @return CHIP_ERROR The result of the preparation of a StatusReport message. May also indicate if the TransferSession object
240
     *                    is unable to handle this request.
241
     */
242
    CHIP_ERROR RejectTransfer(StatusCode reason);
243
244
    /**
245
     * @brief
246
     *   Prepare a BlockQuery message. The Block counter will be populated automatically.
247
     *
248
     * @return CHIP_ERROR The result of the preparation of a BlockQuery message. May also indicate if the TransferSession object
249
     *                    is unable to handle this request.
250
     */
251
    CHIP_ERROR PrepareBlockQuery();
252
253
    /**
254
     * @brief
255
     *   Prepare a BlockQueryWithSkip message. The Block counter will be populated automatically.
256
     *
257
     * @param bytesToSkip Number of bytes to seek skip
258
     *
259
     * @return CHIP_ERROR The result of the preparation of a BlockQueryWithSkip message. May also indicate if the TransferSession
260
     * object is unable to handle this request.
261
     */
262
    CHIP_ERROR PrepareBlockQueryWithSkip(const uint64_t & bytesToSkip);
263
264
    /**
265
     * @brief
266
     *   Prepare a Block message. The Block counter will be populated automatically.
267
     *
268
     * @param inData Contains data for filling out the Block message
269
     *
270
     * @return CHIP_ERROR The result of the preparation of a Block message. May also indicate if the TransferSession object
271
     *                    is unable to handle this request.
272
     */
273
    CHIP_ERROR PrepareBlock(const BlockData & inData);
274
275
    /**
276
     * @brief
277
     *   Prepare a BlockAck message. The Block counter will be populated automatically.
278
     *
279
     * @return CHIP_ERROR The result of the preparation of a BlockAck message. May also indicate if the TransferSession object
280
     *                    is unable to handle this request.
281
     */
282
    CHIP_ERROR PrepareBlockAck();
283
284
    /**
285
     * @brief
286
     *   Prematurely end a transfer with a StatusReport. Must still call Reset() to prepare the TransferSession for another
287
     *   transfer.
288
     *
289
     * @param reason The StatusCode reason for ending the transfer.
290
     *
291
     * @return CHIP_ERROR May return an error if there is no transfer in progress.
292
     */
293
    CHIP_ERROR AbortTransfer(StatusCode reason);
294
295
    /**
296
     * @brief
297
     *   Reset all TransferSession parameters. The TransferSession object must then be re-initialized with StartTransfer() or
298
     *   WaitForTransfer().
299
     */
300
    void Reset();
301
302
    /**
303
     * @brief
304
     *   Process a message intended for this TransferSession object.
305
     *
306
     * @param payloadHeader A PayloadHeader containing the Protocol type and Message Type
307
     * @param msg           A PacketBufferHandle pointing to the message buffer to process. May be BDX or StatusReport protocol.
308
     *                      Buffer is expected to start at data (not header).
309
     * @param curTime       Current time
310
     *
311
     * @return CHIP_ERROR Indicates any problems in decoding the message, or if the message is not of the BDX or StatusReport
312
     *                    protocols.
313
     */
314
    CHIP_ERROR HandleMessageReceived(const PayloadHeader & payloadHeader, System::PacketBufferHandle msg,
315
                                     System::Clock::Timestamp curTime);
316
317
0
    TransferControlFlags GetControlMode() const { return mControlMode; }
318
0
    uint64_t GetStartOffset() const { return mStartOffset; }
319
0
    uint64_t GetTransferLength() const { return mTransferLength; }
320
0
    uint16_t GetTransferBlockSize() const { return mTransferMaxBlockSize; }
321
0
    uint32_t GetNextBlockNum() const { return mNextBlockNum; }
322
0
    uint32_t GetNextQueryNum() const { return mNextQueryNum; }
323
0
    size_t GetNumBytesProcessed() const { return mNumBytesProcessed; }
324
    const uint8_t * GetFileDesignator(uint16_t & fileDesignatorLen) const
325
0
    {
326
0
        fileDesignatorLen = mTransferRequestData.FileDesLength;
327
0
        return mTransferRequestData.FileDesignator;
328
0
    }
329
330
    TransferSession();
331
332
private:
333
    enum class TransferState : uint8_t
334
    {
335
        kUnitialized,
336
        kAwaitingInitMsg,
337
        kAwaitingAccept,
338
        kNegotiateTransferParams,
339
        kTransferInProgress,
340
        kAwaitingEOFAck,
341
        kReceivedEOF,
342
        kTransferDone,
343
        kErrorState,
344
    };
345
346
    // Incoming message handlers
347
    CHIP_ERROR HandleBdxMessage(const PayloadHeader & header, System::PacketBufferHandle msg);
348
    CHIP_ERROR HandleStatusReportMessage(const PayloadHeader & header, System::PacketBufferHandle msg);
349
    void HandleTransferInit(MessageType msgType, System::PacketBufferHandle msgData);
350
    void HandleReceiveAccept(System::PacketBufferHandle msgData);
351
    void HandleSendAccept(System::PacketBufferHandle msgData);
352
    void HandleBlockQuery(System::PacketBufferHandle msgData);
353
    void HandleBlockQueryWithSkip(System::PacketBufferHandle msgData);
354
    void HandleBlock(System::PacketBufferHandle msgData);
355
    void HandleBlockEOF(System::PacketBufferHandle msgData);
356
    void HandleBlockAck(System::PacketBufferHandle msgData);
357
    void HandleBlockAckEOF(System::PacketBufferHandle msgData);
358
359
    /**
360
     * @brief
361
     *   Used when handling a TransferInit message. Determines if there are any compatible Transfer control modes between the two
362
     *   transfer peers.
363
     */
364
    void ResolveTransferControlOptions(const BitFlags<TransferControlFlags> & proposed);
365
366
    /**
367
     * @brief
368
     *   Used when handling an Accept message. Verifies that the chosen control mode is compatible with the orignal supported modes.
369
     */
370
    CHIP_ERROR VerifyProposedMode(const BitFlags<TransferControlFlags> & proposed);
371
372
    void PrepareStatusReport(StatusCode code);
373
    bool IsTransferLengthDefinite() const;
374
375
    OutputEventType mPendingOutput = OutputEventType::kNone;
376
    TransferState mState           = TransferState::kUnitialized;
377
    TransferRole mRole;
378
379
    // Indicate supported options pre- transfer accept
380
    BitFlags<TransferControlFlags> mSuppportedXferOpts;
381
    uint16_t mMaxSupportedBlockSize = 0;
382
383
    // Used to govern transfer once it has been accepted
384
    TransferControlFlags mControlMode;
385
    uint8_t mTransferVersion       = 0;
386
    uint64_t mStartOffset          = 0; ///< 0 represents no offset
387
    uint64_t mTransferLength       = 0; ///< 0 represents indefinite length
388
    uint16_t mTransferMaxBlockSize = 0;
389
390
    // Used to store event data before it is emitted via PollOutput()
391
    System::PacketBufferHandle mPendingMsgHandle;
392
    StatusReportData mStatusReportData;
393
    TransferInitData mTransferRequestData;
394
    TransferAcceptData mTransferAcceptData;
395
    BlockData mBlockEventData;
396
    MessageTypeData mMsgTypeData;
397
    TransferSkipData mBytesToSkip;
398
399
    size_t mNumBytesProcessed = 0;
400
401
    uint32_t mLastBlockNum = 0;
402
    uint32_t mNextBlockNum = 0;
403
    uint32_t mLastQueryNum = 0;
404
    uint32_t mNextQueryNum = 0;
405
406
    System::Clock::Timeout mTimeout            = System::Clock::kZero;
407
    System::Clock::Timestamp mTimeoutStartTime = System::Clock::kZero;
408
    bool mShouldInitTimeoutStart               = true;
409
    bool mAwaitingResponse                     = false;
410
};
411
412
} // namespace bdx
413
} // namespace chip