| /* |
| * |
| * Copyright (c) 2018 Nest Labs, Inc. |
| * 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 |
| * Contains non-inline method definitions for the |
| * GenericNetworkProvisioningServerImpl<> template. |
| */ |
| |
| #ifndef GENERIC_NETWORK_PROVISIONING_SERVER_IMPL_IPP |
| #define GENERIC_NETWORK_PROVISIONING_SERVER_IMPL_IPP |
| |
| #include <Weave/DeviceLayer/internal/WeaveDeviceLayerInternal.h> |
| #include <Weave/DeviceLayer/internal/NetworkProvisioningServer.h> |
| #include <Weave/DeviceLayer/internal/GenericNetworkProvisioningServerImpl.h> |
| #if WEAVE_DEVICE_CONFIG_ENABLE_THREAD |
| #include <Weave/DeviceLayer/ThreadStackManager.h> |
| #endif // WEAVE_DEVICE_CONFIG_ENABLE_THREAD |
| |
| namespace nl { |
| namespace Weave { |
| namespace DeviceLayer { |
| namespace Internal { |
| |
| using namespace ::nl::Weave::TLV; |
| using namespace ::nl::Weave::Profiles::Common; |
| using namespace ::nl::Weave::Profiles::NetworkProvisioning; |
| |
| using Profiles::kWeaveProfile_Common; |
| using Profiles::kWeaveProfile_NetworkProvisioning; |
| |
| namespace { |
| |
| const char sLogPrefix[] = "NetworkProvisioningServer: "; |
| |
| } // unnamed namespace |
| |
| template<class ImplClass> |
| void GenericNetworkProvisioningServerImpl<ImplClass>::_StartPendingScan() |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| |
| // Do nothing if there's no pending ScanNetworks request outstanding, or if a scan is already in progress. |
| if (mState != kState_ScanNetworks_Pending) |
| { |
| ExitNow(); |
| } |
| |
| switch (mScanNetworkType) |
| { |
| #if WEAVE_DEVICE_CONFIG_ENABLE_WIFI_STATION |
| |
| case kNetworkType_WiFi: |
| |
| // Defer the scan if the Connection Manager says the system is in a state |
| // where a WiFi scan cannot be started (e.g. if the system is connecting to |
| // an AP and can't scan and connect at the same time). The Connection Manager |
| // is responsible for calling this method again when the system is read to scan. |
| if (!ConnectivityMgr().CanStartWiFiScan()) |
| { |
| ExitNow(); |
| } |
| |
| mState = kState_ScanNetworks_InProgress; |
| |
| // Delegate to the implementation subclass to initiate the WiFi scan operation. |
| err = Impl()->InitiateWiFiScan(); |
| SuccessOrExit(err); |
| |
| break; |
| |
| #endif // WEAVE_DEVICE_CONFIG_ENABLE_WIFI_STATION |
| |
| #if WEAVE_DEVICE_CONFIG_ENABLE_THREAD |
| |
| case kNetworkType_Thread: |
| |
| // TODO: implement this |
| ExitNow(err = WEAVE_ERROR_UNSUPPORTED_WEAVE_FEATURE); |
| |
| break; |
| |
| #endif // WEAVE_DEVICE_CONFIG_ENABLE_THREAD |
| |
| default: |
| ExitNow(err = WEAVE_ERROR_INCORRECT_STATE); |
| } |
| |
| exit: |
| // If an error occurred, send a Internal Error back to the requestor. |
| if (err != WEAVE_NO_ERROR) |
| { |
| SendStatusReport(kWeaveProfile_Common, kStatus_InternalError, err); |
| mState = kState_Idle; |
| } |
| } |
| |
| template<class ImplClass> |
| void GenericNetworkProvisioningServerImpl<ImplClass>::_OnPlatformEvent(const WeaveDeviceEvent * event) |
| { |
| #if WEAVE_DEVICE_CONFIG_ENABLE_WIFI_STATION |
| |
| // Handle a change in Internet connectivity... |
| if (event->Type == DeviceEventType::kInternetConnectivityChange) |
| { |
| // If a TestConnectivity operation is in progress for WiFi, re-evaluate the state |
| // connectivity now. |
| ContinueWiFiConnectivityTest(); |
| } |
| |
| #endif // WEAVE_DEVICE_CONFIG_ENABLE_WIFI_STATION |
| |
| #if WEAVE_DEVICE_CONFIG_ENABLE_THREAD |
| |
| // Define some short-hands for various interesting event conditions. |
| const bool threadConnChanged = (event->Type == DeviceEventType::kThreadConnectivityChange && |
| event->ThreadConnectivityChange.Result != kConnectivity_NoChange); |
| const bool threadRoleChanged = (event->Type == DeviceEventType::kThreadStateChange && |
| event->ThreadStateChange.RoleChanged); |
| const bool threadChildrenChanged = (event->Type == DeviceEventType::kThreadStateChange && |
| event->ThreadStateChange.ChildNodesChanged); |
| |
| // If the state of the Thread interface changed, OR if the Thread role changed, OR |
| // if there was a change to the set of child nodes... |
| if (threadConnChanged || threadRoleChanged || threadChildrenChanged) |
| { |
| // If a TestConnectivity operation is in progress for Thread, re-evaluate the state |
| // connectivity now. |
| ContinueThreadConnectivityTest(); |
| } |
| |
| #endif // WEAVE_DEVICE_CONFIG_ENABLE_THREAD |
| } |
| |
| template<class ImplClass> |
| WEAVE_ERROR GenericNetworkProvisioningServerImpl<ImplClass>::HandleScanNetworks(uint8_t networkType) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| bool isAppControlled = false; |
| |
| VerifyOrExit(mState == kState_Idle, err = WEAVE_ERROR_INCORRECT_STATE); |
| |
| switch (networkType) |
| { |
| #if WEAVE_DEVICE_CONFIG_ENABLE_WIFI_STATION |
| |
| case kNetworkType_WiFi: |
| isAppControlled = ConnectivityMgr().IsWiFiStationApplicationControlled(); |
| break; |
| |
| #endif // WEAVE_DEVICE_CONFIG_ENABLE_WIFI_STATION |
| |
| #if WEAVE_DEVICE_CONFIG_ENABLE_THREAD |
| |
| case kNetworkType_Thread: |
| isAppControlled = ConnectivityMgr().IsThreadApplicationControlled(); |
| break; |
| |
| #endif // WEAVE_DEVICE_CONFIG_ENABLE_THREAD |
| |
| default: |
| err = SendStatusReport(kWeaveProfile_NetworkProvisioning, kStatusCode_UnsupportedNetworkType); |
| ExitNow(); |
| } |
| |
| // Reject the request if the application is currently in control of the requested network. |
| if (isAppControlled) |
| { |
| err = SendStatusReport(kWeaveProfile_Common, kStatus_NotAvailable); |
| ExitNow(); |
| } |
| |
| // Enter the ScanNetworks Pending state and delegate to the implementation class to start the scan. |
| mState = kState_ScanNetworks_Pending; |
| mScanNetworkType = networkType; |
| Impl()->StartPendingScan(); |
| |
| exit: |
| return err; |
| } |
| |
| template<class ImplClass> |
| WEAVE_ERROR GenericNetworkProvisioningServerImpl<ImplClass>::DoInit() |
| { |
| WEAVE_ERROR err; |
| |
| // Call init on the server base class. |
| err = ServerBaseClass::Init(&::nl::Weave::DeviceLayer::ExchangeMgr); |
| SuccessOrExit(err); |
| |
| // Set the pointer to the delegate object. |
| SetDelegate(this); |
| |
| mState = kState_Idle; |
| mScanNetworkType = kNetworkType_NotSpecified; |
| |
| exit: |
| return err; |
| } |
| |
| template<class ImplClass> |
| WEAVE_ERROR GenericNetworkProvisioningServerImpl<ImplClass>::HandleAddNetwork(PacketBuffer * networkInfoTLV) |
| { |
| return HandleAddUpdateNetwork(networkInfoTLV, false); |
| } |
| |
| template<class ImplClass> |
| WEAVE_ERROR GenericNetworkProvisioningServerImpl<ImplClass>::HandleUpdateNetwork(PacketBuffer * networkInfoTLV) |
| { |
| return HandleAddUpdateNetwork(networkInfoTLV, true); |
| } |
| |
| template<class ImplClass> |
| WEAVE_ERROR GenericNetworkProvisioningServerImpl<ImplClass>::HandleAddUpdateNetwork(PacketBuffer * networkInfoTLV, bool isUpdate) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| DeviceNetworkInfo netInfo; |
| uint32_t netId; |
| |
| VerifyOrExit(mState == kState_Idle, err = WEAVE_ERROR_INCORRECT_STATE); |
| |
| // Parse the supplied network configuration info. |
| { |
| TLV::TLVReader reader; |
| reader.Init(networkInfoTLV); |
| err = netInfo.Decode(reader); |
| SuccessOrExit(err); |
| } |
| |
| // Discard the request buffer. |
| PacketBuffer::Free(networkInfoTLV); |
| networkInfoTLV = NULL; |
| |
| switch (netInfo.NetworkType) |
| { |
| #if WEAVE_DEVICE_CONFIG_ENABLE_WIFI_STATION |
| |
| case kNetworkType_WiFi: |
| |
| // If updating the provision, verify that the specified network is provisioned. |
| if (isUpdate && !ConnectivityMgr().IsWiFiStationProvisioned()) |
| { |
| err = SendStatusReport(kWeaveProfile_NetworkProvisioning, kStatusCode_UnknownNetwork); |
| ExitNow(); |
| } |
| |
| // Reject the request if the application is currently in control of the WiFi station. |
| if (ConnectivityMgr().IsWiFiStationApplicationControlled()) |
| { |
| err = SendStatusReport(kWeaveProfile_Common, kStatus_NotAvailable); |
| ExitNow(); |
| } |
| |
| // If updating the provision... |
| if (isUpdate) |
| { |
| DeviceNetworkInfo existingNetInfo; |
| |
| // Delegate to the implementation subclass to get the existing station provision. |
| err = Impl()->GetWiFiStationProvision(existingNetInfo, true); |
| SuccessOrExit(err); |
| |
| // Override the existing provision with values specified in the update. |
| err = netInfo.MergeTo(existingNetInfo); |
| SuccessOrExit(err); |
| memcpy(&netInfo, &existingNetInfo, sizeof(existingNetInfo)); |
| } |
| |
| // Check the validity of the new provision. If not acceptable, respond to the requestor |
| // with an appropriate StatusReport. |
| { |
| uint32_t statusProfileId; |
| uint16_t statusCode; |
| err = Impl()->ValidateWiFiStationProvision(netInfo, statusProfileId, statusCode); |
| if (err != WEAVE_NO_ERROR) |
| { |
| err = SendStatusReport(statusProfileId, statusCode, err); |
| ExitNow(); |
| } |
| } |
| |
| // If the WiFi station is not already configured, disable the station interface. This |
| // ensures that the device will not automatically connect to the new network until an |
| // EnableNetwork request is received. |
| if (!ConnectivityMgr().IsWiFiStationProvisioned()) |
| { |
| err = ConnectivityMgr().SetWiFiStationMode(ConnectivityManager::kWiFiStationMode_Disabled); |
| SuccessOrExit(err); |
| } |
| |
| // Delegate to the implementation subclass to set the WiFi station provision. |
| err = Impl()->SetWiFiStationProvision(netInfo); |
| SuccessOrExit(err); |
| |
| // Tell the ConnectivityManager there's been a change to the station provision. |
| ConnectivityMgr().OnWiFiStationProvisionChange(); |
| |
| netId = kWiFiStationNetworkId; |
| |
| break; |
| |
| #endif // WEAVE_DEVICE_CONFIG_ENABLE_WIFI_STATION |
| |
| #if WEAVE_DEVICE_CONFIG_ENABLE_THREAD |
| |
| case kNetworkType_Thread: |
| |
| // If updating the provision, verify that the Thread network is provisioned. |
| if (isUpdate && !ThreadStackMgr().IsThreadProvisioned()) |
| { |
| err = SendStatusReport(kWeaveProfile_NetworkProvisioning, kStatusCode_UnknownNetwork); |
| ExitNow(); |
| } |
| |
| // Reject the request if the application is currently in control of the Thread network. |
| if (ConnectivityMgr().IsThreadApplicationControlled()) |
| { |
| err = SendStatusReport(kWeaveProfile_Common, kStatus_NotAvailable); |
| ExitNow(); |
| } |
| |
| // Check the validity of the supplied Thread parameters. If not acceptable, respond to |
| // the requestor with an appropriate StatusReport. |
| { |
| uint32_t statusProfileId; |
| uint16_t statusCode; |
| err = Impl()->ValidateThreadProvision(isUpdate, netInfo, statusProfileId, statusCode); |
| if (err != WEAVE_NO_ERROR) |
| { |
| err = SendStatusReport(statusProfileId, statusCode, err); |
| ExitNow(); |
| } |
| } |
| |
| // Apply suitable defaults for any parameters not supplied by the client. |
| err = Impl()->SetThreadProvisionDefaults(isUpdate, netInfo); |
| SuccessOrExit(err); |
| |
| // Store the Thread provision. |
| err = ThreadStackMgr().SetThreadProvision(netInfo); |
| SuccessOrExit(err); |
| |
| netId = kThreadNetworkId; |
| |
| break; |
| |
| #endif // WEAVE_DEVICE_CONFIG_ENABLE_THREAD |
| |
| default: |
| |
| WeaveLogProgress(DeviceLayer, "%sUnsupported network type: %d", sLogPrefix, netInfo.NetworkType); |
| err = SendStatusReport(kWeaveProfile_NetworkProvisioning, kStatusCode_UnsupportedNetworkType, WEAVE_ERROR_INVALID_ARGUMENT); |
| ExitNow(); |
| } |
| |
| // Send an AddNetworkComplete message back to the requestor. |
| SendAddNetworkComplete(netId); |
| |
| exit: |
| PacketBuffer::Free(networkInfoTLV); |
| return err; |
| } |
| |
| template<class ImplClass> |
| WEAVE_ERROR GenericNetworkProvisioningServerImpl<ImplClass>::HandleRemoveNetwork(uint32_t networkId) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| |
| VerifyOrExit(mState == kState_Idle, err = WEAVE_ERROR_INCORRECT_STATE); |
| |
| switch (networkId) |
| { |
| #if WEAVE_DEVICE_CONFIG_ENABLE_WIFI_STATION |
| |
| case kWiFiStationNetworkId: |
| |
| // Verify that the specified network exists. |
| if (!ConnectivityMgr().IsWiFiStationProvisioned()) |
| { |
| goto sendUnknownNetworkResp; |
| } |
| |
| // Reject the request if the application is currently in control of the WiFi station. |
| if (ConnectivityMgr().IsWiFiStationApplicationControlled()) |
| { |
| err = SendStatusReport(kWeaveProfile_Common, kStatus_NotAvailable); |
| ExitNow(); |
| } |
| |
| // Delegate to the implementation subclass to clear the WiFi station provision. |
| err = Impl()->ClearWiFiStationProvision(); |
| SuccessOrExit(err); |
| |
| // Tell the ConnectivityManager there's been a change to the station provision. |
| ConnectivityMgr().OnWiFiStationProvisionChange(); |
| |
| break; |
| |
| #endif // WEAVE_DEVICE_CONFIG_ENABLE_WIFI_STATION |
| |
| #if WEAVE_DEVICE_CONFIG_ENABLE_THREAD |
| |
| case kThreadNetworkId: |
| |
| // Verify that the specified network exists. |
| if (!ThreadStackMgr().IsThreadProvisioned()) |
| { |
| goto sendUnknownNetworkResp; |
| } |
| |
| // Reject the request if the application is currently in control of the Thread network. |
| if (ConnectivityMgr().IsThreadApplicationControlled()) |
| { |
| err = SendStatusReport(kWeaveProfile_Common, kStatus_NotAvailable); |
| ExitNow(); |
| } |
| |
| // Clear the Thread provision. |
| ThreadStackMgr().ClearThreadProvision(); |
| |
| break; |
| |
| #endif // WEAVE_DEVICE_CONFIG_ENABLE_THREAD |
| |
| default: |
| sendUnknownNetworkResp: |
| |
| err = SendStatusReport(kWeaveProfile_NetworkProvisioning, kStatusCode_UnknownNetwork); |
| ExitNow(); |
| } |
| |
| // Respond with a Success response. |
| err = SendSuccessResponse(); |
| SuccessOrExit(err); |
| |
| exit: |
| return err; |
| } |
| |
| template<class ImplClass> |
| WEAVE_ERROR GenericNetworkProvisioningServerImpl<ImplClass>::HandleGetNetworks(uint8_t flags) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| DeviceNetworkInfo netInfo[2]; |
| PacketBuffer * respBuf = NULL; |
| uint8_t resultCount = 0; |
| const bool includeCredentials = (flags & kGetNetwork_IncludeCredentials) != 0; |
| |
| VerifyOrExit(mState == kState_Idle, err = WEAVE_ERROR_INCORRECT_STATE); |
| |
| #if WEAVE_DEVICE_CONFIG_ENABLE_WIFI_STATION |
| |
| // Delegate to the implementation subclass to get the WiFi station provision. |
| err = Impl()->GetWiFiStationProvision(netInfo[resultCount], includeCredentials); |
| if (err == WEAVE_NO_ERROR) |
| { |
| resultCount++; |
| } |
| else if (err != WEAVE_ERROR_INCORRECT_STATE) |
| { |
| ExitNow(); |
| } |
| |
| #endif // WEAVE_DEVICE_CONFIG_ENABLE_WIFI_STATION |
| |
| #if WEAVE_DEVICE_CONFIG_ENABLE_THREAD |
| |
| // Delegate to the implementation subclass to get the Thread provision. |
| err = ThreadStackMgr().GetThreadProvision(netInfo[resultCount], includeCredentials); |
| if (err == WEAVE_NO_ERROR) |
| { |
| resultCount++; |
| } |
| else if (err != WEAVE_ERROR_INCORRECT_STATE) |
| { |
| ExitNow(); |
| } |
| |
| #endif // WEAVE_DEVICE_CONFIG_ENABLE_THREAD |
| |
| // Allocate a buffer to hold the response. |
| respBuf = PacketBuffer::New(); |
| VerifyOrExit(respBuf != NULL, err = WEAVE_ERROR_NO_MEMORY); |
| |
| // Encode the GetNetworks response data. |
| { |
| TLVWriter writer; |
| |
| writer.Init(respBuf); |
| |
| err = DeviceNetworkInfo::EncodeArray(writer, netInfo, resultCount); |
| SuccessOrExit(err); |
| |
| err = writer.Finalize(); |
| SuccessOrExit(err); |
| } |
| |
| // Send the response. |
| err = SendGetNetworksComplete(resultCount, respBuf); |
| respBuf = NULL; |
| SuccessOrExit(err); |
| |
| exit: |
| PacketBuffer::Free(respBuf); |
| return err; |
| } |
| |
| template<class ImplClass> |
| WEAVE_ERROR GenericNetworkProvisioningServerImpl<ImplClass>::HandleEnableNetwork(uint32_t networkId) |
| { |
| return HandleEnableDisableNetwork(networkId, true); |
| } |
| |
| template<class ImplClass> |
| WEAVE_ERROR GenericNetworkProvisioningServerImpl<ImplClass>::HandleDisableNetwork(uint32_t networkId) |
| { |
| return HandleEnableDisableNetwork(networkId, false); |
| } |
| |
| template<class ImplClass> |
| WEAVE_ERROR GenericNetworkProvisioningServerImpl<ImplClass>::HandleEnableDisableNetwork(uint32_t networkId, bool enable) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| |
| VerifyOrExit(mState == kState_Idle, err = WEAVE_ERROR_INCORRECT_STATE); |
| |
| switch (networkId) |
| { |
| #if WEAVE_DEVICE_CONFIG_ENABLE_WIFI_STATION |
| |
| case kWiFiStationNetworkId: |
| |
| // Verify that the specified network exists. |
| if (!ConnectivityMgr().IsWiFiStationProvisioned()) |
| { |
| goto sendUnknownNetworkResp; |
| } |
| |
| // Reject the request if the application is currently in control of the WiFi station. |
| if (ConnectivityMgr().IsWiFiStationApplicationControlled()) |
| { |
| err = SendStatusReport(kWeaveProfile_Common, kStatus_NotAvailable); |
| ExitNow(); |
| } |
| |
| // Tell the ConnectivityManager to enable/disable the WiFi station interface. |
| // Note that any effects of chaning the WiFi station mode happen asynchronously with this call. |
| err = ConnectivityMgr().SetWiFiStationMode((enable) |
| ? ConnectivityManager::kWiFiStationMode_Enabled |
| : ConnectivityManager::kWiFiStationMode_Disabled); |
| SuccessOrExit(err); |
| |
| break; |
| |
| #endif // WEAVE_DEVICE_CONFIG_ENABLE_WIFI_STATION |
| |
| #if WEAVE_DEVICE_CONFIG_ENABLE_THREAD |
| |
| case kThreadNetworkId: |
| |
| // Verify that the specified network exists. |
| if (!ThreadStackMgr().IsThreadProvisioned()) |
| { |
| goto sendUnknownNetworkResp; |
| } |
| |
| // Reject the request if the application is currently in control of the Thread network. |
| if (ConnectivityMgr().IsThreadApplicationControlled()) |
| { |
| err = SendStatusReport(kWeaveProfile_Common, kStatus_NotAvailable); |
| ExitNow(); |
| } |
| |
| // Tell the ConnectivityManager to enable/disable the Thread network interface. |
| // Note that any effects of changing the Thread mode happen asynchronously with this call. |
| err = ThreadStackMgr().SetThreadEnabled(enable); |
| SuccessOrExit(err); |
| |
| break; |
| |
| #endif // WEAVE_DEVICE_CONFIG_ENABLE_THREAD |
| |
| default: |
| sendUnknownNetworkResp: |
| |
| err = SendStatusReport(kWeaveProfile_NetworkProvisioning, kStatusCode_UnknownNetwork); |
| ExitNow(); |
| } |
| |
| // Respond with a Success response. |
| err = SendSuccessResponse(); |
| SuccessOrExit(err); |
| |
| exit: |
| return err; |
| } |
| |
| template<class ImplClass> |
| WEAVE_ERROR GenericNetworkProvisioningServerImpl<ImplClass>::HandleTestConnectivity(uint32_t networkId) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| |
| VerifyOrExit(mState == kState_Idle, err = WEAVE_ERROR_INCORRECT_STATE); |
| |
| switch (networkId) |
| { |
| #if WEAVE_DEVICE_CONFIG_ENABLE_WIFI_STATION |
| |
| case kWiFiStationNetworkId: |
| |
| // Verify that the specified network exists. |
| if (!ConnectivityMgr().IsWiFiStationProvisioned()) |
| { |
| goto sendUnknownNetworkResp; |
| } |
| |
| // Reject the request if the application is currently in control of the WiFi station. |
| if (ConnectivityMgr().IsWiFiStationApplicationControlled()) |
| { |
| err = SendStatusReport(kWeaveProfile_Common, kStatus_NotAvailable); |
| ExitNow(); |
| } |
| |
| // Tell the ConnectivityManager to enable the WiFi station interface if it hasn't been done already. |
| // Note that any effects of enabling the WiFi station interface (e.g. connecting to an AP) happen |
| // asynchronously with this call. |
| err = ConnectivityMgr().SetWiFiStationMode(ConnectivityManager::kWiFiStationMode_Enabled); |
| SuccessOrExit(err); |
| |
| // Record that we're waiting for the WiFi station interface to establish connectivity |
| // with the Internet and arm a timer that will generate an error if connectivity isn't established |
| // within a certain amount of time. |
| mState = kState_TestConnectivity_WaitWiFiConnectivity; |
| SystemLayer.StartTimer(WEAVE_DEVICE_CONFIG_WIFI_CONNECTIVITY_TIMEOUT, HandleConnectivityTestTimeOut, NULL); |
| |
| // Go check for connectivity now. |
| Impl()->ContinueWiFiConnectivityTest(); |
| |
| break; |
| |
| #endif // WEAVE_DEVICE_CONFIG_ENABLE_WIFI_STATION |
| |
| #if WEAVE_DEVICE_CONFIG_ENABLE_THREAD |
| |
| case kThreadNetworkId: |
| |
| // Verify that the specified network exists. |
| if (!ThreadStackMgr().IsThreadProvisioned()) |
| { |
| goto sendUnknownNetworkResp; |
| } |
| |
| // If the Thread interface is NOT already enabled... |
| if (!ThreadStackMgr().IsThreadEnabled()) |
| { |
| // Reject the request if the application is currently in control of the Thread network. |
| if (ConnectivityMgr().IsThreadApplicationControlled()) |
| { |
| err = SendStatusReport(kWeaveProfile_Common, kStatus_NotAvailable); |
| ExitNow(); |
| } |
| |
| // Enable the Thread interface. |
| err = ThreadStackMgr().SetThreadEnabled(true); |
| SuccessOrExit(err); |
| } |
| |
| // Record that we're waiting to establish Thread connectivity and arm a timer that will |
| // generate an error if connectivity isn't established within a certain amount of time. |
| mState = kState_TestConnectivity_WaitThreadConnectivity; |
| SystemLayer.StartTimer(WEAVE_DEVICE_CONFIG_THREAD_CONNECTIVITY_TIMEOUT, HandleConnectivityTestTimeOut, NULL); |
| |
| // Go check for connectivity now. |
| Impl()->ContinueThreadConnectivityTest(); |
| |
| break; |
| |
| #endif // WEAVE_DEVICE_CONFIG_ENABLE_THREAD |
| |
| default: |
| sendUnknownNetworkResp: |
| |
| err = SendStatusReport(kWeaveProfile_NetworkProvisioning, kStatusCode_UnknownNetwork); |
| ExitNow(); |
| } |
| |
| exit: |
| return err; |
| } |
| |
| template<class ImplClass> |
| WEAVE_ERROR GenericNetworkProvisioningServerImpl<ImplClass>::HandleSetRendezvousMode(uint16_t rendezvousMode) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| VerifyOrExit(mState == kState_Idle, err = WEAVE_ERROR_INCORRECT_STATE); |
| |
| // Fail with Common:UnsupportedMessage if any unsupported modes were specified. |
| { |
| const uint16_t kSupportedModes = 0 |
| #if WEAVE_DEVICE_CONFIG_ENABLE_WIFI_AP |
| | kRendezvousMode_EnableWiFiRendezvousNetwork |
| #endif |
| #if WEAVE_DEVICE_CONFIG_ENABLE_THREAD |
| | kRendezvousMode_EnableThreadRendezvous |
| #endif |
| ; |
| |
| if ((rendezvousMode & ~kSupportedModes) != 0) |
| { |
| err = SendStatusReport(kWeaveProfile_Common, kStatus_UnsupportedMessage); |
| ExitNow(); |
| } |
| } |
| |
| #if WEAVE_DEVICE_CONFIG_ENABLE_WIFI_AP |
| |
| // If the request is to start the WiFi "rendezvous network" (a.k.a. the WiFi AP interface)... |
| if ((rendezvousMode & kRendezvousMode_EnableWiFiRendezvousNetwork) != 0) |
| { |
| // Reject the request if the application is currently in control of the WiFi AP |
| // OR if the AP interface has been expressly disabled by the application. |
| const ConnectivityManager::WiFiAPMode apMode = ConnectivityMgr().GetWiFiAPMode(); |
| if (apMode == ConnectivityManager::kWiFiAPMode_ApplicationControlled || |
| apMode == ConnectivityManager::kWiFiAPMode_Disabled) |
| { |
| err = SendStatusReport(kWeaveProfile_Common, kStatus_NotAvailable); |
| ExitNow(); |
| } |
| |
| // Otherwise, request the ConnectivityManager to demand start the WiFi AP interface. |
| // If the interface is already active this will have no immediate effect, except if the |
| // interface is in the "demand" mode, in which case this will serve to extend the |
| // active time. |
| ConnectivityMgr().DemandStartWiFiAP(); |
| } |
| |
| // Otherwise the request is to stop the WiFi rendezvous network, so request the ConnectivityManager |
| // to stop the AP interface if it has been demand started. This will have no effect if the |
| // interface is already stopped, or if the application has expressly enabled the interface. |
| else |
| { |
| ConnectivityMgr().StopOnDemandWiFiAP(); |
| } |
| |
| #endif // WEAVE_DEVICE_CONFIG_ENABLE_WIFI_AP |
| |
| #if WEAVE_DEVICE_CONFIG_ENABLE_THREAD |
| |
| // If the request is to enable Thread "rendezvous"--i.e. enable Thread joinable mode... |
| if ((rendezvousMode & kRendezvousMode_EnableThreadRendezvous) != 0) |
| { |
| // Reject the request if the application is currently in control of the Thread Network |
| // OR if the Thread interface has been expressly disabled by the application |
| // OR if the Thread network has not been provisioned. |
| const ConnectivityManager::ThreadMode threadMode = ConnectivityMgr().GetThreadMode(); |
| if (threadMode == ConnectivityManager::kThreadMode_ApplicationControlled || |
| threadMode == ConnectivityManager::kThreadMode_Disabled || |
| !ThreadStackMgr().IsThreadProvisioned()) |
| { |
| err = SendStatusReport(kWeaveProfile_Common, kStatus_NotAvailable); |
| ExitNow(); |
| } |
| |
| #if defined(__Fuchsia__) |
| err = ThreadStackMgrImpl().SetThreadJoinable(true); |
| if (err != WEAVE_NO_ERROR) |
| { |
| SendStatusReport(kWeaveProfile_Common, kStatus_InternalError); |
| ExitNow(); |
| } |
| #else |
| SendStatusReport(kWeaveProfile_Common, kStatus_NotAvailable); |
| ExitNow(err = WEAVE_ERROR_UNSUPPORTED_WEAVE_FEATURE); |
| #endif |
| } |
| |
| // Otherwise the request is to stop the Thread rendezvous... |
| else |
| { |
| #if defined(__Fuchsia__) |
| err = ThreadStackMgrImpl().SetThreadJoinable(false); |
| if (err != WEAVE_NO_ERROR) |
| { |
| SendStatusReport(kWeaveProfile_Common, kStatus_InternalError); |
| ExitNow(); |
| } |
| #else |
| SendStatusReport(kWeaveProfile_Common, kStatus_NotAvailable); |
| ExitNow(err = WEAVE_ERROR_UNSUPPORTED_WEAVE_FEATURE); |
| #endif |
| } |
| |
| #endif // WEAVE_DEVICE_CONFIG_ENABLE_THREAD |
| // Respond with a Success response. |
| err = SendSuccessResponse(); |
| SuccessOrExit(err); |
| |
| exit: |
| return err; |
| } |
| |
| template<class ImplClass> |
| bool GenericNetworkProvisioningServerImpl<ImplClass>::IsPairedToAccount() const |
| { |
| return ConfigurationMgr().IsServiceProvisioned() && ConfigurationMgr().IsPairedToAccount(); |
| } |
| |
| #if WEAVE_DEVICE_CONFIG_ENABLE_WIFI_STATION |
| |
| template<class ImplClass> |
| WEAVE_ERROR GenericNetworkProvisioningServerImpl<ImplClass>::ValidateWiFiStationProvision( |
| const DeviceNetworkInfo & netInfo, uint32_t & statusProfileId, uint16_t & statusCode) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| |
| if (netInfo.NetworkType != kNetworkType_WiFi) |
| { |
| WeaveLogProgress(DeviceLayer, "%sUnsupported WiFi station network type: %d", sLogPrefix, netInfo.NetworkType); |
| statusProfileId = kWeaveProfile_NetworkProvisioning; |
| statusCode = kStatusCode_UnsupportedNetworkType; |
| ExitNow(err = WEAVE_ERROR_INVALID_ARGUMENT); |
| } |
| |
| if (netInfo.WiFiSSID[0] == 0) |
| { |
| WeaveLogProgress(DeviceLayer, "%sMissing WiFi station SSID", sLogPrefix); |
| statusProfileId = kWeaveProfile_NetworkProvisioning; |
| statusCode = kStatusCode_InvalidNetworkConfiguration; |
| ExitNow(err = WEAVE_ERROR_INVALID_ARGUMENT); |
| } |
| |
| if (netInfo.WiFiMode != kWiFiMode_Managed) |
| { |
| if (netInfo.WiFiMode == kWiFiMode_NotSpecified) |
| { |
| WeaveLogProgress(DeviceLayer, "%sMissing WiFi station mode", sLogPrefix); |
| } |
| else |
| { |
| WeaveLogProgress(DeviceLayer, "%sUnsupported WiFi station mode: %d", sLogPrefix, netInfo.WiFiMode); |
| } |
| statusProfileId = kWeaveProfile_NetworkProvisioning; |
| statusCode = kStatusCode_InvalidNetworkConfiguration; |
| ExitNow(err = WEAVE_ERROR_INVALID_ARGUMENT); |
| } |
| |
| if (netInfo.WiFiRole != kWiFiRole_Station) |
| { |
| if (netInfo.WiFiRole == kWiFiRole_NotSpecified) |
| { |
| WeaveLogProgress(DeviceLayer, "%sMissing WiFi station role", sLogPrefix); |
| } |
| else |
| { |
| WeaveLogProgress(DeviceLayer, "%sUnsupported WiFi station role: %d", sLogPrefix, netInfo.WiFiRole); |
| } |
| statusProfileId = kWeaveProfile_NetworkProvisioning; |
| statusCode = kStatusCode_InvalidNetworkConfiguration; |
| ExitNow(err = WEAVE_ERROR_INVALID_ARGUMENT); |
| } |
| |
| // Defer to the implementation class to determine if the proposed security type is supported. |
| if (!ImplClass::IsSupportedWiFiSecurityType(netInfo.WiFiSecurityType)) |
| { |
| WeaveLogProgress(DeviceLayer, "%sUnsupported WiFi station security type: %d", sLogPrefix, netInfo.WiFiSecurityType); |
| statusProfileId = kWeaveProfile_NetworkProvisioning; |
| statusCode = kStatusCode_UnsupportedWiFiSecurityType; |
| ExitNow(err = WEAVE_ERROR_INVALID_ARGUMENT); |
| } |
| |
| if (netInfo.WiFiSecurityType != kWiFiSecurityType_None && netInfo.WiFiKeyLen == 0) |
| { |
| WeaveLogProgress(DeviceLayer, "%sMissing WiFi Key", sLogPrefix); |
| statusProfileId = kWeaveProfile_NetworkProvisioning; |
| statusCode = kStatusCode_InvalidNetworkConfiguration; |
| ExitNow(err = WEAVE_ERROR_INVALID_ARGUMENT); |
| } |
| |
| exit: |
| return err; |
| } |
| |
| #endif // WEAVE_DEVICE_CONFIG_ENABLE_WIFI_STATION |
| |
| |
| #if WEAVE_DEVICE_CONFIG_ENABLE_THREAD |
| |
| template<class ImplClass> |
| WEAVE_ERROR GenericNetworkProvisioningServerImpl<ImplClass>::ValidateThreadProvision( |
| bool isUpdate, const DeviceNetworkInfo & netInfo, uint32_t & statusProfileId, uint16_t & statusCode) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| |
| // Verify a valid Thread channel was specified. |
| if (netInfo.ThreadChannel != kThreadChannel_NotSpecified) |
| { |
| if (netInfo.ThreadChannel < 11 || netInfo.ThreadChannel > 26) |
| { |
| statusProfileId = kWeaveProfile_NetworkProvisioning; |
| statusCode = kStatusCode_InvalidNetworkConfiguration; |
| ExitNow(err = WEAVE_ERROR_INVALID_ARGUMENT); |
| } |
| } |
| |
| exit: |
| return err; |
| } |
| |
| template<class ImplClass> |
| WEAVE_ERROR GenericNetworkProvisioningServerImpl<ImplClass>::SetThreadProvisionDefaults( |
| bool isUpdate, DeviceNetworkInfo & netInfo) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| |
| // Generate unique values for any Thread parameters not supplied by the client. |
| |
| // If extended PAN id was not specified, generate a random one. |
| if (!netInfo.FieldPresent.ThreadExtendedPANId) |
| { |
| err = Platform::Security::GetSecureRandomData(netInfo.ThreadExtendedPANId, NetworkInfo::kThreadExtendedPANIdLength); |
| SuccessOrExit(err); |
| netInfo.FieldPresent.ThreadExtendedPANId = true; |
| } |
| |
| // If network name was not specified, generate a default one. If the device is a member of a |
| // Weave fabric, base part of the name on the fabric id. |
| if (netInfo.ThreadNetworkName[0] == 0) |
| { |
| uint16_t nameSuffix = (::nl::Weave::DeviceLayer::FabricState.FabricId != kFabricIdNotSpecified) |
| ? (uint16_t)::nl::Weave::DeviceLayer::FabricState.FabricId |
| : Encoding::BigEndian::Get16(&netInfo.ThreadExtendedPANId[6]); |
| snprintf(netInfo.ThreadNetworkName, sizeof(netInfo.ThreadNetworkName), "%s%04X", |
| WEAVE_DEVICE_CONFIG_DEFAULT_THREAD_NETWORK_NAME_PREFIX, nameSuffix); |
| } |
| |
| // If a mesh prefix was not specified, generate one based on the extended PAN id. |
| if (!netInfo.FieldPresent.ThreadMeshPrefix) |
| { |
| memset(netInfo.ThreadMeshPrefix, 0, sizeof(netInfo.ThreadMeshPrefix)); |
| netInfo.ThreadMeshPrefix[0] = 0xFD; // IPv6 ULA prefix |
| memcpy(&netInfo.ThreadMeshPrefix[1], netInfo.ThreadExtendedPANId, 5); |
| netInfo.FieldPresent.ThreadMeshPrefix = true; |
| } |
| |
| // If network key was not specified, generate a random key. |
| if (!netInfo.FieldPresent.ThreadNetworkKey) |
| { |
| err = Platform::Security::GetSecureRandomData(netInfo.ThreadNetworkKey, NetworkInfo::kThreadNetworkKeyLength); |
| SuccessOrExit(err); |
| netInfo.FieldPresent.ThreadNetworkKey = true; |
| } |
| |
| // If a PSKc was not specified, generate a random PSKc. |
| if (!netInfo.FieldPresent.ThreadPSKc) |
| { |
| err = Platform::Security::GetSecureRandomData(netInfo.ThreadPSKc, NetworkInfo::kThreadPSKcLength); |
| SuccessOrExit(err); |
| netInfo.FieldPresent.ThreadPSKc = true; |
| } |
| |
| // If a PAN Id was not specified, generate a random Id. |
| if (netInfo.ThreadPANId == kThreadPANId_NotSpecified) |
| { |
| uint16_t randPANId; |
| err = Platform::Security::GetSecureRandomData((uint8_t *)&randPANId, sizeof(randPANId)); |
| SuccessOrExit(err); |
| netInfo.ThreadPANId = randPANId; |
| } |
| |
| // If Thread channel not present, choose a random one. |
| if (netInfo.ThreadChannel == kThreadChannel_NotSpecified) |
| { |
| err = Platform::Security::GetSecureRandomData((uint8_t *)&netInfo.ThreadChannel, sizeof(netInfo.ThreadChannel)); |
| SuccessOrExit(err); |
| netInfo.ThreadChannel = (netInfo.ThreadChannel % 0xF) + 11; // Convert value to 11 thru 26 |
| } |
| |
| exit: |
| return err; |
| } |
| |
| #endif // WEAVE_DEVICE_CONFIG_ENABLE_THREAD |
| |
| #if WEAVE_DEVICE_CONFIG_ENABLE_WIFI_STATION |
| |
| template<class ImplClass> |
| void GenericNetworkProvisioningServerImpl<ImplClass>::ContinueWiFiConnectivityTest(void) |
| { |
| // If waiting for Internet connectivity to be established ... |
| if (mState == kState_TestConnectivity_WaitWiFiConnectivity) |
| { |
| // Check for IPv4 Internet connectivity. If available... |
| if (ConnectivityMgr().HaveIPv4InternetConnectivity()) |
| { |
| // TODO: perform positive test of connectivity to the Internet by pinging/connecting to |
| // a well-known external server. |
| |
| // Send a Success result to the client. |
| HandleConnectivityTestSuccess(); |
| } |
| |
| // Otherwise, arrange to return an appropriate error when the connectivity test times out. |
| else |
| { |
| // TODO: Elaborate on the nature of the connectivity failure. Ideally the status |
| // code would distinguish the following types of failures: |
| // - Inability to connect to the local WiFi AP |
| // - Lack of a suitable local address (RFC1918 for IPv4; Global address for IPv6) |
| // - Lack of a default router |
| // - Lack of a DNS server |
| // - Inability to contact an external server. |
| mTestConnectivityResult.mStatusProfileId = kWeaveProfile_NetworkProvisioning; |
| mTestConnectivityResult.mStatusCode = kStatusCode_TestNetworkFailed; |
| } |
| } |
| } |
| |
| #endif // WEAVE_DEVICE_CONFIG_ENABLE_WIFI_STATION |
| |
| #if WEAVE_DEVICE_CONFIG_ENABLE_THREAD |
| |
| template<class ImplClass> |
| void GenericNetworkProvisioningServerImpl<ImplClass>::ContinueThreadConnectivityTest(void) |
| { |
| // If waiting for Thread connectivity to be established... |
| if (mState == kState_TestConnectivity_WaitThreadConnectivity) |
| { |
| // Check for connectivity to the Thread mesh. In this context, connectivity means |
| // that this node knows of (i.e. has in its Thread neighbor table) another node |
| // which is acting as a Thread router. If connectivity exists, send a Success |
| // result to the client. |
| if (ThreadStackMgr().HaveMeshConnectivity()) |
| { |
| // TODO: perform positive test of connectivity to peer router node. |
| |
| // Send a Success result to the client. |
| HandleConnectivityTestSuccess(); |
| } |
| |
| // If connectivity doesn't exist arrange to return an appropriate error when the |
| // connectivity test times out. |
| else |
| { |
| // TODO: Elaborate on the nature of the connectivity failure. Ideally the status |
| // code would distinguish the following types of failures: |
| // - Lack of a peer router detected in the Mesh (for end nodes, this means |
| // lack of a parent router). |
| // - Inability to contact/ping the peer router. |
| mTestConnectivityResult.mStatusProfileId = kWeaveProfile_NetworkProvisioning; |
| mTestConnectivityResult.mStatusCode = kStatusCode_NoRouterAvailable; |
| } |
| } |
| } |
| |
| #endif // WEAVE_DEVICE_CONFIG_ENABLE_THREAD |
| |
| #if WEAVE_DEVICE_CONFIG_ENABLE_WIFI_STATION || WEAVE_DEVICE_CONFIG_ENABLE_THREAD |
| |
| template<class ImplClass> |
| void GenericNetworkProvisioningServerImpl<ImplClass>::HandleConnectivityTestSuccess(void) |
| { |
| // Reset the state. |
| mState = kState_Idle; |
| SystemLayer.CancelTimer(HandleConnectivityTestTimeOut, NULL); |
| |
| // Verify that the TestConnectivity request is still outstanding and if so, |
| // send a Success response to the client |
| if (GetCurrentOp() == kMsgType_TestConnectivity) |
| { |
| SendSuccessResponse(); |
| } |
| } |
| |
| template<class ImplClass> |
| void GenericNetworkProvisioningServerImpl<ImplClass>::HandleConnectivityTestTimeOut( |
| ::nl::Weave::System::Layer * aLayer, void * aAppState, ::nl::Weave::System::Error aError) |
| { |
| GenericNetworkProvisioningServerImpl<ImplClass> * self = &NetworkProvisioningSvrImpl(); |
| |
| if (self->mState == kState_TestConnectivity_WaitWiFiConnectivity || |
| self->mState == kState_TestConnectivity_WaitThreadConnectivity) |
| { |
| const bool testingWiFi = (self->mState == kState_TestConnectivity_WaitWiFiConnectivity); |
| |
| WeaveLogProgress(DeviceLayer, "%sTime out waiting for %s connectivity", sLogPrefix, (testingWiFi) ? "Internet" : "Thread"); |
| |
| // Reset the state. |
| self->mState = kState_Idle; |
| SystemLayer.CancelTimer(HandleConnectivityTestTimeOut, NULL); |
| |
| // Verify that the TestConnectivity request is still outstanding; if so, send a StatusReport |
| // to the client contain an appropriate error. |
| if (self->GetCurrentOp() == kMsgType_TestConnectivity) |
| { |
| self->SendStatusReport(self->mTestConnectivityResult.mStatusProfileId, |
| self->mTestConnectivityResult.mStatusCode); |
| } |
| } |
| } |
| |
| #endif // WEAVE_DEVICE_CONFIG_ENABLE_WIFI_STATION || WEAVE_DEVICE_CONFIG_ENABLE_THREAD |
| |
| // Fully instantiate the generic implementation class in whatever compilation unit includes this file. |
| template class GenericNetworkProvisioningServerImpl<NetworkProvisioningServerImpl>; |
| |
| } // namespace Internal |
| } // namespace DeviceLayer |
| } // namespace Weave |
| } // namespace nl |
| |
| |
| #endif // GENERIC_NETWORK_PROVISIONING_SERVER_IMPL_IPP |