Skip to content

Commit 23223a1

Browse files
Fix handling of errors in Darwin framework OTA delegate bridge. (#22437)
This allows the framework consumer to return an error that will be sent as an error status to the OTA provider client. Fixes project-chip/connectedhomeip#20653
1 parent 754a208 commit 23223a1

File tree

3 files changed

+78
-16
lines changed

3 files changed

+78
-16
lines changed

examples/darwin-framework-tool/commands/provider/OTAProviderDelegate.mm

+4-13
Original file line numberDiff line numberDiff line change
@@ -46,28 +46,19 @@ - (void)handleQueryImageForNodeID:(NSNumber * _Nonnull)nodeID
4646
completionHandler:(void (^_Nonnull)(MTROtaSoftwareUpdateProviderClusterQueryImageResponseParams * _Nullable data,
4747
NSError * _Nullable error))completionHandler
4848
{
49-
NSError * error;
50-
5149
auto isBDXProtocolSupported =
5250
[params.protocolsSupported containsObject:@(MTROtaSoftwareUpdateProviderOTADownloadProtocolBDXSynchronous)];
5351
if (!isBDXProtocolSupported) {
5452
_selectedCandidate.status = @(MTROtaSoftwareUpdateProviderOTAQueryStatusDownloadProtocolNotSupported);
55-
error =
56-
[[NSError alloc] initWithDomain:@"OTAProviderDomain"
57-
code:MTRErrorCodeGeneralError
58-
userInfo:@{ NSLocalizedDescriptionKey : NSLocalizedString(@"Protocol is not supported.", nil) }];
59-
completionHandler(_selectedCandidate, error);
53+
completionHandler(_selectedCandidate, nil);
6054
return;
6155
}
6256

6357
auto hasCandidate = [self SelectOTACandidate:params.vendorId rPID:params.productId rSV:params.softwareVersion];
6458
if (!hasCandidate) {
6559
NSLog(@"Unable to select OTA Image.");
6660
_selectedCandidate.status = @(MTROtaSoftwareUpdateProviderOTAQueryStatusNotAvailable);
67-
error = [[NSError alloc]
68-
initWithDomain:@"OTAProviderDomain"
69-
code:MTRErrorCodeInvalidState
70-
userInfo:@{ NSLocalizedDescriptionKey : NSLocalizedString(@"Unable to select Candidate.", nil) }];
61+
completionHandler(_selectedCandidate, nil);
7162
return;
7263
}
7364

@@ -78,7 +69,7 @@ - (void)handleQueryImageForNodeID:(NSNumber * _Nonnull)nodeID
7869
_selectedCandidate.userConsentNeeded
7970
= (_userConsentState == OTAProviderUserUnknown || _userConsentState == OTAProviderUserDenied) ? @(1) : @(0);
8071
NSLog(@"User Consent Needed: %@", _selectedCandidate.userConsentNeeded);
81-
completionHandler(_selectedCandidate, error);
72+
completionHandler(_selectedCandidate, nil);
8273
return;
8374
}
8475

@@ -101,7 +92,7 @@ - (void)handleQueryImageForNodeID:(NSNumber * _Nonnull)nodeID
10192
break;
10293
}
10394
_selectedCandidate.status = @(_queryImageStatus);
104-
completionHandler(_selectedCandidate, error);
95+
completionHandler(_selectedCandidate, nil);
10596
}
10697

10798
- (void)handleApplyUpdateRequestForNodeID:(NSNumber * _Nonnull)nodeID

src/darwin/Framework/CHIP/MTROTAProviderDelegate.h

+11
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ NS_ASSUME_NONNULL_BEGIN
3131
* Notify the delegate when the query image command is received from some node.
3232
* The controller identifies the fabric the node is on, and the nodeID
3333
* identifies the node within that fabric.
34+
*
35+
* If completionHandler is passed a non-nil error, that will be converted into
36+
* an error response to the client. Otherwise it must have a non-nil data,
37+
* which will be returned to the client.
3438
*/
3539
- (void)handleQueryImageForNodeID:(NSNumber *)nodeID
3640
controller:(MTRDeviceController *)controller
@@ -42,6 +46,10 @@ NS_ASSUME_NONNULL_BEGIN
4246
* Notify the delegate when the apply update request command is received from
4347
* some node. The controller identifies the fabric the node is on, and the
4448
* nodeID identifies the node within that fabric.
49+
*
50+
* If completionHandler is passed a non-nil error, that will be converted into
51+
* an error response to the client. Otherwise it must have a non-nil data,
52+
* which will be returned to the client.
4553
*/
4654
- (void)handleApplyUpdateRequestForNodeID:(NSNumber *)nodeID
4755
controller:(MTRDeviceController *)controller
@@ -53,6 +61,9 @@ NS_ASSUME_NONNULL_BEGIN
5361
* Notify the delegate when the notify update applied command is received from
5462
* some node. The controller identifies the fabric the node is on, and the
5563
* nodeID identifies the node within that fabric.
64+
*
65+
* If completionHandler is passed a non-nil error, that will be converted into
66+
* an error response to the client. Otherwise a success response will be sent.
5667
*/
5768
- (void)handleNotifyUpdateAppliedForNodeID:(NSNumber *)nodeID
5869
controller:(MTRDeviceController *)controller

