@@ -57,6 +57,7 @@ @interface MTRDownload : NSObject
57
57
- (instancetype )initWithType : (MTRDiagnosticLogType)type
58
58
fabricIndex : (NSNumber *)fabricIndex
59
59
nodeID : (NSNumber *)nodeID
60
+ timeout : (NSTimeInterval )timeout
60
61
queue : (dispatch_queue_t )queue
61
62
completion : (void (^)(NSURL * _Nullable url, NSError * _Nullable error))completion
62
63
done : (void (^)(MTRDownload * finishedDownload))done ;
@@ -69,8 +70,14 @@ - (BOOL)matches:(NSString *)fileDesignator
69
70
70
71
- (void )checkInteractionModelResponse : (MTRDiagnosticLogsClusterRetrieveLogsResponseParams * _Nullable)response error : (NSError * _Nullable)error ;
71
72
73
+ // TODO: The lifetime management for these objects is very odd. Nothing
74
+ // _really_ prevents a BDX transfer starting after a failure: call, for example.
75
+ // We should move more of the state into a single place that will know the exact
76
+ // state of the object.
72
77
- (void )success ;
73
- - (void )failure : (NSError * _Nullable)error ;
78
+ - (void )failure : (NSError *)error ;
79
+ - (void )cancelTimeoutTimer ;
80
+ - (void )abort : (NSError *)error ;
74
81
@end
75
82
76
83
@interface MTRDownloads : NSObject
@@ -83,6 +90,7 @@ - (MTRDownload * _Nullable)get:(NSString *)fileDesignator
83
90
- (MTRDownload * _Nullable)add : (MTRDiagnosticLogType)type
84
91
fabricIndex : (NSNumber *)fabricIndex
85
92
nodeID : (NSNumber *)nodeID
93
+ timeout : (NSTimeInterval )timeout
86
94
queue : (dispatch_queue_t )queue
87
95
completion : (void (^)(NSURL * _Nullable url, NSError * _Nullable error))completion
88
96
done : (void (^)(MTRDownload * finishedDownload))done ;
@@ -141,22 +149,38 @@ - (void)handleBDXTransferSessionEndForFileDesignator:(NSString *)fileDesignator
141
149
CHIP_ERROR OnTransferEnd (chip::bdx::BDXTransferProxy * transfer, CHIP_ERROR error) override ;
142
150
CHIP_ERROR OnTransferData (chip::bdx::BDXTransferProxy * transfer, const chip::ByteSpan & data) override ;
143
151
144
- CHIP_ERROR StartBDXTransferTimeout (MTRDownload * download, uint16_t timeoutInSeconds);
145
- void CancelBDXTransferTimeout (MTRDownload * download);
146
-
147
152
private:
148
- static void OnTransferTimeout (chip::System::Layer * layer, void * context);
149
153
MTRDiagnosticLogsDownloader * __weak mDelegate ;
150
154
};
151
155
152
156
@implementation MTRDownload
157
+
158
+ static void OnTransferTimeout (chip::System::Layer * layer, void * context)
159
+ {
160
+ assertChipStackLockedByCurrentThread ();
161
+
162
+ auto * download = (__bridge MTRDownload *) context;
163
+ VerifyOrReturn (nil != download);
164
+
165
+ auto * controller = [[MTRDeviceControllerFactory sharedInstance ] runningControllerForFabricIndex: download.fabricIndex.unsignedCharValue];
166
+
167
+ MTR_LOG (" %@ Diagnostic log transfer timed out for %016llX-%016llX (%llu), abortHandler: %@" , download,
168
+ controller.compressedFabricID .unsignedLongLongValue , download.nodeID .unsignedLongLongValue ,
169
+ download.nodeID .unsignedLongLongValue , download.abortHandler );
170
+
171
+ [download abort :[MTRError errorForCHIPErrorCode: CHIP_ERROR_TIMEOUT]];
172
+ }
173
+
153
174
- (instancetype )initWithType : (MTRDiagnosticLogType)type
154
175
fabricIndex : (NSNumber *)fabricIndex
155
176
nodeID : (NSNumber *)nodeID
177
+ timeout : (NSTimeInterval )timeout
156
178
queue : (dispatch_queue_t )queue
157
179
completion : (void (^)(NSURL * _Nullable url, NSError * _Nullable error))completion
158
180
done : (void (^)(MTRDownload * finishedDownload))done ;
159
181
{
182
+ assertChipStackLockedByCurrentThread ();
183
+
160
184
self = [super init ];
161
185
if (self) {
162
186
auto * fileDesignator = [self _toFileDesignatorString: type nodeID: nodeID];
@@ -184,6 +208,22 @@ - (instancetype)initWithType:(MTRDiagnosticLogType)type
184
208
_fileURL = fileURL;
185
209
_fileHandle = nil ;
186
210
_finalize = bdxTransferDone;
211
+
212
+ if (timeout <= 0 ) {
213
+ timeout = 0 ;
214
+ } else if (timeout > UINT16_MAX) {
215
+ MTR_LOG (" Warning: timeout is too large. It will be truncated to UINT16_MAX." );
216
+ timeout = UINT16_MAX;
217
+ }
218
+
219
+ if (timeout > 0 ) {
220
+ CHIP_ERROR timerStartErr = chip::DeviceLayer::SystemLayer ().StartTimer (chip::System::Clock::Seconds16 (static_cast <uint16_t >(timeout)),
221
+ OnTransferTimeout, (__bridge void *) self);
222
+ if (timerStartErr != CHIP_NO_ERROR) {
223
+ MTR_LOG_ERROR (" Failed to start timer for diagnostic log download timeout" );
224
+ return nil ;
225
+ }
226
+ }
187
227
}
188
228
return self;
189
229
}
@@ -259,7 +299,7 @@ - (BOOL)matches:(NSString *)fileDesignator
259
299
return [_fileDesignator isEqualToString: fileDesignator] && [_fabricIndex isEqualToNumber: fabricIndex] && [_nodeID isEqualToNumber: nodeID];
260
300
}
261
301
262
- - (void )failure : (NSError * _Nullable )error
302
+ - (void )failure : (NSError *)error
263
303
{
264
304
MTR_LOG (" %@ Diagnostic log transfer failure: %@" , self, error);
265
305
_finalize (error);
@@ -270,6 +310,29 @@ - (void)success
270
310
_finalize (nil );
271
311
}
272
312
313
+ - (void )cancelTimeoutTimer
314
+ {
315
+ assertChipStackLockedByCurrentThread ();
316
+ chip::DeviceLayer::SystemLayer ().CancelTimer (OnTransferTimeout, (__bridge void *) self);
317
+ }
318
+
319
+ - (void )abort : (NSError *)error
320
+ {
321
+ assertChipStackLockedByCurrentThread ();
322
+
323
+ [self cancelTimeoutTimer ];
324
+
325
+ // If there is no abortHandler, it means that the BDX transfer has not
326
+ // started, so we can just call failure: directly.
327
+ //
328
+ // If there is an abortHandler, we need to call it to abort the transfer.
329
+ if (self.abortHandler == nil ) {
330
+ [self failure: error];
331
+ } else {
332
+ self.abortHandler (error);
333
+ }
334
+ }
335
+
273
336
- (NSURL *)_toFileURL : (MTRDiagnosticLogType)type nodeID : (NSNumber *)nodeID
274
337
{
275
338
auto * dateFormatter = [[NSDateFormatter alloc ] init ];
@@ -344,13 +407,14 @@ - (MTRDownload * _Nullable)get:(NSString *)fileDesignator fabricIndex:(NSNumber
344
407
- (MTRDownload * _Nullable)add : (MTRDiagnosticLogType)type
345
408
fabricIndex : (NSNumber *)fabricIndex
346
409
nodeID : (NSNumber *)nodeID
410
+ timeout : (NSTimeInterval )timeout
347
411
queue : (dispatch_queue_t )queue
348
412
completion : (void (^)(NSURL * _Nullable url, NSError * _Nullable error))completion
349
413
done : (void (^)(MTRDownload * finishedDownload))done
350
414
{
351
415
assertChipStackLockedByCurrentThread ();
352
416
353
- auto download = [[MTRDownload alloc ] initWithType: type fabricIndex: fabricIndex nodeID: nodeID queue: queue completion: completion done: done];
417
+ auto download = [[MTRDownload alloc ] initWithType: type fabricIndex: fabricIndex nodeID: nodeID timeout: timeout queue: queue completion: completion done: done];
354
418
VerifyOrReturnValue (nil != download, nil );
355
419
356
420
[_downloads addObject: download];
@@ -367,7 +431,10 @@ - (void)abortDownloadsForController:(MTRDeviceController_Concrete *)controller
367
431
continue ;
368
432
}
369
433
370
- [download failure: [MTRError errorForCHIPErrorCode: CHIP_ERROR_CANCELLED]];
434
+ [download abort :[MTRError errorForCHIPErrorCode: CHIP_ERROR_CANCELLED]];
435
+ // Remove directly instead of waiting for the async bits to catch up,
436
+ // since those async bits might run after controller shutdown finishes
437
+ // and then not be able to dispatch the async task to do the removal.
371
438
[self remove :download];
372
439
}
373
440
}
@@ -376,6 +443,7 @@ - (void)remove:(MTRDownload *)download
376
443
{
377
444
assertChipStackLockedByCurrentThread ();
378
445
446
+ [download cancelTimeoutTimer ];
379
447
[_downloads removeObject: download];
380
448
}
381
449
@end
@@ -423,29 +491,15 @@ - (void)downloadLogFromNodeWithID:(NSNumber *)nodeID
423
491
// any existing ones so we can start this new one.
424
492
[self abortDownloadsForController: controller];
425
493
426
- uint16_t timeoutInSeconds = 0 ;
427
- if (timeout <= 0 ) {
428
- timeoutInSeconds = 0 ;
429
- } else if (timeout > UINT16_MAX) {
430
- MTR_LOG (" Warning: timeout is too large. It will be truncated to UINT16_MAX." );
431
- timeoutInSeconds = UINT16_MAX;
432
- } else {
433
- timeoutInSeconds = static_cast <uint16_t >(timeout);
434
- }
435
-
436
494
// This block is always called when a download is finished.
437
495
auto done = ^(MTRDownload * finishedDownload) {
438
496
[controller asyncDispatchToMatterQueue: ^() {
439
497
[self ->_downloads remove :finishedDownload];
440
-
441
- if (timeoutInSeconds > 0 ) {
442
- self->_bridge ->CancelBDXTransferTimeout (finishedDownload);
443
- }
444
498
} errorHandler: nil ];
445
499
};
446
500
447
501
auto fabricIndex = @(controller.fabricIndex );
448
- auto download = [_downloads add: type fabricIndex: fabricIndex nodeID: nodeID queue: queue completion: completion done: done];
502
+ auto download = [_downloads add: type fabricIndex: fabricIndex nodeID: nodeID timeout: timeout queue: queue completion: completion done: done];
449
503
VerifyOrReturn (nil != download,
450
504
dispatch_async (queue, ^{ completion (nil , [MTRError errorForCHIPErrorCode: CHIP_ERROR_INTERNAL]); }));
451
505
@@ -463,11 +517,6 @@ - (void)downloadLogFromNodeWithID:(NSNumber *)nodeID
463
517
464
518
[cluster retrieveLogsRequestWithParams: params expectedValues: nil expectedValueInterval: nil completion: interactionModelDone];
465
519
466
- if (timeoutInSeconds > 0 ) {
467
- auto err = _bridge->StartBDXTransferTimeout (download, timeoutInSeconds);
468
- VerifyOrReturn (CHIP_NO_ERROR == err, [download failure: [MTRError errorForCHIPErrorCode: err]]);
469
- }
470
-
471
520
MTR_LOG (" %@ Started log download attempt for node %016llX-%016llX (%llu)" , download,
472
521
controller.compressedFabricID .unsignedLongLongValue , nodeID.unsignedLongLongValue , nodeID.unsignedLongLongValue );
473
522
}
@@ -648,40 +697,3 @@ - (void)handleBDXTransferSessionEndForFileDesignator:(NSString *)fileDesignator
648
697
completion: completionHandler];
649
698
return CHIP_NO_ERROR;
650
699
}
651
-
652
- CHIP_ERROR DiagnosticLogsDownloaderBridge::StartBDXTransferTimeout (MTRDownload * download, uint16_t timeoutInSeconds)
653
- {
654
- assertChipStackLockedByCurrentThread ();
655
- return chip::DeviceLayer::SystemLayer ().StartTimer (chip::System::Clock::Seconds16 (timeoutInSeconds), OnTransferTimeout, (__bridge void *) download);
656
- }
657
-
658
- void DiagnosticLogsDownloaderBridge::CancelBDXTransferTimeout (MTRDownload * download)
659
- {
660
- assertChipStackLockedByCurrentThread ();
661
- chip::DeviceLayer::SystemLayer ().CancelTimer (OnTransferTimeout, (__bridge void *) download);
662
- }
663
-
664
- void DiagnosticLogsDownloaderBridge::OnTransferTimeout (chip::System::Layer * layer, void * context)
665
- {
666
- assertChipStackLockedByCurrentThread ();
667
-
668
- auto * download = (__bridge MTRDownload *) context;
669
- VerifyOrReturn (nil != download);
670
-
671
- auto * controller = [[MTRDeviceControllerFactory sharedInstance ] runningControllerForFabricIndex: download.fabricIndex.unsignedCharValue];
672
-
673
- MTR_LOG (" %@ Diagnostic log transfer timed out for %016llX-%016llX (%llu), abortHandler: %@" , download,
674
- controller.compressedFabricID .unsignedLongLongValue , download.nodeID .unsignedLongLongValue ,
675
- download.nodeID .unsignedLongLongValue , download.abortHandler );
676
-
677
- // If there is no abortHandler, it means that the BDX transfer has not started.
678
- // When a BDX transfer has started we need to abort the transfer and we would error out
679
- // at next poll. We would end up calling OnTransferEnd and eventually [download failure:error].
680
- // But if the transfer has not started we would stop right now.
681
- auto error = [MTRError errorForCHIPErrorCode: CHIP_ERROR_TIMEOUT];
682
- if (download.abortHandler == nil ) {
683
- [download failure: error];
684
- } else {
685
- download.abortHandler (error);
686
- }
687
- }
0 commit comments