blob: 216dea75075d2aa945148bfee12d1b4f488fc42b [file] [log] [blame]
/*
*
* 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