src/darwin/Framework/CHIP/MTROTAProviderDelegateBridge.mm

+63-3
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,60 @@ bool GetPeerNodeInfo(CommandHandler * commandHandler, const ConcreteCommandPath
378378
*outNodeId = desc.subject;
379379
return true;
380380
}
381+
382+
// Ensures we have a usable CommandHandler and do not have an error.
383+
//
384+
// When this function returns non-null, it's safe to go ahead and use the return
385+
// value to send a response.
386+
//
387+
// When this function returns null, the CommandHandler::Handle should not be
388+
// used anymore.
389+
CommandHandler * _Nullable EnsureValidState(
390+
CommandHandler::Handle & handle, const ConcreteCommandPath & cachedCommandPath, const char * prefix, NSError * _Nullable error)
391+
{
392+
CommandHandler * handler = handle.Get();
393+
if (handler == nullptr) {
394+
ChipLogError(Controller, "%s: no CommandHandler to send response", prefix);
395+
return nullptr;
396+
}
397+
398+
if (error != nil) {
399+
auto * desc = [error description];
400+
auto err = [MTRError errorToCHIPErrorCode:error];
401+
ChipLogError(Controller, "%s: application returned error: '%s', sending error: '%s'", prefix,
402+
[desc cStringUsingEncoding:NSUTF8StringEncoding], chip::ErrorStr(err));
403+
404+
handler->AddStatus(cachedCommandPath, StatusIB(err).mStatus);
405+
handle.Release();
406+
return nullptr;
407+
}
408+
409+
return handler;
410+
}
411+
412+
// Ensures we have a usable CommandHandler and that our args don't involve any
413+
// errors, for the case when we have data to send back.
414+
//
415+
// When this function returns non-null, it's safe to go ahead and use whatever
416+
// object "data" points to to add a response to the command.
417+
//
418+
// When this function returns null, the CommandHandler::Handle should not be
419+
// used anymore.
420+
CommandHandler * _Nullable EnsureValidState(CommandHandler::Handle & handle, const ConcreteCommandPath & cachedCommandPath,
421+
const char * prefix, NSObject * _Nullable data, NSError * _Nullable error)
422+
{
423+
CommandHandler * handler = EnsureValidState(handle, cachedCommandPath, prefix, error);
424+
VerifyOrReturnValue(handler != nullptr, nullptr);
425+
426+
if (data == nil) {
427+
ChipLogError(Controller, "%s: no data to send as a response", prefix);
428+
handler->AddStatus(cachedCommandPath, Protocols::InteractionModel::Status::Failure);
429+
handle.Release();
430+
return nullptr;
431+
}
432+
433+
return handler;
434+
}
381435
} // anonymous namespace
382436

383437
void MTROTAProviderDelegateBridge::HandleQueryImage(
@@ -403,9 +457,12 @@ bool GetPeerNodeInfo(CommandHandler * commandHandler, const ConcreteCommandPath
403457
auto completionHandler = ^(
404458
MTROtaSoftwareUpdateProviderClusterQueryImageResponseParams * _Nullable data, NSError * _Nullable error) {
405459
dispatch_async(mWorkQueue, ^{
406-
CommandHandler * handler = handle.Get();
460+
CommandHandler * handler = EnsureValidState(handle, cachedCommandPath, "QueryImage", data, error);
407461
VerifyOrReturn(handler != nullptr);
408462

463+
ChipLogDetail(Controller, "QueryImage: application responded with: %s",
464+
[[data description] cStringUsingEncoding:NSUTF8StringEncoding]);
465+
409466
Commands::QueryImageResponse::Type response;
410467
ConvertFromQueryImageResponseParms(data, response);
411468

@@ -472,9 +529,12 @@ bool GetPeerNodeInfo(CommandHandler * commandHandler, const ConcreteCommandPath
472529
auto completionHandler
473530
= ^(MTROtaSoftwareUpdateProviderClusterApplyUpdateResponseParams * _Nullable data, NSError * _Nullable error) {
474531
dispatch_async(mWorkQueue, ^{
475-
CommandHandler * handler = handle.Get();
532+
CommandHandler * handler = EnsureValidState(handle, cachedCommandPath, "ApplyUpdateRequest", data, error);
476533
VerifyOrReturn(handler != nullptr);
477534

535+
ChipLogDetail(Controller, "ApplyUpdateRequest: application responded with: %s",
536+
[[data description] cStringUsingEncoding:NSUTF8StringEncoding]);
537+
478538
Commands::ApplyUpdateResponse::Type response;
479539
ConvertFromApplyUpdateRequestResponseParms(data, response);
480540
handler->AddResponse(cachedCommandPath, response);
@@ -509,7 +569,7 @@ bool GetPeerNodeInfo(CommandHandler * commandHandler, const ConcreteCommandPath
509569

510570
auto completionHandler = ^(NSError * _Nullable error) {
511571
dispatch_async(mWorkQueue, ^{
512-
CommandHandler * handler = handle.Get();
572+
CommandHandler * handler = EnsureValidState(handle, cachedCommandPath, "NotifyUpdateApplied", error);
513573
VerifyOrReturn(handler != nullptr);
514574

515575
handler->AddStatus(cachedCommandPath, Protocols::InteractionModel::Status::Success);

0 commit comments

Comments
 (0)