| /* |
| * |
| * Copyright (c) 2015-2018 Nest Labs, Inc. |
| * Copyright (c) 2019-2020 Google LLC. |
| * All rights reserved. |
| * |
| * 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. |
| */ |
| |
| /** |
| * @file |
| * This file implements NLWeaveStack interface |
| * |
| */ |
| |
| #import <Foundation/Foundation.h> |
| #import "NLWeaveStack.h" |
| #import "NLLogging.h" |
| #import "NLWeaveBleDelegate_Protected.h" |
| #import "NLWeaveDeviceManager.h" |
| #import "NLWeaveDeviceManager_Protected.h" |
| #import "NLWdmClient_Protected.h" |
| |
| #include <Weave/Core/WeaveCore.h> |
| #include <Weave/Core/WeaveError.h> |
| #include <Weave/Support/CodeUtils.h> |
| #include <Weave/Profiles/data-management/Current/WdmManagedNamespace.h> |
| #include <Weave/Profiles/data-management/DataManagement.h> |
| |
| using namespace nl::Weave::Profiles; |
| using namespace nl::Weave::Profiles::DataManagement; |
| |
| @interface NLWeaveStack () { |
| dispatch_queue_t _mWorkQueue; |
| dispatch_queue_t _mSelectQueue; |
| nl::Weave::System::Layer _mSystemLayer; |
| nl::Inet::InetLayer _mInetLayer; |
| nl::Weave::WeaveFabricState _mFabricState; |
| nl::Weave::WeaveMessageLayer _mMessageLayer; |
| nl::Weave::WeaveExchangeManager _mExchangeMgr; |
| nl::Weave::WeaveSecurityManager _mSecurityMgr; |
| NLWeaveDeviceManager * _mDeviceMgr; |
| NLWdmClient * _mWdmClient; |
| |
| // for shutdown |
| bool _mIsWaitingOnSelect; |
| ShutdownCompletionBlock _mShutdownCompletionBlock; |
| |
| #if CONFIG_NETWORK_LAYER_BLE |
| nl::Ble::BleLayer _mBleLayer; |
| NLWeaveBleDelegate * _mBleDelegate; |
| #endif // #if CONFIG_NETWORK_LAYER_BLE |
| /* |
| int selectRes; |
| int MaxNumberedFdPlusOne; |
| struct timeval sleepTime; |
| fd_set readFDs, writeFDs, exceptFDs; |
| */ |
| } |
| |
| - (WEAVE_ERROR)InitStack_internal:(NSString *)listenAddr bleDelegate:(NLWeaveBleDelegate *)bleDelegate; |
| - (void)ShutdownStack_Stage1; |
| - (void)ShutdownStack_Stage2; |
| - (void)TryProcessNetworkEvents; |
| |
| - (instancetype)init NS_DESIGNATED_INITIALIZER; |
| |
| @end |
| |
| @implementation NLWeaveStack |
| |
| @synthesize WorkQueue = _mWorkQueue; |
| |
| @synthesize BleDelegate = _mBleDelegate; |
| |
| #if CONFIG_NETWORK_LAYER_BLE |
| #endif //#if CONFIG_NETWORK_LAYER_BLE |
| |
| /** |
| @note |
| This function can only be called by the ARC runtime |
| */ |
| - (void)dealloc |
| { |
| // This method can only be called by ARC |
| // Let's not rely on this unpredictable mechanism for de-initialization |
| // application shall call ShutdownStack if it want to cleanly destroy everything before application termination |
| WDM_LOG_METHOD_SIG(); |
| } |
| |
| /** |
| @note |
| This function can be called from any thread/queue at any time |
| */ |
| + (instancetype)sharedStack |
| { |
| static NLWeaveStack * mStack = nil; |
| static dispatch_once_t onceToken; |
| |
| dispatch_once(&onceToken, ^{ |
| mStack = [[self alloc] init]; |
| }); |
| |
| return mStack; |
| } |
| |
| /** |
| @note |
| This function can only be called indirectly via sharedStack |
| */ |
| - (instancetype)init |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| |
| WDM_LOG_METHOD_SIG(); |
| |
| self = [super init]; |
| VerifyOrExit((self), err = WEAVE_ERROR_NO_MEMORY); |
| |
| _currentState = kWeaveStack_NotInitialized; |
| |
| // create a serial work queue for all direct Weave operations |
| _mWorkQueue = dispatch_queue_create("com.nestlabs.queue.weave.work", DISPATCH_QUEUE_SERIAL); |
| VerifyOrExit((_mWorkQueue), err = WEAVE_ERROR_NO_MEMORY); |
| |
| // create a serial work queue for all the select loop to wait on |
| _mSelectQueue = dispatch_queue_create("com.nestlabs.queue.weave.select", DISPATCH_QUEUE_SERIAL); |
| VerifyOrExit((_mSelectQueue), err = WEAVE_ERROR_NO_MEMORY); |
| |
| _mIsWaitingOnSelect = false; |
| |
| _currentState = kWeaveStack_QueueInitialized; |
| |
| exit: |
| id result = nil; |
| if (WEAVE_NO_ERROR == err) { |
| result = self; |
| } else { |
| if (self) { |
| if (_mWorkQueue) { |
| _mWorkQueue = nil; |
| } |
| |
| if (_mSelectQueue) { |
| _mSelectQueue = nil; |
| } |
| } |
| |
| // ErrorStr uses more than one global resources which cannot be safely accessed |
| // from other threads without locking, so we just log the number here |
| WDM_LOG_ERROR(@"Error in init : %d\n", err); |
| } |
| return result; |
| } |
| |
| /** |
| @note |
| This function can only be called indirectly via InitStack, within the Weave workqueue. |
| */ |
| - (WEAVE_ERROR)InitStack_internal:(NSString *)listenAddr bleDelegate:(NLWeaveBleDelegate *)bleDelegate |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| nl::Weave::WeaveMessageLayer::InitContext initContext; |
| |
| VerifyOrExit((kWeaveStack_QueueInitialized == self.currentState), err = WEAVE_ERROR_INCORRECT_STATE); |
| |
| self.currentState = kWeaveStack_Initializing; |
| |
| _mIsWaitingOnSelect = false; |
| |
| // Initialize the System::Layer object |
| err = _mSystemLayer.Init(NULL); |
| SuccessOrExit(err); |
| |
| // Initialize the InetLayer object. |
| err = _mInetLayer.Init(_mSystemLayer, NULL); |
| SuccessOrExit(err); |
| |
| // Initialize the FabricState object. |
| err = _mFabricState.Init(); |
| SuccessOrExit(err); |
| |
| // Not a member of a fabric. |
| _mFabricState.FabricId = 0; |
| |
| // Generate a unique node id for local Weave stack. |
| err = GenerateWeaveNodeId(_mFabricState.LocalNodeId); |
| SuccessOrExit(err); |
| |
| // Configure the weave listening address, if one was provided |
| { |
| if (listenAddr != NULL) { |
| #if WEAVE_CONFIG_ENABLE_TARGETED_LISTEN |
| const char * listenAddrStr = ([listenAddr length] != 0) ? [listenAddr UTF8String] : NULL; |
| |
| nl::Inet::IPAddress addr; |
| if (nl::Inet::IPAddress::FromString(listenAddrStr, addr)) { |
| _mFabricState.ListenIPv4Addr = addr; |
| } |
| #else |
| WDM_LOG_ERROR(@"Error in InitStack: targeted listening is not enabled"); |
| ExitNow(err = WEAVE_ERROR_INVALID_ARGUMENT); |
| #endif |
| } |
| } |
| |
| #if CONFIG_NETWORK_LAYER_BLE |
| if (nil != bleDelegate) { |
| // retain the BLE Delegate provided by the application layer |
| _mBleDelegate = bleDelegate; |
| initContext.listenBLE = true; |
| } else { |
| _mBleDelegate = [[NLWeaveBleDelegate alloc] initDummyDelegate]; |
| initContext.listenBLE = false; |
| } |
| |
| // Initialize the BleLayer |
| err = _mBleLayer.Init([_mBleDelegate GetPlatformDelegate], [_mBleDelegate GetApplicationDelegate], &_mSystemLayer); |
| SuccessOrExit(err); |
| |
| [_mBleDelegate SetBleLayer:&_mBleLayer]; |
| |
| initContext.ble = &_mBleLayer; |
| #endif |
| |
| // Initialize the WeaveMessageLayer object. |
| initContext.systemLayer = &_mSystemLayer; |
| initContext.inet = &_mInetLayer; |
| initContext.fabricState = &_mFabricState; |
| initContext.listenTCP = false; |
| #if WEAVE_CONFIG_DEVICE_MGR_DEMAND_ENABLE_UDP |
| initContext.listenUDP = false; |
| #else |
| initContext.listenUDP = true; |
| #endif |
| #if WEAVE_CONFIG_ENABLE_EPHEMERAL_UDP_PORT |
| initContext.enableEphemeralUDPPort = true; |
| #endif |
| err = _mMessageLayer.Init(&initContext); |
| SuccessOrExit(err); |
| |
| // Initialize the Exchange Manager object. |
| err = _mExchangeMgr.Init(&_mMessageLayer); |
| SuccessOrExit(err); |
| |
| // Initialize the Security Manager object. |
| err = _mSecurityMgr.Init(_mExchangeMgr, _mSystemLayer); |
| SuccessOrExit(err); |
| |
| err = SubscriptionEngine::GetInstance()->Init(&_mExchangeMgr, NULL, NULL); |
| SuccessOrExit(err); |
| |
| self.currentState = kWeaveStack_FullyInitialized; |
| |
| [self TryProcessNetworkEvents]; |
| |
| exit: |
| if (WEAVE_NO_ERROR != err) { |
| WDM_LOG_ERROR(@"Error in InitStack_internal : (%d) %@\n", err, [NSString stringWithUTF8String:nl::ErrorStr(err)]); |
| } |
| |
| return err; |
| } |
| |
| /** |
| @note |
| This function can be called from any thread/queue at any time |
| */ |
| - (WEAVE_ERROR)InitStack:(NSString *)listenAddr bleDelegate:(NLWeaveBleDelegate *)bleDelegate |
| { |
| // initialize the stack |
| WDM_LOG_METHOD_SIG(); |
| |
| __block WEAVE_ERROR result = WEAVE_NO_ERROR; |
| |
| dispatch_sync(_mWorkQueue, ^(void) { |
| result = [self InitStack_internal:listenAddr bleDelegate:bleDelegate]; |
| }); |
| |
| if (WEAVE_NO_ERROR != result) { |
| // ErrorStr uses more than one global resources which cannot be safely accessed |
| // from other threads without locking, so we just log the number here |
| WDM_LOG_ERROR(@"Error in InitStack : %d\n", result); |
| } |
| |
| return result; |
| } |
| |
| /** |
| @note |
| This function can only be called indirectly via TryProcessNetworkEvents, within the Weave workqueue. |
| */ |
| - (void)ShutdownStack_Stage2 |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| ShutdownCompletionBlock block = _mShutdownCompletionBlock; |
| |
| WDM_LOG_METHOD_SIG(); |
| |
| _mShutdownCompletionBlock = nil; |
| |
| WDM_LOG_DEBUG(@"Shutdown Security Manager\n"); |
| _mSecurityMgr.Shutdown(); |
| |
| WDM_LOG_DEBUG(@"Shutdown Exchange Manager\n"); |
| _mExchangeMgr.Shutdown(); |
| |
| WDM_LOG_DEBUG(@"Shutdown Message Layer\n"); |
| _mMessageLayer.Shutdown(); |
| |
| #if CONFIG_NETWORK_LAYER_BLE |
| WDM_LOG_DEBUG(@"Shutdown BLE Layer\n"); |
| _mBleLayer.Shutdown(); |
| #endif |
| |
| WDM_LOG_DEBUG(@"Shutdown Fabric State\n"); |
| _mFabricState.Shutdown(); |
| |
| WDM_LOG_DEBUG(@"Shutdown Inet Layer\n"); |
| _mInetLayer.Shutdown(); |
| |
| WDM_LOG_DEBUG(@"Shutdown Weave System Layer\n"); |
| _mSystemLayer.Shutdown(); |
| |
| WDM_LOG_DEBUG(@"Shutdown completed\n"); |
| |
| self.currentState = kWeaveStack_QueueInitialized; |
| |
| if (WEAVE_NO_ERROR != err) { |
| WDM_LOG_ERROR(@"Error in ShutdownStack_Stage2 : (%d) %@\n", err, [NSString stringWithUTF8String:nl::ErrorStr(err)]); |
| } |
| |
| // inform application layer about the completion, if so desired |
| if (block) { |
| block(err); |
| } |
| } |
| |
| /** |
| @note |
| This function can only be called indirectly via ShutdownStack, within the Weave workqueue. |
| */ |
| - (void)ShutdownStack_Stage1 |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| |
| WDM_LOG_METHOD_SIG(); |
| |
| switch (self.currentState) { |
| case kWeaveStack_Initializing: |
| case kWeaveStack_FullyInitialized: |
| // continue shutting down |
| break; |
| case kWeaveStack_QueueInitialized: |
| // do nothing and just return without error |
| ExitNow(); |
| default: |
| // abort shutting down |
| ExitNow(err = WEAVE_ERROR_INCORRECT_STATE); |
| } |
| |
| self.currentState = kWeaveStack_ShuttingDown; |
| |
| if (_mIsWaitingOnSelect) { |
| // Inform the select queue that it's time to leave |
| _mSystemLayer.WakeSelect(); |
| } else { |
| // invoke stage 2 directly, as the select queue is not active (probably the initialization failed) |
| [self ShutdownStack_Stage2]; |
| } |
| |
| exit: |
| if (WEAVE_NO_ERROR != err) { |
| WDM_LOG_ERROR(@"Error in ShutdownStack_Stage1 : (%d) %@\n", err, [NSString stringWithUTF8String:nl::ErrorStr(err)]); |
| |
| _mShutdownCompletionBlock(err); |
| } |
| } |
| |
| /** |
| @note |
| This function can be called from any thread/queue at any time |
| */ |
| - (void)ShutdownStack:(ShutdownCompletionBlock)block; |
| { |
| WEAVE_ERROR result = WEAVE_NO_ERROR; |
| |
| // release all C/C++ resources at here, if possible |
| WDM_LOG_METHOD_SIG(); |
| |
| // We're dispatching using sync on a serial queue, so it's safe to assume |
| // everything dispatched earlier has been finished |
| dispatch_sync(_mWorkQueue, ^(void) { |
| _mShutdownCompletionBlock = block; |
| [self ShutdownStack_Stage1]; |
| }); |
| |
| if (WEAVE_NO_ERROR != result) { |
| // ErrorStr uses more than one global resources which cannot be safely accessed |
| // from other threads without locking, so we just log the number here |
| WDM_LOG_ERROR(@"Error in ShutdownStack : %d\n", result); |
| } |
| } |
| |
| /** |
| @note |
| This function can only be called indirectly via initStack, within the Weave workqueue. |
| */ |
| - (void)TryProcessNetworkEvents |
| { |
| __block struct timeval sleepTime; |
| __block fd_set readFDs, writeFDs, exceptFDs; |
| int MaxNumberedFdPlusOne = 0; |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| |
| // WDM_LOG_METHOD_SIG(); |
| |
| VerifyOrExit(kWeaveStack_FullyInitialized == self.currentState, err = WEAVE_ERROR_INCORRECT_STATE); |
| |
| FD_ZERO(&readFDs); |
| FD_ZERO(&writeFDs); |
| FD_ZERO(&exceptFDs); |
| |
| sleepTime.tv_sec = 10; |
| sleepTime.tv_usec = 0; |
| |
| // Collect the currently active file descriptors. |
| _mSystemLayer.PrepareSelect(MaxNumberedFdPlusOne, &readFDs, &writeFDs, &exceptFDs, sleepTime); |
| _mInetLayer.PrepareSelect(MaxNumberedFdPlusOne, &readFDs, &writeFDs, &exceptFDs, sleepTime); |
| |
| // WDM_LOG_DEBUG(@"Sleeping for %f sec.\n", sleepTime.tv_sec + (sleepTime.tv_usec / 1000000.0)); |
| |
| _mIsWaitingOnSelect = true; |
| |
| // need this bracket to use the VerifyOrExit macro |
| { |
| dispatch_async(_mSelectQueue, ^(void) { |
| // Wait for for I/O or for the next timer to expire. |
| // Note that this is not a good practice to use with GCD, but it's |
| int selectRes = select(MaxNumberedFdPlusOne, &readFDs, &writeFDs, &exceptFDs, &sleepTime); |
| |
| dispatch_async(_mWorkQueue, ^(void) { |
| _mIsWaitingOnSelect = false; |
| |
| if (kWeaveStack_ShuttingDown == self.currentState) { |
| WDM_LOG_DEBUG(@"Select queue woke up but we're shutting down\n"); |
| |
| // continue on the 2nd stage of shutting down |
| [self ShutdownStack_Stage2]; |
| } else if (kWeaveStack_FullyInitialized == self.currentState) { |
| // Perform I/O and/or dispatch timers. |
| _mSystemLayer.HandleSelectResult(selectRes, &readFDs, &writeFDs, &exceptFDs); |
| _mInetLayer.HandleSelectResult(selectRes, &readFDs, &writeFDs, &exceptFDs); |
| |
| // It's wierd that with iOS 9 SDK, we have to use disaptch_after here, |
| // instead of directly calling TryProcessNetworkEvents nor dispatch_async. |
| // If not, the memory usage skyrockets like there is memory leak |
| dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_MSEC), _mWorkQueue, ^(void) { |
| [self TryProcessNetworkEvents]; |
| }); |
| } else { |
| WDM_LOG_ERROR(@"Select queue woke up in unexpected state\n"); |
| } |
| }); |
| }); |
| } |
| |
| exit: |
| if (WEAVE_NO_ERROR != err) { |
| // ErrorStr uses more than one global resources which cannot be safely accessed |
| // from other threads without locking, so we just log the number here |
| WDM_LOG_ERROR(@"Error in TryProcessNetworkEvents : %d\n", err); |
| } |
| } |
| |
| - (NLWeaveDeviceManager *)createDeviceManager:(NSString *)name appCallbackQueue:(dispatch_queue_t)appCallbackQueue |
| { |
| WDM_LOG_METHOD_SIG(); |
| |
| _mDeviceMgr = [[NLWeaveDeviceManager alloc] init:name |
| weaveWorkQueue:_mWorkQueue |
| appCallbackQueue:appCallbackQueue |
| exchangeMgr:&_mExchangeMgr |
| securityMgr:&_mSecurityMgr]; |
| if (nil == _mDeviceMgr) { |
| WDM_LOG_ERROR(@"Cannot create new NLWeaveDeviceManager\n"); |
| } |
| return _mDeviceMgr; |
| } |
| |
| #if WEAVE_CONFIG_DATA_MANAGEMENT_CLIENT_EXPERIMENTAL |
| - (NLWdmClient *)createWdmClient:(NSString *)name appCallbackQueue:(dispatch_queue_t)appCallbackQueue |
| { |
| WDM_LOG_METHOD_SIG(); |
| |
| _mWdmClient = [[NLWdmClient alloc] init:name |
| weaveWorkQueue:_mWorkQueue |
| appCallbackQueue:appCallbackQueue |
| exchangeMgr:&_mExchangeMgr |
| messageLayer:&_mMessageLayer |
| nlWeaveDeviceManager:_mDeviceMgr]; |
| if (nil == _mWdmClient) { |
| WDM_LOG_ERROR(@"Cannot create new NLWdmClient\n"); |
| } |
| return _mWdmClient; |
| } |
| #endif // WEAVE_CONFIG_DATA_MANAGEMENT_CLIENT_EXPERIMENTAL |
| |
| @end |
| |
| namespace nl { |
| namespace Weave { |
| namespace Platform { |
| namespace PersistedStorage { |
| WEAVE_ERROR Read(const char *aKey, uint32_t &aValue) |
| { |
| return WEAVE_NO_ERROR; |
| } |
| |
| WEAVE_ERROR Write(const char *aKey, uint32_t aValue) |
| { |
| return WEAVE_NO_ERROR; |
| } |
| |
| } // PersistentStorage |
| } // Platform |
| } // Weave |
| } // nl |
| |
| namespace nl { |
| namespace Weave { |
| namespace Profiles { |
| namespace WeaveMakeManagedNamespaceIdentifier(DataManagement, kWeaveManagedNamespaceDesignation_Current) { |
| |
| SubscriptionEngine * SubscriptionEngine::GetInstance() |
| { |
| static nl::Weave::Profiles::DataManagement::SubscriptionEngine sWdmSubscriptionEngine; |
| return &sWdmSubscriptionEngine; |
| } |
| |
| namespace Platform { |
| void CriticalSectionEnter() |
| { |
| return; |
| } |
| |
| void CriticalSectionExit() |
| { |
| return; |
| } |
| |
| } // Platform |
| |
| } // WeaveMakeManagedNamespaceIdentifier(DataManagement, kWeaveManagedNamespaceDesignation_Current) |
| } // Profiles |
| } // Weave |
| } // nl |