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 match 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,8 @@ - (instancetype)initWithMTROTAImageTransferHandler:(MTROTAImageTransferHandler *
78
85
VerifyOrReturnError (mDelegate != nil , CHIP_ERROR_INCORRECT_STATE);
79
86
VerifyOrReturnError (mDelegateNotificationQueue != nil , CHIP_ERROR_INCORRECT_STATE);
80
87
88
+ mIsPeerNodeAThreadDevice = [controller usesThreadForDevice: mPeer .GetNodeId ()];
89
+
81
90
BitFlags<bdx::TransferControlFlags> flags (bdx::TransferControlFlags::kReceiverDrive );
82
91
83
92
return AsyncResponder::Init (mSystemLayer , exchangeCtx, kBdxRole , flags, kMaxBdxBlockSize , kBdxTimeout );
@@ -233,6 +242,11 @@ - (instancetype)initWithMTROTAImageTransferHandler:(MTROTAImageTransferHandler *
233
242
{
234
243
assertChipStackLockedByCurrentThread ();
235
244
245
+ // For thread devices, we need to throttle sending the response to BlockQuery if the query is processed before kBdxThrottleIntervalInMsecs
246
+ // has elapsed to prevent the BDX messages spamming up the network. Get the timestamp at which we start processing the BlockQuery message.
247
+
248
+ __block uint64_t startBlockQueryHandlingTimestamp = chip::System::SystemClock ().GetMonotonicMilliseconds64 ().count ();
249
+
236
250
auto blockSize = @(mTransfer .GetTransferBlockSize ());
237
251
auto blockIndex = @(mTransfer .GetNextBlockNum ());
238
252
@@ -241,7 +255,7 @@ - (instancetype)initWithMTROTAImageTransferHandler:(MTROTAImageTransferHandler *
241
255
242
256
MTROTAImageTransferHandlerWrapper * __weak weakWrapper = mOTAImageTransferHandlerWrapper ;
243
257
244
- auto completionHandler = ^(NSData * _Nullable data, BOOL isEOF) {
258
+ auto respondWithBlock = ^(NSData * _Nullable data, BOOL isEOF) {
245
259
[controller
246
260
asyncDispatchToMatterQueue: ^() {
247
261
assertChipStackLockedByCurrentThread ();
@@ -272,6 +286,35 @@ - (instancetype)initWithMTROTAImageTransferHandler:(MTROTAImageTransferHandler *
272
286
}];
273
287
};
274
288
289
+ __block void (^completionHandler)(NSData * _Nullable data, BOOL isEOF) = nil ;
290
+
291
+ // If the peer node is a Thread device, check how much time has elapsed since we started processing the BlockQuery.
292
+ // If the time elapsed is greater than kBdxThrottleIntervalInMsecs, call the completion handler to respond with a Block right away.
293
+ // If time elapsed is less than kBdxThrottleIntervalInMsecs, dispatch the completion handler to respond with a Block after kBdxThrottleIntervalInMsecs has elapsed.
294
+
295
+ if (mIsPeerNodeAThreadDevice )
296
+ {
297
+ completionHandler = ^(NSData * _Nullable data, BOOL isEOF) {
298
+ uint64_t timeElapsed = chip::System::SystemClock ().GetMonotonicMilliseconds64 ().count () - startBlockQueryHandlingTimestamp;
299
+ if (timeElapsed >= kBdxThrottleIntervalInMsecs .count ())
300
+ {
301
+ completionHandler = respondWithBlock;
302
+ }
303
+ else
304
+ {
305
+ double timeRemainingInSecs = (kBdxThrottleIntervalInMsecs .count () - timeElapsed) / kMilliSecondsInSecond ;
306
+ dispatch_time_t time = dispatch_time (DISPATCH_TIME_NOW, (int64_t )(timeRemainingInSecs * NSEC_PER_SEC));
307
+ dispatch_after (time , dispatch_get_main_queue (), ^{
308
+ respondWithBlock (data, isEOF);
309
+ });
310
+ }
311
+ };
312
+ }
313
+ else
314
+ {
315
+ completionHandler = respondWithBlock;
316
+ }
317
+
275
318
// TODO Handle MaxLength
276
319
277
320
auto nodeId = @(mPeer .GetNodeId ());
0 commit comments