Skip to content

Commit

Permalink
Add OTA Software Update in Darwin Tool Framework (#20767)
Browse files Browse the repository at this point in the history
  • Loading branch information
krypton36 authored and pull[bot] committed Aug 8, 2023
1 parent a00874f commit 1293137
Show file tree
Hide file tree
Showing 11 changed files with 570 additions and 0 deletions.
1 change: 1 addition & 0 deletions .github/.wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -946,6 +946,7 @@ OTARequestorDriver
OTARequestorInterface
OTARequestorSerialPort
OTARequestorStorage
otasoftwareupdateapp
OtaSoftwareUpdateProvider
otasoftwareupdaterequestor
otatesting
Expand Down
3 changes: 3 additions & 0 deletions examples/darwin-framework-tool/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ executable("darwin-framework-tool") {
"commands/pairing/PairingCommandBridge.mm",
"commands/pairing/PairingDelegateBridge.mm",
"commands/payload/SetupPayloadParseCommand.mm",
"commands/provider/Commands.h",
"commands/provider/OTAProviderDelegate.mm",
"commands/provider/OTASoftwareUpdateInteractive.mm",
"commands/storage/Commands.h",
"commands/storage/StorageManagementCommand.mm",
"main.mm",
Expand Down
45 changes: 45 additions & 0 deletions examples/darwin-framework-tool/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,48 @@ To get the list of parameters for a specific command, run the built executable
with the target cluster name and the target command name

$ darwin-framework-tool onoff on

## Using Interactive mode

To start the interactive mode run the following command:

$ darwin-framework-tool interactive start

Once in interactive mode, 'help' will display commands available

## Using the OTA Software Update app

OTA SW app will only work in interactive mode. In interactive mode there will be
an additional command 'otasoftwareupdateapp'. Running the following command in
interactive will display available commands.

$ otasoftwareupdateapp

The following json is an example of a list of candidates to set in interactive
mode with `otasoftwareupdateapp candidate-file-path`:

```json
{
"deviceSoftwareVersionModel": [
{
"vendorId": 65521,
"productId": 32769,
"softwareVersion": 10,
"softwareVersionString": "1.0.0",
"cDVersionNumber": 18,
"softwareVersionValid": true,
"minApplicableSoftwareVersion": 0,
"maxApplicableSoftwareVersion": 100,
"otaURL": "/Users/josh/Desktop/OTACandidates/ota_v10.bin"
}
]
}
```

darwin-framework-tool allows to set the consent status on the Provider side with
the following command:

$ otasoftwareupdateapp set-consent-status [granted, obtaining, denied]

By default, the consent will be set to unknown and the requestor will have to
consent. If the requestor cannot consent, the update will be denied.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
#include <map>
#include <string>

#include "../provider/OTAProviderDelegate.h"

#pragma once

constexpr const char kIdentityAlpha[] = "alpha";
Expand All @@ -48,6 +50,8 @@ class CHIPCommandBridge : public Command
StopWaiting();
}

static OTAProviderDelegate * mOTADelegate;

protected:
// Will be called in a setting in which it's safe to touch the CHIP
// stack. The rules for Run() are as follows:
Expand Down Expand Up @@ -115,4 +119,5 @@ class CHIPCommandBridge : public Command
std::mutex cvWaitingForResponseMutex;
chip::Optional<char *> mCommissionerName;
bool mWaitingForResponse{ true };
static dispatch_queue_t mOTAProviderCallbackQueue;
};
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,17 @@
static CHIPToolPersistentStorageDelegate * storage = nil;
std::set<CHIPCommandBridge *> CHIPCommandBridge::sDeferredCleanups;
std::map<std::string, MTRDeviceController *> CHIPCommandBridge::mControllers;
dispatch_queue_t CHIPCommandBridge::mOTAProviderCallbackQueue;
OTAProviderDelegate * CHIPCommandBridge::mOTADelegate;

