blob: 63c08824590ec1e0d4a8f44ee0c3fa688ccbf5e0 [file]
/**
*
* Copyright (c) 2020 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.
*/
#import "CHIPDeviceController.h"
#import "CHIPDevicePairingDelegateBridge.h"
#import "CHIPDevice_Internal.h"
#import "CHIPError.h"
#import "CHIPLogging.h"
#import "CHIPPersistentStorageDelegateBridge.h"
#include <controller/CHIPDeviceController.h>
#include <support/CHIPMem.h>
static const char * const CHIP_CONTROLLER_QUEUE = "com.zigbee.chip.framework.controller.workqueue";
static const char * const CHIP_COMMISSIONER_DEVICE_ID_KEY = "com.zigbee.chip.commissioner.device.id";
static NSString * const kErrorMemoryInit = @"Init Memory failure";
static NSString * const kErrorCommissionerInit = @"Init failure while initializing a commissioner";
static NSString * const kErrorPairingInit = @"Init failure while creating a pairing delegate";
static NSString * const kErrorPersistentStorageInit = @"Init failure while creating a persistent storage delegate";
static NSString * const kErrorNetworkDispatchQueueInit = @"Init failure while initializing a dispatch queue for the network events";
static NSString * const kErrorPairDevice = @"Failure while pairing the device";
static NSString * const kErrorUnpairDevice = @"Failure while unpairing the device";
static NSString * const kErrorStopPairing = @"Failure while trying to stop the pairing process";
static NSString * const kErrorGetPairedDevice = @"Failure while trying to retrieve a paired device";
@interface CHIPDeviceController ()
@property (nonatomic, readonly, strong, nonnull) NSRecursiveLock * lock;
// queue used to serialize all work performed by the CHIPDeviceController
@property (atomic, readonly) dispatch_queue_t chipWorkQueue;
@property (readonly) chip::Controller::DeviceCommissioner * cppCommissioner;
@property (readonly) CHIPDevicePairingDelegateBridge * pairingDelegateBridge;
@property (readonly) CHIPPersistentStorageDelegateBridge * persistentStorageDelegateBridge;
@property (readonly) chip::NodeId localDeviceId;
@end
@implementation CHIPDeviceController
+ (CHIPDeviceController *)sharedController
{
static CHIPDeviceController * controller = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// initialize the device controller
controller = [[CHIPDeviceController alloc] init];
});
return controller;
}
- (instancetype)init
{
if (self = [super init]) {
CHIP_ERROR errorCode = CHIP_NO_ERROR;
_chipWorkQueue = dispatch_queue_create(CHIP_CONTROLLER_QUEUE, DISPATCH_QUEUE_SERIAL);
if ([self checkForStartError:(_chipWorkQueue != nil) logMsg:kErrorNetworkDispatchQueueInit]) {
return nil;
}
errorCode = chip::Platform::MemoryInit();
if ([self checkForInitError:(CHIP_NO_ERROR == errorCode) logMsg:kErrorMemoryInit]) {
return nil;
}
_pairingDelegateBridge = new CHIPDevicePairingDelegateBridge();
if ([self checkForInitError:(_pairingDelegateBridge != nullptr) logMsg:kErrorPairingInit]) {
return nil;
}
_persistentStorageDelegateBridge = new CHIPPersistentStorageDelegateBridge();
if ([self checkForInitError:(_persistentStorageDelegateBridge != nullptr) logMsg:kErrorPersistentStorageInit]) {
return nil;
}
}
return self;
}
- (BOOL)isRunning
{
__block BOOL commissionerInitialized;
dispatch_sync(_chipWorkQueue, ^{
commissionerInitialized = [self _isRunning];
});
return commissionerInitialized;
}
- (BOOL)_isRunning
{
return _cppCommissioner != nullptr;
}
- (BOOL)shutdown
{
dispatch_sync(_chipWorkQueue, ^{
if (_cppCommissioner) {
CHIP_LOG_DEBUG("%@", kInfoStackShutdown);
_cppCommissioner->Shutdown();
delete _cppCommissioner;
_cppCommissioner = nullptr;
}
});
return YES;
}
- (BOOL)startup:(id<CHIPPersistentStorageDelegate>)storageDelegate queue:(nonnull dispatch_queue_t)queue
{
__block BOOL commissionerInitialized = NO;
dispatch_sync(_chipWorkQueue, ^{
if ([self _isRunning]) {
commissionerInitialized = YES;
return;
}
CHIP_ERROR errorCode = CHIP_ERROR_INCORRECT_STATE;
_persistentStorageDelegateBridge->setFrameworkDelegate(storageDelegate, queue);
// initialize NodeID if needed
[self _getControllerNodeId];
_cppCommissioner = new chip::Controller::DeviceCommissioner();
if (_cppCommissioner != nullptr) {
errorCode = _cppCommissioner->Init(_localDeviceId, _persistentStorageDelegateBridge, _pairingDelegateBridge);
}
if ([self checkForStartError:(CHIP_NO_ERROR == errorCode) logMsg:kErrorCommissionerInit]) {
return;
}
// Start the IO pump
self.cppCommissioner->ServiceEvents();
});
return YES;
}
- (NSNumber *)getControllerNodeId
{
__block NSNumber * nodeID;
dispatch_sync(_chipWorkQueue, ^{
nodeID = [self _getControllerNodeId];
});
return nodeID;
}
- (NSNumber *)_getControllerNodeId
{
uint16_t idStringLen = 32;
char deviceIdString[idStringLen];
if (CHIP_NO_ERROR
!= _persistentStorageDelegateBridge->SyncGetKeyValue(CHIP_COMMISSIONER_DEVICE_ID_KEY, deviceIdString, idStringLen)) {
_localDeviceId = arc4random();
_localDeviceId = _localDeviceId << 32 | arc4random();
CHIP_LOG_ERROR("Assigned %llx node ID to the controller", _localDeviceId);
_persistentStorageDelegateBridge->AsyncSetKeyValue(
CHIP_COMMISSIONER_DEVICE_ID_KEY, [[NSString stringWithFormat:@"%llx", _localDeviceId] UTF8String]);
} else {
NSScanner * scanner = [NSScanner scannerWithString:[NSString stringWithUTF8String:deviceIdString]];
[scanner scanHexLongLong:&_localDeviceId];
CHIP_LOG_ERROR("Found %llx node ID for the controller", _localDeviceId);
}
return [NSNumber numberWithUnsignedLongLong:_localDeviceId];
}
- (BOOL)pairDevice:(uint64_t)deviceID
discriminator:(uint16_t)discriminator
setupPINCode:(uint32_t)setupPINCode
error:(NSError * __autoreleasing *)error
{
__block BOOL success;
dispatch_sync(_chipWorkQueue, ^{
chip::RendezvousParameters params
= chip::RendezvousParameters().SetSetupPINCode(setupPINCode).SetDiscriminator(discriminator);
CHIP_ERROR err = self.cppCommissioner->PairDevice(deviceID, params);
success = ![self checkForError:err logMsg:kErrorPairDevice error:error];
});
return success;
}
- (BOOL)unpairDevice:(uint64_t)deviceID error:(NSError * __autoreleasing *)error
{
__block BOOL success;
dispatch_sync(_chipWorkQueue, ^{
CHIP_ERROR errorCode = CHIP_ERROR_INCORRECT_STATE;
if ([self _isRunning]) {
errorCode = self.cppCommissioner->UnpairDevice(deviceID);
}
success = ![self checkForError:errorCode logMsg:kErrorUnpairDevice error:error];
});
return success;
}
- (BOOL)stopDevicePairing:(uint64_t)deviceID error:(NSError * __autoreleasing *)error
{
__block BOOL success;
dispatch_sync(_chipWorkQueue, ^{
CHIP_ERROR errorCode = CHIP_ERROR_INCORRECT_STATE;
if ([self _isRunning]) {
errorCode = self.cppCommissioner->StopPairing(deviceID);
}
success = ![self checkForError:errorCode logMsg:kErrorStopPairing error:error];
});
return success;
}
- (CHIPDevice *)getPairedDevice:(uint64_t)deviceID error:(NSError * __autoreleasing *)error
{
__block CHIPDevice * chipDevice = nil;
dispatch_sync(_chipWorkQueue, ^{
CHIP_ERROR errorCode = CHIP_ERROR_INCORRECT_STATE;
chip::Controller::Device * device = nullptr;
if ([self _isRunning]) {
errorCode = self.cppCommissioner->GetDevice(deviceID, &device);
}
if ([self checkForError:errorCode logMsg:kErrorGetPairedDevice error:error]) {
return;
}
chipDevice = [[CHIPDevice alloc] initWithDevice:device];
});
return chipDevice;
}
- (void)setPairingDelegate:(id<CHIPDevicePairingDelegate>)delegate queue:(dispatch_queue_t)queue
{
dispatch_async(_chipWorkQueue, ^{
self->_pairingDelegateBridge->setDelegate(delegate, queue);
});
}
- (void)sendWiFiCredentials:(NSString *)ssid password:(NSString *)password
{
dispatch_async(_chipWorkQueue, ^{
self->_pairingDelegateBridge->SendWiFiCredentials(ssid, password);
});
}
- (void)sendThreadCredentials:(NSData *)threadDataSet
{
dispatch_async(_chipWorkQueue, ^{
self->_pairingDelegateBridge->SendThreadCredentials(threadDataSet);
});
}
- (BOOL)checkForInitError:(BOOL)condition logMsg:(NSString *)logMsg
{
if (condition) {
return NO;
}
CHIP_LOG_ERROR("Error: %@", logMsg);
if (_cppCommissioner) {
delete _cppCommissioner;
_cppCommissioner = NULL;
}
if (_pairingDelegateBridge) {
delete _pairingDelegateBridge;
_pairingDelegateBridge = NULL;
}
if (_persistentStorageDelegateBridge) {
delete _persistentStorageDelegateBridge;
_persistentStorageDelegateBridge = NULL;
}
return YES;
}
- (BOOL)checkForStartError:(BOOL)condition logMsg:(NSString *)logMsg
{
if (condition) {
return NO;
}
CHIP_LOG_ERROR("Error: %@", logMsg);
if (_cppCommissioner) {
delete _cppCommissioner;
_cppCommissioner = NULL;
}
return YES;
}
- (BOOL)checkForError:(CHIP_ERROR)errorCode logMsg:(NSString *)logMsg error:(NSError * __autoreleasing *)error
{
if (CHIP_NO_ERROR == errorCode) {
return NO;
}
CHIP_LOG_ERROR("Error(%d): %@, %@", errorCode, [CHIPError errorForCHIPErrorCode:errorCode], logMsg);
if (error) {
*error = [CHIPError errorForCHIPErrorCode:errorCode];
}
return YES;
}
@end