| /* |
| * |
| * Copyright (c) 2016-2017 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 |
| * This file implements Binding for Weave |
| * Data Management (WDM) profile. |
| * |
| * This implementation is a stub, which should be replaced once |
| * we have the real Binding implementation. |
| * |
| */ |
| |
| #ifndef __STDC_FORMAT_MACROS |
| #define __STDC_FORMAT_MACROS |
| #endif // __STDC_FORMAT_MACROS |
| |
| #include <Weave/Profiles/data-management/Current/WdmManagedNamespace.h> |
| #include <Weave/Profiles/data-management/DataManagement.h> |
| |
| #if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING |
| |
| namespace nl { |
| namespace Weave { |
| namespace Profiles { |
| namespace WeaveMakeManagedNamespaceIdentifier(DataManagement, kWeaveManagedNamespaceDesignation_Current) { |
| |
| // Do nothing |
| ViewClient::ViewClient () |
| { |
| } |
| |
| // AddRef to Binding |
| // store pointers to binding and app state |
| // null out EC |
| WEAVE_ERROR ViewClient::Init (Binding * const apBinding, void * const apAppState, EventCallback const aEventCallback) |
| { |
| // initialize all pointers to NULL |
| (void)Cancel (); |
| |
| // add reference to the binding |
| apBinding->AddRef(); |
| |
| // make a copy of the pointers |
| mBinding = apBinding; |
| mAppState = apAppState; |
| mEventCallback = aEventCallback; |
| mPrevIsPartialChange = false; |
| #if WDM_ENABLE_PROTOCOL_CHECKS |
| mPrevTraitDataHandle = -1; |
| #endif |
| |
| mCurrentMode = kMode_Initialized; |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| WEAVE_ERROR ViewClient::SendRequest (TraitCatalogBase<TraitDataSink>* apCatalog, const TraitPath aPathList[], const size_t aPathListSize) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| PacketBuffer *MsgBuf = NULL; |
| SchemaVersionRange requestedSchemaVersionRange; |
| |
| VerifyOrExit(kMode_Initialized == mCurrentMode, err = WEAVE_ERROR_INCORRECT_STATE); |
| |
| mCurrentMode = kMode_DataSink; |
| mDataSinkCatalog = apCatalog; |
| |
| MsgBuf = PacketBuffer::New(); |
| VerifyOrExit(NULL != MsgBuf, err = WEAVE_ERROR_NO_MEMORY); |
| |
| { |
| nl::Weave::TLV::TLVWriter writer; |
| nl::Weave::TLV::TLVType dummyContainerType; |
| writer.Init(MsgBuf); |
| |
| err = writer.StartContainer(nl::Weave::TLV::AnonymousTag, nl::Weave::TLV::kTLVType_Structure, dummyContainerType); |
| SuccessOrExit(err); |
| |
| PathList::Builder pathList; |
| err = pathList.Init(&writer, ViewRequest::kCsTag_PathList); |
| SuccessOrExit(err); |
| |
| for (size_t i = 0; i < aPathListSize; ++i) |
| { |
| TraitDataSink * pDataSink; |
| nl::Weave::TLV::TLVType dummyContainerType2; |
| |
| // Start the TLV Path |
| err = writer.StartContainer(nl::Weave::TLV::AnonymousTag, nl::Weave::TLV::kTLVType_Path, dummyContainerType2); |
| SuccessOrExit(err); |
| |
| // Start, fill, and close the TLV Structure that contains ResourceID, ProfileID, and InstanceID |
| err = mDataSinkCatalog->HandleToAddress(aPathList[i].mTraitDataHandle, writer, requestedSchemaVersionRange); |
| SuccessOrExit(err); |
| |
| err = mDataSinkCatalog->Locate(aPathList[i].mTraitDataHandle, &pDataSink); |
| SuccessOrExit(err); |
| |
| // Append zero or more TLV tags based on the Path Handle |
| err = pDataSink->GetSchemaEngine()->MapHandleToPath(aPathList[i].mPropertyPathHandle, writer); |
| SuccessOrExit(err); |
| |
| // Close the TLV Path |
| err = writer.EndContainer(dummyContainerType2); |
| SuccessOrExit(err); |
| } |
| |
| err = pathList.EndOfPathList().GetError(); |
| SuccessOrExit(err); |
| |
| err = writer.EndContainer(dummyContainerType); |
| SuccessOrExit(err); |
| |
| err = writer.Finalize(); |
| SuccessOrExit(err); |
| } |
| |
| err = mBinding->NewExchangeContext(mEC); |
| if (WEAVE_NO_ERROR != err) |
| { |
| EventParam Param; |
| // Nothing to initialize in Param.mRequestFailureEventParam |
| mEventCallback(mAppState, kEvent_RequestFailed, err, Param); |
| ExitNow(); |
| } |
| |
| mEC->AppState = this; |
| mEC->OnMessageReceived = OnMessageReceived; |
| mEC->OnResponseTimeout = OnResponseTimeout; |
| mEC->OnSendError = OnSendError; |
| |
| err = mEC->SendMessage(nl::Weave::Profiles::kWeaveProfile_WDM, kMsgType_ViewRequest, MsgBuf); |
| MsgBuf = NULL; |
| SuccessOrExit(err); |
| |
| exit: |
| WeaveLogFunctError(err); |
| |
| if (NULL != MsgBuf) |
| { |
| PacketBuffer::Free(MsgBuf); |
| MsgBuf = NULL; |
| } |
| |
| if (WEAVE_NO_ERROR != err) |
| { |
| Cancel (); |
| } |
| |
| return err; |
| } |
| |
| // acquire EC from binding, kick off send message |
| WEAVE_ERROR ViewClient::SendRequest (AppendToPathList const aAppendToPathList, HandleDataElement const aHandleDataElement) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| PacketBuffer *MsgBuf = NULL; |
| |
| VerifyOrExit(kMode_Initialized == mCurrentMode, err = WEAVE_ERROR_INCORRECT_STATE); |
| |
| mCurrentMode = kMode_WithoutDataSink; |
| mHandleDataElement = aHandleDataElement; |
| |
| MsgBuf = PacketBuffer::New(); |
| VerifyOrExit(NULL != MsgBuf, err = WEAVE_ERROR_NO_MEMORY); |
| |
| { |
| nl::Weave::TLV::TLVWriter writer; |
| nl::Weave::TLV::TLVType dummyContainerType; |
| writer.Init(MsgBuf); |
| |
| err = writer.StartContainer(nl::Weave::TLV::AnonymousTag, nl::Weave::TLV::kTLVType_Structure, dummyContainerType); |
| SuccessOrExit(err); |
| |
| PathList::Builder pathList; |
| err = pathList.Init(&writer, ViewRequest::kCsTag_PathList); |
| SuccessOrExit(err); |
| |
| err = aAppendToPathList(mAppState, pathList); |
| SuccessOrExit(err); |
| |
| err = pathList.EndOfPathList().GetError(); |
| SuccessOrExit(err); |
| |
| err = writer.EndContainer(dummyContainerType); |
| SuccessOrExit(err); |
| |
| err = writer.Finalize(); |
| SuccessOrExit(err); |
| } |
| |
| { |
| const uint8_t * const begin = MsgBuf->Start(); |
| const uint8_t * const end = MsgBuf->Start() + MsgBuf->DataLength(); |
| for (const uint8_t *pch = begin; pch < end; ++pch) |
| { |
| //WeaveLogDetail(DataManagement, "0x%02X", *pch); |
| } |
| } |
| |
| err = mBinding->NewExchangeContext(mEC); |
| if (WEAVE_NO_ERROR != err) |
| { |
| EventParam Param; |
| // Nothing to initialize in Param.mRequestFailureEventParam |
| mEventCallback(mAppState, kEvent_RequestFailed, err, Param); |
| ExitNow(); |
| } |
| |
| mEC->AppState = this; |
| mEC->OnMessageReceived = OnMessageReceived; |
| mEC->OnResponseTimeout = OnResponseTimeout; |
| mEC->OnSendError = OnSendError; |
| |
| err = mEC->SendMessage(nl::Weave::Profiles::kWeaveProfile_WDM, kMsgType_ViewRequest, MsgBuf); |
| MsgBuf = NULL; |
| SuccessOrExit(err); |
| |
| exit: |
| WeaveLogFunctError(err); |
| |
| if (NULL != MsgBuf) |
| { |
| PacketBuffer::Free(MsgBuf); |
| MsgBuf = NULL; |
| } |
| |
| if (WEAVE_NO_ERROR != err) |
| { |
| Cancel (); |
| } |
| |
| return err; |
| } |
| |
| // release binding, close EC, null out all pointers |
| WEAVE_ERROR ViewClient::Cancel () |
| { |
| if (kMode_Canceled != mCurrentMode) |
| { |
| |
| mEventCallback = NULL; |
| |
| if (NULL != mBinding) |
| { |
| mBinding->Release(); |
| mBinding = NULL; |
| } |
| |
| if (NULL != mEC) |
| { |
| mEC->Close(); |
| mEC = NULL; |
| } |
| |
| mHandleDataElement = NULL; |
| |
| // We still need these two variables when we're canceling all data sinks |
| mCurrentMode = kMode_Canceled; |
| mAppState = NULL; |
| } |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| void ViewClient::OnSendError (ExchangeContext *aEC, WEAVE_ERROR aErrorCode, void *aMsgSpecificContext) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| |
| ViewClient * const pViewClient = reinterpret_cast<ViewClient *>(aEC->AppState); |
| void * const pAppState = pViewClient->mAppState; |
| EventCallback CallbackFunc = pViewClient->mEventCallback; |
| |
| VerifyOrExit((kMode_DataSink == pViewClient->mCurrentMode) || (kMode_WithoutDataSink == pViewClient->mCurrentMode), |
| err = WEAVE_ERROR_INCORRECT_STATE); |
| |
| pViewClient->Cancel(); |
| |
| EventParam Param; |
| // Nothing to initialize in Param.mRequestFailureEventParam |
| CallbackFunc(pAppState, kEvent_RequestFailed, aErrorCode, Param); |
| |
| exit: |
| WeaveLogFunctError(err); |
| } |
| |
| void ViewClient::OnResponseTimeout (nl::Weave::ExchangeContext *aEC) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| |
| ViewClient * pViewClient = reinterpret_cast<ViewClient *>(aEC->AppState); |
| void * const pAppState = pViewClient->mAppState; |
| EventCallback CallbackFunc = pViewClient->mEventCallback; |
| |
| VerifyOrExit((kMode_DataSink == pViewClient->mCurrentMode) || (kMode_WithoutDataSink == pViewClient->mCurrentMode), |
| err = WEAVE_ERROR_INCORRECT_STATE); |
| |
| pViewClient->Cancel(); |
| |
| EventParam Param; |
| // Nothing to initialize in Param.mRequestFailureEventParam |
| CallbackFunc(pAppState, kEvent_RequestFailed, WEAVE_ERROR_TIMEOUT, Param); |
| |
| exit: |
| WeaveLogFunctError(err); |
| } |
| |
| void ViewClient::OnMessageReceived (nl::Weave::ExchangeContext *aEC, const nl::Inet::IPPacketInfo *aPktInfo, |
| const nl::Weave::WeaveMessageInfo *aMsgInfo, uint32_t aProfileId, |
| uint8_t aMsgType, PacketBuffer *aPayload) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| ViewClient * pViewClient = reinterpret_cast<ViewClient *>(aEC->AppState); |
| void * const pAppState = pViewClient->mAppState; |
| EventCallback CallbackFunc = pViewClient->mEventCallback; |
| EventParam Param; |
| |
| VerifyOrExit((kMode_DataSink == pViewClient->mCurrentMode) || (kMode_WithoutDataSink == pViewClient->mCurrentMode), |
| err = WEAVE_ERROR_INCORRECT_STATE); |
| |
| VerifyOrExit(aEC == pViewClient->mEC, err = WEAVE_ERROR_INCORRECT_STATE); |
| |
| if ((nl::Weave::Profiles::kWeaveProfile_Common == aProfileId) && (nl::Weave::Profiles::Common::kMsgType_StatusReport == aMsgType)) |
| { |
| pViewClient->Cancel(); |
| |
| Param.mStatusReportReceivedEventParam.mMessage = aPayload; |
| CallbackFunc(pAppState, kEvent_StatusReportReceived, WEAVE_ERROR_STATUS_REPORT_RECEIVED, Param); |
| } |
| else if ((nl::Weave::Profiles::kWeaveProfile_WDM == aProfileId) && (kMsgType_ViewResponse == aMsgType)) |
| { |
| Param.mViewResponseReceivedEventParam.mEC = aEC; |
| Param.mViewResponseReceivedEventParam.mMessage = aPayload; |
| CallbackFunc(pAppState, kEvent_ViewResponseReceived, WEAVE_NO_ERROR, Param); |
| |
| nl::Weave::TLV::TLVReader reader; |
| nl::Weave::TLV::TLVType dummyContainerType; |
| reader.Init(aPayload); |
| |
| err = reader.Next(); |
| SuccessOrExit(err); |
| |
| err = reader.EnterContainer(dummyContainerType); |
| SuccessOrExit(err); |
| |
| err = reader.Next(); |
| SuccessOrExit(err); |
| |
| DataList::Parser dataList; |
| dataList.Init(reader); |
| |
| #if WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK |
| // simple schema checking |
| err = dataList.CheckSchemaValidity(); |
| SuccessOrExit(err); |
| #endif // WEAVE_CONFIG_DATA_MANAGEMENT_ENABLE_SCHEMA_CHECK |
| |
| // re-initialize the reader to point to individual data element (reuse to save stack depth) |
| dataList.GetReader(&reader); |
| |
| // TODO: Verify all paths in the original request has been fulfilled! |
| |
| bool isPartialChange = false; |
| uint8_t flags; |
| |
| while (WEAVE_NO_ERROR == (err = reader.Next())) |
| { |
| // schema checking has been done earlier with the whole data list |
| |
| if (kMode_DataSink == pViewClient->mCurrentMode) |
| { |
| nl::Weave::TLV::TLVReader pathReader; |
| |
| { |
| DataElement::Parser element; |
| |
| err = element.Init(reader); |
| SuccessOrExit(err); |
| |
| err = element.GetReaderOnPath(&pathReader); |
| SuccessOrExit(err); |
| |
| isPartialChange = false; |
| err = element.GetPartialChangeFlag(&isPartialChange); |
| VerifyOrExit(err == WEAVE_NO_ERROR || err == WEAVE_END_OF_TLV, ); |
| } |
| |
| { |
| Path::Parser path; |
| |
| err = path.Init(pathReader); |
| SuccessOrExit(err); |
| |
| err = path.GetTags(&pathReader); |
| SuccessOrExit(err); |
| } |
| |
| TraitDataSink * DataSink; |
| TraitDataHandle handle; |
| PropertyPathHandle pathHandle; |
| SchemaVersionRange requestedSchemaVersionRange; |
| |
| err = pViewClient->mDataSinkCatalog->AddressToHandle(pathReader, handle, requestedSchemaVersionRange); |
| SuccessOrExit(err); |
| |
| err = pViewClient->mDataSinkCatalog->Locate(handle, &DataSink); |
| SuccessOrExit(err); |
| |
| err = DataSink->GetSchemaEngine()->MapPathToHandle(pathReader, pathHandle); |
| #if TDM_DISABLE_STRICT_SCHEMA_COMPLIANCE |
| // if we're not in strict compliance mode, we can ignore data elements that refer to paths we can't map due to mismatching schema. |
| // The eventual call to StoreDataElement will correctly deal with the presence of a null property path handle that |
| // has been returned by the above call. It's necessary to call into StoreDataElement with this null handle to ensure |
| // the requisite OnEvent calls are made to the application despite the presence of an unknown tag. It's also necessary to ensure |
| // that we update the internal version tracked by the sink. |
| VerifyOrExit(err == WEAVE_NO_ERROR || err == WEAVE_ERROR_TLV_TAG_NOT_FOUND, /* no-op */); |
| if (err == WEAVE_ERROR_TLV_TAG_NOT_FOUND) { |
| WeaveLogDetail(DataManagement, "Ignoring un-mappable path!"); |
| err = WEAVE_NO_ERROR; |
| } |
| #else |
| SuccessOrExit(err); |
| #endif |
| |
| pathReader = reader; |
| flags = 0; |
| |
| #if WDM_ENABLE_PROTOCOL_CHECKS |
| bool prevHandleMatches = (pViewClient->mPrevTraitDataHandle == handle); |
| |
| // Previous and current trait data handles can only match if we were previously encountered a partial change. |
| // Otherwise, it shouldn't. If there is a violation here, it should be flagged. |
| if (prevHandleMatches != pViewClient->mPrevIsPartialChange) { |
| WeaveLogError(DataManagement, "Encountered partial change flag violation (%u, %08x, %08x)", pViewClient->mPrevIsPartialChange, pViewClient->mPrevTraitDataHandle, handle); |
| err = WEAVE_ERROR_INVALID_DATA_LIST; |
| goto exit; |
| } |
| #endif |
| |
| if (!pViewClient->mPrevIsPartialChange) { |
| flags = TraitDataSink::kFirstElementInChange; |
| } |
| |
| if (!isPartialChange) { |
| flags |= TraitDataSink::kLastElementInChange; |
| } |
| |
| err = DataSink->StoreDataElement(pathHandle, pathReader, flags, NULL, NULL); |
| SuccessOrExit(err); |
| |
| pViewClient->mPrevIsPartialChange = isPartialChange; |
| |
| #if WDM_ENABLE_PROTOCOL_CHECKS |
| // it's important to clear out mPrevTraitDataHandle if this isn't a partial change so that an ensuing notify |
| // that has the first data element pointing to the same trait data instance doesn't trip up the (if prevHandleMatches != mPrevIsPartialChange) logic above. |
| if (!isPartialChange) { |
| pViewClient->mPrevTraitDataHandle = -1; |
| } |
| else { |
| pViewClient->mPrevTraitDataHandle = handle; |
| } |
| #endif |
| |
| } |
| else if (kMode_WithoutDataSink == pViewClient->mCurrentMode) |
| { |
| DataElement::Parser element; |
| err = element.Init(reader); |
| SuccessOrExit(err); |
| |
| err = pViewClient->mHandleDataElement(pViewClient->mAppState, element); |
| SuccessOrExit(err); |
| } |
| } |
| |
| // if we have exhausted this container |
| if (WEAVE_END_OF_TLV == err) |
| { |
| err = WEAVE_NO_ERROR; |
| } |
| |
| err = reader.ExitContainer(dummyContainerType); |
| SuccessOrExit(err); |
| |
| pViewClient->Cancel(); |
| |
| Param.mViewResponseConsumedEventParam.mMessage = aPayload; |
| CallbackFunc(pAppState, kEvent_ViewResponseConsumed, WEAVE_NO_ERROR, Param); |
| } |
| else |
| { |
| pViewClient->Cancel(); |
| |
| // Nothing to initialize in Param.mRequestFailureEventParam |
| CallbackFunc(pAppState, kEvent_RequestFailed, WEAVE_ERROR_INVALID_MESSAGE_TYPE, Param); |
| } |
| |
| exit: |
| WeaveLogFunctError(err); |
| |
| // aEC should be the same as pViewClient->mEC and be closed in InternalCancel |
| // If they are not the same, we're in big trouble |
| pViewClient->Cancel(); |
| aEC = NULL; |
| |
| if (NULL != aPayload) |
| { |
| PacketBuffer::Free(aPayload); |
| aPayload = NULL; |
| } |
| } |
| |
| }; // WeaveMakeManagedNamespaceIdentifier(DataManagement, kWeaveManagedNamespaceDesignation_Current) |
| }; // Profiles |
| }; // Weave |
| }; // nl |
| |
| #endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING |