28
28
29
29
constexpr uint32_t kMaxBdxBlockSize = 1024 ;
30
30
31
+ constexpr double kMilliSecondsInSecond = 1000.0 ;
32
+
31
33
// Timeout for the BDX transfer session. The OTA Spec mandates this should be >= 5 minutes.
32
34
constexpr System::Clock::Timeout kBdxTimeout = System::Clock::Seconds16(5 * 60 );
33
35
36
+ // For thread devices, we need to throttle sending Blocks in response to BlockQuery messages
37
+ // to avoid spamming the network with too many BDX messages. We are going to re-use the polling
38
+ // interval of 50 ms as the time to wait before sending a Block in response to a BlockQuery.
39
+ constexpr System::Clock::Timeout kBdxThrottleIntervalInMsecs = System::Clock::Milliseconds32(50 );
40
+
34
41
constexpr bdx::TransferRole kBdxRole = bdx::TransferRole::kSender ;
35
42
36
43
// An ARC-managed object that lets us do weak references to a MTROTAImageTransferHandler
@@ -78,6 +85,11 @@ - (instancetype)initWithMTROTAImageTransferHandler:(MTROTAImageTransferHandler *
78
85
VerifyOrReturnError (mDelegate != nil , CHIP_ERROR_INCORRECT_STATE);
79
86
VerifyOrReturnError (mDelegateNotificationQueue != nil , CHIP_ERROR_INCORRECT_STATE);
80
87
88
+ // Check if the peer node uses Thread. If it does, we need to throttle sending Block messages in response to
89
+ // BlockQuery messages in OnBlockQuery to stop overloading the network.
90
+
91
+ mIsPeerNodeAThreadDevice = [controller usesThreadForDevice: mPeer .GetNodeId ()];
92
+
81
93
BitFlags<bdx::TransferControlFlags> flags (bdx::TransferControlFlags::kReceiverDrive );
82
94
83
95
return AsyncResponder::Init (mSystemLayer , exchangeCtx, kBdxRole , flags, kMaxBdxBlockSize , kBdxTimeout );
@@ -233,6 +245,11 @@ - (instancetype)initWithMTROTAImageTransferHandler:(MTROTAImageTransferHandler *
233
245
{
234
246
assertChipStackLockedByCurrentThread ();
235
247
248
+ // For thread devices, we need to throttle sending the response to BlockQuery if the query is processed before kBdxThrottleIntervalInMsecs
249
+ // has elapsed to prevent the BDX messages spamming up the network. Get the timestamp at which we start processing the BlockQuery message.
250
+
251
+ __block uint64_t startBlockQueryHandlingTimestamp = chip::System::SystemClock ().GetMonotonicMilliseconds64 ().count ();
252
+
236
253
auto blockSize = @(mTransfer .GetTransferBlockSize ());
237
254
auto blockIndex = @(mTransfer .GetNextBlockNum ());
238
255
@@ -241,7 +258,7 @@ - (instancetype)initWithMTROTAImageTransferHandler:(MTROTAImageTransferHandler *
241
258
242
259
MTROTAImageTransferHandlerWrapper * __weak weakWrapper = mOTAImageTransferHandlerWrapper ;
243
260
244
- auto completionHandler = ^(NSData * _Nullable data, BOOL isEOF) {
261
+ auto respondWithBlock = ^(NSData * _Nullable data, BOOL isEOF) {
245
262
[controller
246
263
asyncDispatchToMatterQueue: ^() {
247
264
assertChipStackLockedByCurrentThread ();
@@ -272,6 +289,35 @@ - (instancetype)initWithMTROTAImageTransferHandler:(MTROTAImageTransferHandler *
272
289
}];
273
290
};
274
291
292
+ __block void (^completionHandler)(NSData * _Nullable data, BOOL isEOF) = nil ;
293
+
294
+ // If the peer node is a Thread device, check how much time has elapsed since we started processing the BlockQuery.
295
+ // If the time elapsed is greater than kBdxThrottleIntervalInMsecs, call the completion handler to respond with a Block right away.
296
+ // If time elapsed is less than kBdxThrottleIntervalInMsecs, dispatch the completion handler to respond with a Block after kBdxThrottleIntervalInMsecs has elapsed.
297
+
298
+ if (mIsPeerNodeAThreadDevice )
299
+ {
300
+ completionHandler = ^(NSData * _Nullable data, BOOL isEOF) {
301
+ uint64_t timeElapsed = chip::System::SystemClock ().GetMonotonicMilliseconds64 ().count () - startBlockQueryHandlingTimestamp;
302
+ if (timeElapsed >= kBdxThrottleIntervalInMsecs .count ())
303
+ {
304
+ completionHandler = respondWithBlock;
305
+ }
306
+ else
307
+ {
308
+ double timeRemainingInSecs = (kBdxThrottleIntervalInMsecs .count () - timeElapsed) / kMilliSecondsInSecond ;
309
+ dispatch_time_t time = dispatch_time (DISPATCH_TIME_NOW, (int64_t )(timeRemainingInSecs * NSEC_PER_SEC));
310
+ dispatch_after (time , dispatch_get_main_queue (), ^{
311
+ respondWithBlock (data, isEOF);
312
+ });
313
+ }
314
+ };
315
+ }
316
+ else
317
+ {
318
+ completionHandler = respondWithBlock;
319
+ }
320
+
275
321
// TODO Handle MaxLength
276
322
277
323
auto nodeId = @(mPeer .GetNodeId ());
0 commit comments