CHIP_ERROR CHIPCommandBridge::Run()
{
ChipLogProgress(chipTool, "Running Command");
ReturnErrorOnFailure(MaybeSetUpStack());
SetIdentity(mCommissionerName.HasValue() ? mCommissionerName.Value() : kIdentityAlpha);
ChipLogDetail(chipTool, "Setting OTA Provider Delegate:");
mOTADelegate.nodeID = [CurrentCommissioner() controllerNodeId];
[CurrentCommissioner() setOTAProviderDelegate:mOTADelegate queue:mOTAProviderCallbackQueue];
ReturnLogErrorOnFailure(RunCommand());
ReturnLogErrorOnFailure(StartWaiting(GetWaitDuration()));

Expand All @@ -62,6 +67,9 @@
CHIPToolKeypair * nocSigner = [[CHIPToolKeypair alloc] init];
storage = [[CHIPToolPersistentStorageDelegate alloc] init];

mOTAProviderCallbackQueue = dispatch_queue_create("com.darwin-framework-tool.command", DISPATCH_QUEUE_SERIAL);
mOTADelegate = [[OTAProviderDelegate alloc] init];

auto factory = [MTRControllerFactory sharedInstance];
if (factory == nil) {
ChipLogError(chipTool, "Controller factory is nil");
Expand Down
15 changes: 15 additions & 0 deletions examples/darwin-framework-tool/commands/provider/Commands.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include "OTASoftwareUpdateInteractive.h"

void registerClusterOtaSoftwareUpdateProviderInteractive(Commands & commands)
{

const char * clusterName = "OtaSoftwareUpdateApp";

commands_list clusterCommands = {
make_unique<OTASoftwareUpdateSetFilePath>(), //
make_unique<OTASoftwareUpdateSetStatus>(), //

};

commands.Register(clusterName, clusterCommands);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
*
* Copyright (c) 2022 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#import <Matter/Matter.h>

typedef NS_ENUM(uint8_t, UserConsentState) {
OTAProviderUserGranted = 0x00,
OTAProviderUserObtaining = 0x01,
OTAProviderUserDenied = 0x02,
OTAProviderUserUnknown = 0x03,
};

@interface DeviceSoftwareVersionModelData : MTROtaSoftwareUpdateProviderClusterQueryImageParams
@property BOOL softwareVersionValid;
@property (strong, nonatomic, nullable) NSNumber * cDVersionNumber;
@property (strong, nonatomic, nullable) NSNumber * minApplicableSoftwareVersion;
@property (strong, nonatomic, nullable) NSNumber * maxApplicableSoftwareVersion;
@property (strong, nonatomic, nullable) NSString * otaURL;
@end

@interface DeviceSoftwareVersionModel : MTROtaSoftwareUpdateProviderClusterQueryImageResponseParams
@property (strong, nonatomic, nullable) DeviceSoftwareVersionModelData * deviceModelData;
- (NSComparisonResult)CompareSoftwareVersions:(DeviceSoftwareVersionModel * _Nullable)otherObject;
@end

@interface OTAProviderDelegate : NSObject <MTROTAProviderDelegate>
- (void)handleQueryImage:(MTROtaSoftwareUpdateProviderClusterQueryImageParams * _Nonnull)params
completionHandler:(void (^_Nonnull)(MTROtaSoftwareUpdateProviderClusterQueryImageResponseParams * _Nullable data,
NSError * _Nullable error))completionHandler;

- (void)handleApplyUpdateRequest:(MTROtaSoftwareUpdateProviderClusterApplyUpdateRequestParams * _Nonnull)params
completionHandler:(void (^_Nonnull)(MTROtaSoftwareUpdateProviderClusterApplyUpdateResponseParams * _Nullable data,
NSError * _Nullable error))completionHandler;

- (void)handleNotifyUpdateApplied:(MTROtaSoftwareUpdateProviderClusterNotifyUpdateAppliedParams * _Nonnull)params
completionHandler:(StatusCompletion _Nonnull)completionHandler;

@property (strong, nonatomic, nullable) NSArray<DeviceSoftwareVersionModel *> * candidates;
@property (strong, nonatomic, nullable) DeviceSoftwareVersionModel * selectedCandidate;
@property (strong, nonatomic, nullable) NSNumber * nodeID;
@property (nonatomic, readwrite) MTROtaSoftwareUpdateProviderOTAQueryStatus queryImageStatus;
@property (nonatomic, readwrite) UserConsentState userConsentState;

@end
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/*
*
* Copyright (c) 2022 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "OTAProviderDelegate.h"
#import <Matter/Matter.h>

constexpr uint8_t kUpdateTokenLen = 32;

@interface OTAProviderDelegate ()
@property NSString * mOTAFilePath;
@property DeviceSoftwareVersionModel * candidate;
@end

@implementation OTAProviderDelegate

- (instancetype)init
{
if (self = [super init]) {
_nodeID = @(0);
_selectedCandidate = [[DeviceSoftwareVersionModel alloc] init];
_userConsentState = OTAProviderUserUnknown;
}
return self;
}

// TODO: When BDX is added to Matter.framework, update to initialize
// it when there is an update available.
- (void)handleQueryImage:(MTROtaSoftwareUpdateProviderClusterQueryImageParams * _Nonnull)params
completionHandler:(void (^_Nonnull)(MTROtaSoftwareUpdateProviderClusterQueryImageResponseParams * _Nullable data,
NSError * _Nullable error))completionHandler
{
NSError * error;
_selectedCandidate.status = @(MTROtaSoftwareUpdateProviderOTAQueryStatusNotAvailable);
if (![params.protocolsSupported containsObject:@(MTROtaSoftwareUpdateProviderOTADownloadProtocolBDXSynchronous)]) {
_selectedCandidate.status = @(MTROtaSoftwareUpdateProviderOTAQueryStatusDownloadProtocolNotSupported);
error =
[[NSError alloc] initWithDomain:@"OTAProviderDomain"
code:MTRErrorCodeGeneralError
userInfo:@{ NSLocalizedDescriptionKey : NSLocalizedString(@"Protocol is not supported.", nil) }];
completionHandler(_selectedCandidate, error);
return;
}

if ([self SelectOTACandidate:params.vendorId rPID:params.productId rSV:params.softwareVersion]) {
_selectedCandidate.status = @(MTROtaSoftwareUpdateProviderOTAQueryStatusUpdateAvailable);
_selectedCandidate.updateToken = [self generateUpdateToken];
if (params.requestorCanConsent.integerValue == 1) {
_selectedCandidate.userConsentNeeded
= (_userConsentState == OTAProviderUserUnknown || _userConsentState == OTAProviderUserDenied) ? @(1) : @(0);
NSLog(@"User Consent Needed: %@", _selectedCandidate.userConsentNeeded);
} else {
NSLog(@"Requestor cannot obtain user consent. Our State: %hhu", _userConsentState);
switch (_userConsentState) {
case OTAProviderUserGranted:
NSLog(@"User Consent Granted");
_queryImageStatus = MTROtaSoftwareUpdateProviderOTAQueryStatusUpdateAvailable;
break;

case OTAProviderUserObtaining:
NSLog(@"User Consent Obtaining");
_queryImageStatus = MTROtaSoftwareUpdateProviderOTAQueryStatusBusy;
break;

case OTAProviderUserDenied:
case OTAProviderUserUnknown:
NSLog(@"User Consent Denied or Uknown");
_queryImageStatus = MTROtaSoftwareUpdateProviderOTAQueryStatusNotAvailable;
break;
}
_selectedCandidate.status = @(_queryImageStatus);
}
} else {
NSLog(@"Unable to select OTA Image.");
_selectedCandidate.status = @(MTROtaSoftwareUpdateProviderOTAQueryStatusNotAvailable);
error = [[NSError alloc]
initWithDomain:@"OTAProviderDomain"
code:MTRErrorCodeInvalidState
userInfo:@{ NSLocalizedDescriptionKey : NSLocalizedString(@"Unable to select Candidate.", nil) }];
}
completionHandler(_selectedCandidate, error);
}

- (void)handleApplyUpdateRequest:(MTROtaSoftwareUpdateProviderClusterApplyUpdateRequestParams * _Nonnull)params
completionHandler:(void (^_Nonnull)(MTROtaSoftwareUpdateProviderClusterApplyUpdateResponseParams * _Nullable data,
NSError * _Nullable error))completionHandler
{
MTROtaSoftwareUpdateProviderClusterApplyUpdateResponseParams * applyUpdateResponsePrams =
[[MTROtaSoftwareUpdateProviderClusterApplyUpdateResponseParams alloc] init];
applyUpdateResponsePrams.action = @(MTROtaSoftwareUpdateProviderOTAApplyUpdateActionProceed);
completionHandler(applyUpdateResponsePrams, nil);
}

- (void)handleNotifyUpdateApplied:(MTROtaSoftwareUpdateProviderClusterNotifyUpdateAppliedParams * _Nonnull)params
completionHandler:(StatusCompletion _Nonnull)completionHandler
{
completionHandler(nil);
}

- (void)SetOTAFilePath:(const char *)path
{
_mOTAFilePath = [NSString stringWithUTF8String:path];
}

- (NSData *)generateUpdateToken
{
NSMutableData * updateTokenData = [NSMutableData dataWithCapacity:kUpdateTokenLen];
for (unsigned int i = 0; i < kUpdateTokenLen / 4; ++i) {
u_int32_t randomBits = arc4random();
[updateTokenData appendBytes:(void *) &randomBits length:4];
}

return [NSData dataWithData:updateTokenData];
}

- (bool)SelectOTACandidate:(NSNumber *)requestorVendorID
rPID:(NSNumber *)requestorProductID
rSV:(NSNumber *)requestorSoftwareVersion
{
bool candidateFound = false;
NSArray * sortedArray = [_candidates sortedArrayUsingSelector:@selector(CompareSoftwareVersions:)];
for (DeviceSoftwareVersionModel * candidate : sortedArray) {
if (candidate.deviceModelData.softwareVersionValid
&& ([requestorSoftwareVersion unsignedLongValue] < [candidate.softwareVersion unsignedLongValue])
&& ([requestorSoftwareVersion unsignedLongValue] >=
[candidate.deviceModelData.minApplicableSoftwareVersion unsignedLongValue])
&& ([requestorSoftwareVersion unsignedLongValue] <=
[candidate.deviceModelData.maxApplicableSoftwareVersion unsignedLongValue])
&& ([requestorVendorID unsignedIntValue] == [candidate.deviceModelData.vendorId unsignedIntValue])
&& ([requestorProductID unsignedIntValue] == [candidate.deviceModelData.productId unsignedIntValue])) {
candidateFound = true;
_selectedCandidate = candidate;
_selectedCandidate.imageURI = [NSString
stringWithFormat:@"bdx://%016llX/%@", [_nodeID unsignedLongLongValue], _selectedCandidate.deviceModelData.otaURL];
}
}
return candidateFound;
}

@end

@implementation DeviceSoftwareVersionModelData
- (instancetype)init
{
if (self = [super init]) {
_cDVersionNumber = nil;

_minApplicableSoftwareVersion = nil;

_maxApplicableSoftwareVersion = nil;

_otaURL = nil;
}
return self;
}

@end

@implementation DeviceSoftwareVersionModel
- (instancetype)init
{
if (self = [super init]) {
_deviceModelData = [[DeviceSoftwareVersionModelData alloc] init];
}
return self;
}
- (NSComparisonResult)CompareSoftwareVersions:(DeviceSoftwareVersionModel * _Nullable)otherObject
{
return [self.deviceModelData.softwareVersion compare:otherObject.deviceModelData.softwareVersion];
}
@end
Loading

0 comments on commit 1293137

Please sign in to comment.